First funcional game control change
This commit is contained in:
parent
9a449d1bc1
commit
feb16dd3a3
42 changed files with 464 additions and 96 deletions
|
@ -94,12 +94,13 @@ dependencies {
|
|||
|
||||
// Dagger
|
||||
implementation 'com.google.dagger:hilt-android:2.28.1-alpha'
|
||||
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01'
|
||||
kapt 'com.google.dagger:hilt-android-compiler:2.28.1-alpha'
|
||||
testImplementation 'com.google.dagger:hilt-android-testing:2.28.1-alpha'
|
||||
kaptTest 'com.google.dagger:hilt-android-compiler:2.28.1-alpha'
|
||||
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.28.1-alpha'
|
||||
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.28.1-alpha'
|
||||
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01"
|
||||
kapt "androidx.hilt:hilt-compiler:1.0.0-alpha01"
|
||||
|
||||
// Kotlin
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5'
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package dev.lucasnlm.antimine
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
|
@ -28,6 +29,7 @@ import dev.lucasnlm.antimine.common.level.models.Score
|
|||
import dev.lucasnlm.antimine.common.level.models.Status
|
||||
import dev.lucasnlm.antimine.common.level.repository.ISavesRepository
|
||||
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
|
||||
import dev.lucasnlm.antimine.control.ControlDialogFragment
|
||||
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
|
||||
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
|
@ -46,7 +48,7 @@ import kotlinx.coroutines.launch
|
|||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class GameActivity : AppCompatActivity() {
|
||||
class GameActivity : AppCompatActivity(), DialogInterface.OnDismissListener {
|
||||
@Inject
|
||||
lateinit var preferencesRepository: IPreferencesRepository
|
||||
|
||||
|
@ -162,6 +164,7 @@ class GameActivity : AppCompatActivity() {
|
|||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (status == Status.Running) {
|
||||
viewModel.updateGameControl()
|
||||
viewModel.resumeGame()
|
||||
analyticsManager.sentEvent(Analytics.Resume())
|
||||
}
|
||||
|
@ -264,6 +267,7 @@ class GameActivity : AppCompatActivity() {
|
|||
R.id.intermediate -> changeDifficulty(Difficulty.Intermediate)
|
||||
R.id.expert -> changeDifficulty(Difficulty.Expert)
|
||||
R.id.custom -> showCustomLevelDialog()
|
||||
R.id.control -> showControlDialog()
|
||||
R.id.about -> showAbout()
|
||||
R.id.settings -> showSettings()
|
||||
R.id.rate -> openRateUsLink("Drawer")
|
||||
|
@ -379,6 +383,18 @@ class GameActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun showControlDialog() {
|
||||
if (status == Status.Running) {
|
||||
viewModel.pauseGame()
|
||||
}
|
||||
|
||||
if (supportFragmentManager.findFragmentByTag(CustomLevelDialogFragment.TAG) == null) {
|
||||
ControlDialogFragment().apply {
|
||||
show(supportFragmentManager, ControlDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showAbout() {
|
||||
analyticsManager.sentEvent(Analytics.OpenAbout())
|
||||
Intent(this, AboutActivity::class.java).apply {
|
||||
|
@ -514,8 +530,7 @@ class GameActivity : AppCompatActivity() {
|
|||
*/
|
||||
private fun restartIfNeed() {
|
||||
if (usingLargeArea != preferencesRepository.useLargeAreas()) {
|
||||
finish()
|
||||
Intent(this, GameActivity::class.java).run { startActivity(this) }
|
||||
recreate()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -567,6 +582,13 @@ class GameActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface?) {
|
||||
if (status == Status.Running) {
|
||||
viewModel.updateGameControl()
|
||||
viewModel.resumeGame()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PREFERENCE_FIRST_USE = "preference_first_use"
|
||||
const val PREFERENCE_USE_COUNT = "preference_use_count"
|
||||
|
|
|
@ -30,13 +30,13 @@ class AboutActivity : AppCompatActivity() {
|
|||
Observer { event ->
|
||||
when (event) {
|
||||
AboutEvent.ThirdPartyLicenses -> {
|
||||
replaceFragment(ThirdPartiesFragment(), ThirdPartiesFragment::class.simpleName)
|
||||
replaceFragment(ThirdPartiesFragment(), ThirdPartiesFragment.TAG)
|
||||
}
|
||||
AboutEvent.SourceCode -> {
|
||||
openSourceCode()
|
||||
}
|
||||
AboutEvent.Translators -> {
|
||||
replaceFragment(TranslatorsFragment(), TranslatorsFragment::class.simpleName)
|
||||
replaceFragment(TranslatorsFragment(), TranslatorsFragment.TAG)
|
||||
}
|
||||
else -> {
|
||||
replaceFragment(AboutInfoFragment(), null)
|
||||
|
|
|
@ -48,4 +48,8 @@ class ThirdPartiesFragment : Fragment() {
|
|||
|
||||
licenses.adapter = aboutViewModel?.getLicenses()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "ThirdPartiesFragment"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,4 +40,8 @@ class TranslatorsFragment : Fragment() {
|
|||
adapter = aboutViewModel?.getTranslators()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "TranslatorsFragment"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
package dev.lucasnlm.antimine.control
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.lucasnlm.antimine.R
|
||||
import dev.lucasnlm.antimine.control.view.ControlItemView
|
||||
import dev.lucasnlm.antimine.control.viewmodel.ControlViewModel
|
||||
import dev.lucasnlm.antimine.core.control.ControlStyle
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
import dev.lucasnlm.antimine.core.viewModels
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ControlDialogFragment : AppCompatDialogFragment() {
|
||||
@Inject
|
||||
lateinit var preferencesRepository: IPreferencesRepository
|
||||
|
||||
private val controlViewModel by viewModels<ControlViewModel>()
|
||||
private val adapter by lazy { ControlListAdapter(controlViewModel) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
adapter.setList(controlViewModel.gameControlOptions)
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val currentControl = preferencesRepository.controlType().ordinal
|
||||
|
||||
return AlertDialog.Builder(requireContext(), R.style.MyDialog).apply {
|
||||
setTitle(R.string.control)
|
||||
setSingleChoiceItems(adapter, currentControl, null)
|
||||
setPositiveButton(android.R.string.ok, null) // TODO OK
|
||||
}.create()
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
if (activity is DialogInterface.OnDismissListener) {
|
||||
(activity as DialogInterface.OnDismissListener).onDismiss(dialog)
|
||||
}
|
||||
super.onDismiss(dialog)
|
||||
}
|
||||
|
||||
class ControlListAdapter(
|
||||
private val controlViewModel: ControlViewModel
|
||||
) : BaseAdapter() {
|
||||
private var selected = controlViewModel.controlTypeSelected.value
|
||||
private var controlList = listOf<ControlStyle>()
|
||||
|
||||
fun setList(list: List<ControlStyle>) {
|
||||
controlList = list
|
||||
}
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
|
||||
val view = if (convertView == null) {
|
||||
ControlItemView(parent!!.context)
|
||||
} else {
|
||||
(convertView as ControlItemView)
|
||||
}
|
||||
|
||||
return view.apply {
|
||||
setRadio(selected == controlList[position])
|
||||
setOnClickListener {
|
||||
controlViewModel.selectControlType(controlList[position])
|
||||
selected = controlList[position]
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasStableIds(): Boolean = true
|
||||
|
||||
override fun getItem(position: Int): Any = controlList[position]
|
||||
|
||||
override fun getItemId(position: Int): Long = controlList[position].ordinal.toLong()
|
||||
|
||||
override fun getCount(): Int = controlList.count()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "ControlDialogFragment"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package dev.lucasnlm.antimine.control.model
|
||||
|
||||
data class ControlModel(
|
||||
val id: Long
|
||||
)
|
|
@ -0,0 +1,37 @@
|
|||
package dev.lucasnlm.antimine.control.view
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.appcompat.widget.AppCompatRadioButton
|
||||
import dev.lucasnlm.antimine.R
|
||||
|
||||
class ControlItemView : FrameLayout {
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
private val radio: AppCompatRadioButton
|
||||
private val root: View
|
||||
|
||||
init {
|
||||
LayoutInflater
|
||||
.from(context)
|
||||
.inflate(R.layout.view_control_item, this, true)
|
||||
|
||||
radio = findViewById(R.id.radio)
|
||||
root = findViewById(R.id.root)
|
||||
}
|
||||
|
||||
fun setRadio(selected: Boolean) {
|
||||
radio.isChecked = selected
|
||||
}
|
||||
|
||||
override fun setOnClickListener(listener: OnClickListener?) {
|
||||
super.setOnClickListener(listener)
|
||||
root.setOnClickListener(listener)
|
||||
radio.setOnClickListener(listener)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package dev.lucasnlm.antimine.control.viewmodel
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import dev.lucasnlm.antimine.core.control.ControlStyle
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
|
||||
class ControlViewModel @ViewModelInject constructor(
|
||||
private val preferencesRepository: IPreferencesRepository
|
||||
) : ViewModel() {
|
||||
val controlTypeSelected = MutableLiveData<ControlStyle>(preferencesRepository.controlType())
|
||||
|
||||
val gameControlOptions = listOf(
|
||||
ControlStyle.Standard, ControlStyle.FastFlag, ControlStyle.DoubleClick
|
||||
)
|
||||
|
||||
init {
|
||||
controlTypeSelected.postValue(preferencesRepository.controlType())
|
||||
}
|
||||
|
||||
fun selectControlType(controlStyle: ControlStyle) {
|
||||
preferencesRepository.useControlType(controlStyle)
|
||||
controlTypeSelected.postValue(controlStyle)
|
||||
}
|
||||
}
|
|
@ -88,6 +88,6 @@ class CustomLevelDialogFragment : AppCompatDialogFragment() {
|
|||
const val MIN_HEIGHT = 5
|
||||
const val MIN_MINES = 3
|
||||
|
||||
val TAG = CustomLevelDialogFragment::class.simpleName
|
||||
const val TAG = "CustomLevelDialogFragment"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,6 +153,6 @@ class EndGameDialogFragment : AppCompatDialogFragment() {
|
|||
private const val DIALOG_TOTAL_MINES = "dialog_total_mines"
|
||||
private const val DIALOG_SAVE_ID = "dialog_save_id"
|
||||
|
||||
val TAG = EndGameDialogFragment::class.simpleName
|
||||
const val TAG = "EndGameDialogFragment"
|
||||
}
|
||||
}
|
||||
|
|
74
app/src/main/res/layout/view_control_item.xml
Normal file
74
app/src/main/res/layout/view_control_item.xml
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:paddingHorizontal="16dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/radio"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/standard_details"
|
||||
app:layout_constraintEnd_toStartOf="@id/standard_details"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TableLayout
|
||||
android:id="@+id/standard_details"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toEndOf="@id/standard"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TableRow>
|
||||
<TextView
|
||||
android:padding="4dp"
|
||||
android:text="@string/standard"
|
||||
android:textColor="@color/black"
|
||||
android:textStyle="bold" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TextView
|
||||
android:padding="4dp"
|
||||
android:text="Single Tap"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp"
|
||||
android:text="@string/arrow" />
|
||||
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:padding="4dp"
|
||||
android:text="Open Area" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TextView
|
||||
android:padding="4dp"
|
||||
android:text="Long Tap"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp"
|
||||
android:text="@string/arrow" />
|
||||
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:padding="4dp"
|
||||
android:text="Put Flag" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
|
@ -5,7 +5,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
tools:targetApi="m">
|
||||
|
||||
<TextView
|
||||
|
|
|
@ -37,9 +37,7 @@
|
|||
</menu>
|
||||
</item>
|
||||
|
||||
<group
|
||||
android:id="@+id/history_group">
|
||||
|
||||
<group android:id="@+id/history_group">
|
||||
<item
|
||||
android:id="@+id/previous_games"
|
||||
android:checkable="false"
|
||||
|
@ -49,7 +47,18 @@
|
|||
android:id="@+id/stats"
|
||||
android:checkable="false"
|
||||
android:title="@string/events" />
|
||||
</group>
|
||||
|
||||
<group android:id="@+id/options_group">
|
||||
<item
|
||||
android:id="@+id/control"
|
||||
android:checkable="false"
|
||||
android:title="@string/control" />
|
||||
|
||||
<item
|
||||
android:id="@+id/settings"
|
||||
android:checkable="false"
|
||||
android:title="@string/settings" />
|
||||
</group>
|
||||
|
||||
<group
|
||||
|
@ -62,12 +71,9 @@
|
|||
android:checkable="false"
|
||||
android:icon="@drawable/install"
|
||||
android:title="@string/install" />
|
||||
|
||||
</group>
|
||||
|
||||
<group
|
||||
android:id="@+id/social_group">
|
||||
|
||||
<group android:id="@+id/social_group">
|
||||
<item
|
||||
android:id="@+id/share_now"
|
||||
android:checkable="false"
|
||||
|
@ -79,20 +85,12 @@
|
|||
android:id="@+id/rate"
|
||||
android:checkable="false"
|
||||
android:title="@string/rating_menu" />
|
||||
|
||||
</group>
|
||||
|
||||
<group>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings"
|
||||
android:checkable="false"
|
||||
android:title="@string/settings" />
|
||||
|
||||
<group android:id="@+id/about_group">
|
||||
<item
|
||||
android:id="@+id/about"
|
||||
android:checkable="false"
|
||||
android:title="@string/about" />
|
||||
</group>
|
||||
|
||||
</menu>
|
||||
|
|
|
@ -6,7 +6,7 @@ buildscript {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.2'
|
||||
classpath 'com.android.tools.build:gradle:4.0.0'
|
||||
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28.1-alpha'
|
||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.71'
|
||||
}
|
||||
|
|
|
@ -55,13 +55,13 @@ dependencies {
|
|||
|
||||
// Dagger
|
||||
implementation 'com.google.dagger:hilt-android:2.28.1-alpha'
|
||||
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01'
|
||||
kapt 'com.google.dagger:hilt-android-compiler:2.28.1-alpha'
|
||||
testImplementation 'com.google.dagger:hilt-android-testing:2.28.1-alpha'
|
||||
kaptTest 'com.google.dagger:hilt-android-compiler:2.28.1-alpha'
|
||||
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01"
|
||||
kapt "androidx.hilt:hilt-compiler:1.0.0-alpha01"
|
||||
|
||||
|
||||
// Room
|
||||
api 'androidx.room:room-runtime:2.2.5'
|
||||
api 'androidx.room:room-ktx:2.2.5'
|
||||
|
|
|
@ -9,7 +9,7 @@ import dev.lucasnlm.antimine.common.level.models.Difficulty
|
|||
import dev.lucasnlm.antimine.common.level.models.Mark
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import dev.lucasnlm.antimine.common.level.models.Score
|
||||
import dev.lucasnlm.antimine.core.control.Action
|
||||
import dev.lucasnlm.antimine.core.control.ActionResponse
|
||||
import dev.lucasnlm.antimine.core.control.ActionFeedback
|
||||
import dev.lucasnlm.antimine.core.control.GameControl
|
||||
import kotlin.math.floor
|
||||
|
@ -24,7 +24,7 @@ class GameController {
|
|||
private val startTime = System.currentTimeMillis()
|
||||
private var saveId = 0
|
||||
private var firstOpen: FirstOpen = FirstOpen.Unknown
|
||||
private val gameControl: GameControl = GameControl.Standard
|
||||
private var gameControl: GameControl = GameControl.Standard
|
||||
private var mines: Sequence<Area> = emptySequence()
|
||||
|
||||
var hasMines = false
|
||||
|
@ -264,15 +264,19 @@ class GameController {
|
|||
this.saveId = id.coerceAtLeast(0)
|
||||
}
|
||||
|
||||
fun updateGameControl(newGameControl: GameControl) {
|
||||
this.gameControl = newGameControl
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a game [action] on a given tile.
|
||||
* Run a game [actionResponse] on a given tile.
|
||||
* @return The number of changed tiles.
|
||||
*/
|
||||
private fun Area.runActionOn(action: Action?): ActionFeedback {
|
||||
private fun Area.runActionOn(actionResponse: ActionResponse?): ActionFeedback {
|
||||
val highlightedChanged = turnOffAllHighlighted()
|
||||
|
||||
val changed = when (action) {
|
||||
Action.OpenTile -> {
|
||||
val changed = when (actionResponse) {
|
||||
ActionResponse.OpenTile -> {
|
||||
if (!hasMines) {
|
||||
plantMinesExcept(id, true)
|
||||
}
|
||||
|
@ -284,21 +288,21 @@ class GameController {
|
|||
openTile()
|
||||
}
|
||||
}
|
||||
Action.SwitchMark -> {
|
||||
ActionResponse.SwitchMark -> {
|
||||
if (isCovered) switchMark()
|
||||
1
|
||||
}
|
||||
Action.HighlightNeighbors -> {
|
||||
ActionResponse.HighlightNeighbors -> {
|
||||
if (minesAround != 0) highlight() else 0
|
||||
}
|
||||
Action.OpenNeighbors -> {
|
||||
ActionResponse.OpenNeighbors -> {
|
||||
openNeighbors()
|
||||
8
|
||||
}
|
||||
else -> 0
|
||||
}
|
||||
|
||||
return ActionFeedback(action, id, (changed + highlightedChanged) > 1)
|
||||
return ActionFeedback(actionResponse, id, (changed + highlightedChanged) > 1)
|
||||
}
|
||||
|
||||
fun Area.switchMark(): Area = apply {
|
||||
|
|
|
@ -142,7 +142,7 @@ class AreaAdapter(
|
|||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = AreaAdapter::class.simpleName
|
||||
const val TAG = "AreaAdapter"
|
||||
|
||||
fun createAreaPaintSettings(context: Context, useLargeArea: Boolean): AreaPaintSettings {
|
||||
val resources = context.resources
|
||||
|
|
|
@ -82,7 +82,7 @@ class AreaView : View {
|
|||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent?): Boolean =
|
||||
(gestureDetector?.onTouchEvent(event) ?: false) || super.onTouchEvent(event)
|
||||
gestureDetector.onTouchEvent(event) || super.onTouchEvent(event)
|
||||
|
||||
private fun bindContentDescription(area: Area) {
|
||||
contentDescription = when {
|
||||
|
|
|
@ -19,8 +19,9 @@ import dev.lucasnlm.antimine.common.level.utils.Clock
|
|||
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackInteractor
|
||||
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
|
||||
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
||||
import dev.lucasnlm.antimine.core.control.Action
|
||||
import dev.lucasnlm.antimine.core.control.ActionResponse
|
||||
import dev.lucasnlm.antimine.core.control.ActionFeedback
|
||||
import dev.lucasnlm.antimine.core.control.GameControl
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
|
@ -61,6 +62,7 @@ class GameViewModel @ViewModelInject constructor(
|
|||
)
|
||||
|
||||
gameController = GameController(minefield, minefieldRepository.randomSeed())
|
||||
updateGameControl()
|
||||
|
||||
mineCount.postValue(minefield.mines)
|
||||
difficulty.postValue(newDifficulty)
|
||||
|
@ -86,6 +88,7 @@ class GameViewModel @ViewModelInject constructor(
|
|||
|
||||
val setup = save.minefield
|
||||
gameController = GameController(save)
|
||||
updateGameControl()
|
||||
|
||||
mineCount.postValue(setup.mines)
|
||||
difficulty.postValue(save.difficulty)
|
||||
|
@ -114,6 +117,7 @@ class GameViewModel @ViewModelInject constructor(
|
|||
plantMinesExcept(save.firstOpen.value, true)
|
||||
singleClick(save.firstOpen.value)
|
||||
}
|
||||
updateGameControl()
|
||||
}
|
||||
|
||||
mineCount.postValue(setup.mines)
|
||||
|
@ -252,11 +256,19 @@ class GameViewModel @ViewModelInject constructor(
|
|||
}
|
||||
|
||||
private fun onFeedbackAnalytics(feedback: ActionFeedback) {
|
||||
when (feedback.action) {
|
||||
Action.OpenTile -> { analyticsManager.sentEvent(Analytics.OpenTile(feedback.index)) }
|
||||
Action.SwitchMark -> { analyticsManager.sentEvent(Analytics.SwitchMark(feedback.index)) }
|
||||
Action.HighlightNeighbors -> { analyticsManager.sentEvent(Analytics.HighlightNeighbors(feedback.index)) }
|
||||
Action.OpenNeighbors -> { analyticsManager.sentEvent(Analytics.OpenNeighbors(feedback.index)) }
|
||||
when (feedback.actionResponse) {
|
||||
ActionResponse.OpenTile -> {
|
||||
analyticsManager.sentEvent(Analytics.OpenTile(feedback.index))
|
||||
}
|
||||
ActionResponse.SwitchMark -> {
|
||||
analyticsManager.sentEvent(Analytics.SwitchMark(feedback.index))
|
||||
}
|
||||
ActionResponse.HighlightNeighbors -> {
|
||||
analyticsManager.sentEvent(Analytics.HighlightNeighbors(feedback.index))
|
||||
}
|
||||
ActionResponse.OpenNeighbors -> {
|
||||
analyticsManager.sentEvent(Analytics.OpenNeighbors(feedback.index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,6 +295,12 @@ class GameViewModel @ViewModelInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun updateGameControl() {
|
||||
val controlType = preferencesRepository.controlType()
|
||||
val gameControl = GameControl.fromControlType(controlType)
|
||||
gameController.updateGameControl(gameControl)
|
||||
}
|
||||
|
||||
fun runClock() {
|
||||
clock.run {
|
||||
if (isStopped) start {
|
||||
|
|
18
common/src/main/java/dev/lucasnlm/antimine/core/CoreExt.kt
Normal file
18
common/src/main/java/dev/lucasnlm/antimine/core/CoreExt.kt
Normal file
|
@ -0,0 +1,18 @@
|
|||
package dev.lucasnlm.antimine.core
|
||||
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelLazy
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
|
||||
@MainThread
|
||||
inline fun <reified VM : ViewModel> Fragment.viewModels(
|
||||
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
|
||||
): Lazy<VM> {
|
||||
val factoryPromise = factoryProducer ?: {
|
||||
defaultViewModelProviderFactory
|
||||
}
|
||||
|
||||
return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
|
||||
}
|
|
@ -14,6 +14,6 @@ class DebugAnalyticsManager : AnalyticsManager {
|
|||
}
|
||||
|
||||
companion object {
|
||||
val TAG = DebugAnalyticsManager::class.simpleName
|
||||
const val TAG = "DebugAnalyticsManager"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,64 +1,101 @@
|
|||
package dev.lucasnlm.antimine.core.control
|
||||
|
||||
enum class Action {
|
||||
/**
|
||||
* Possible action response to an user action.
|
||||
*/
|
||||
enum class ActionResponse {
|
||||
OpenTile,
|
||||
SwitchMark,
|
||||
HighlightNeighbors,
|
||||
OpenNeighbors,
|
||||
}
|
||||
|
||||
/**
|
||||
* [Actions] links an [ActionResponse] to an user action.
|
||||
*/
|
||||
data class Actions(
|
||||
val singleClick: Action?,
|
||||
val doubleClick: Action?,
|
||||
val longPress: Action?
|
||||
val singleClick: ActionResponse?,
|
||||
val doubleClick: ActionResponse?,
|
||||
val longPress: ActionResponse?
|
||||
)
|
||||
|
||||
/**
|
||||
* These are the current available game control styles.
|
||||
* Check [GameControl] to details.
|
||||
*/
|
||||
enum class ControlStyle {
|
||||
Standard,
|
||||
DoubleClick,
|
||||
FastFlag
|
||||
}
|
||||
|
||||
/**
|
||||
* [GameControl] will map an user action (from [Actions]) to an [ActionResponse].
|
||||
* This is necessary because same users rather that single click open the tile, other that it flags the tile.
|
||||
*/
|
||||
sealed class GameControl(
|
||||
val id: ControlStyle,
|
||||
val onCovered: Actions,
|
||||
val onOpen: Actions
|
||||
) {
|
||||
object Standard : GameControl(
|
||||
id = ControlStyle.Standard,
|
||||
onCovered = Actions(
|
||||
singleClick = Action.OpenTile,
|
||||
longPress = Action.SwitchMark,
|
||||
singleClick = ActionResponse.OpenTile,
|
||||
longPress = ActionResponse.SwitchMark,
|
||||
doubleClick = null
|
||||
),
|
||||
onOpen = Actions(
|
||||
singleClick = Action.HighlightNeighbors,
|
||||
longPress = Action.OpenNeighbors,
|
||||
singleClick = ActionResponse.HighlightNeighbors,
|
||||
longPress = ActionResponse.OpenNeighbors,
|
||||
doubleClick = null
|
||||
)
|
||||
)
|
||||
|
||||
object FastFlag : GameControl(
|
||||
id = ControlStyle.FastFlag,
|
||||
onCovered = Actions(
|
||||
singleClick = Action.SwitchMark,
|
||||
longPress = Action.OpenTile,
|
||||
singleClick = ActionResponse.SwitchMark,
|
||||
longPress = ActionResponse.OpenTile,
|
||||
doubleClick = null
|
||||
),
|
||||
onOpen = Actions(
|
||||
singleClick = Action.OpenNeighbors,
|
||||
longPress = Action.HighlightNeighbors,
|
||||
singleClick = ActionResponse.OpenNeighbors,
|
||||
longPress = ActionResponse.HighlightNeighbors,
|
||||
doubleClick = null
|
||||
)
|
||||
)
|
||||
|
||||
object DoubleClick : GameControl(
|
||||
id = ControlStyle.DoubleClick,
|
||||
onCovered = Actions(
|
||||
singleClick = Action.SwitchMark,
|
||||
singleClick = ActionResponse.SwitchMark,
|
||||
longPress = null,
|
||||
doubleClick = Action.OpenTile
|
||||
doubleClick = ActionResponse.OpenTile
|
||||
),
|
||||
onOpen = Actions(
|
||||
singleClick = Action.HighlightNeighbors,
|
||||
singleClick = ActionResponse.HighlightNeighbors,
|
||||
longPress = null,
|
||||
doubleClick = Action.OpenNeighbors
|
||||
doubleClick = ActionResponse.OpenNeighbors
|
||||
)
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun fromControlType(controlStyle: ControlStyle): GameControl {
|
||||
return when (controlStyle) {
|
||||
ControlStyle.Standard -> Standard
|
||||
ControlStyle.DoubleClick -> DoubleClick
|
||||
ControlStyle.FastFlag -> FastFlag
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data class used to make feedback or analytics to an user action.
|
||||
*/
|
||||
data class ActionFeedback(
|
||||
val action: Action?,
|
||||
val actionResponse: ActionResponse?,
|
||||
val index: Int,
|
||||
val multipleChanges: Boolean
|
||||
)
|
||||
|
|
|
@ -9,7 +9,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
|
||||
import dev.lucasnlm.antimine.core.analytics.DebugAnalyticsManager
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
import dev.lucasnlm.antimine.core.preferences.PreferencesInteractor
|
||||
import dev.lucasnlm.antimine.core.preferences.PreferencesManager
|
||||
import dev.lucasnlm.antimine.core.preferences.PreferencesRepository
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -19,14 +19,14 @@ class CommonModule {
|
|||
@Singleton
|
||||
@Provides
|
||||
fun providePreferencesRepository(
|
||||
preferencesInteractor: PreferencesInteractor
|
||||
): IPreferencesRepository = PreferencesRepository(preferencesInteractor)
|
||||
preferencesManager: PreferencesManager
|
||||
): IPreferencesRepository = PreferencesRepository(preferencesManager)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun providePreferencesInteractor(
|
||||
@ApplicationContext context: Context
|
||||
): PreferencesInteractor = PreferencesInteractor(context)
|
||||
): PreferencesManager = PreferencesManager(context)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
|
|
|
@ -5,7 +5,7 @@ import androidx.preference.PreferenceManager
|
|||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import javax.inject.Inject
|
||||
|
||||
class PreferencesInteractor @Inject constructor(
|
||||
class PreferencesManager @Inject constructor(
|
||||
private val context: Context
|
||||
) {
|
||||
private val preferences by lazy {
|
||||
|
@ -34,6 +34,10 @@ class PreferencesInteractor @Inject constructor(
|
|||
|
||||
fun putInt(key: String, value: Int) = preferences.edit().putInt(key, value).apply()
|
||||
|
||||
fun removeKey(key: String) {
|
||||
preferences.edit().remove(key).apply()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PREFERENCE_CUSTOM_GAME_WIDTH = "preference_custom_game_width"
|
||||
private const val PREFERENCE_CUSTOM_GAME_HEIGHT = "preference_custom_game_height"
|
||||
|
|
|
@ -1,43 +1,50 @@
|
|||
package dev.lucasnlm.antimine.core.preferences
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import dev.lucasnlm.antimine.core.control.ControlStyle
|
||||
|
||||
interface IPreferencesRepository {
|
||||
fun customGameMode(): Minefield
|
||||
fun updateCustomGameMode(minefield: Minefield)
|
||||
fun getBoolean(key: String, defaultValue: Boolean): Boolean
|
||||
fun getInt(key: String, defaultValue: Int): Int
|
||||
fun putBoolean(key: String, value: Boolean)
|
||||
fun putInt(key: String, value: Int)
|
||||
|
||||
fun customGameMode(): Minefield
|
||||
fun updateCustomGameMode(minefield: Minefield)
|
||||
|
||||
fun controlType(): ControlStyle
|
||||
fun useControlType(controlStyle: ControlStyle)
|
||||
|
||||
fun useFlagAssistant(): Boolean
|
||||
fun useHapticFeedback(): Boolean
|
||||
fun useLargeAreas(): Boolean
|
||||
fun useAnimations(): Boolean
|
||||
fun useDoubleClickToOpen(): Boolean
|
||||
}
|
||||
|
||||
class PreferencesRepository(
|
||||
private val preferencesInteractor: PreferencesInteractor
|
||||
private val preferencesManager: PreferencesManager
|
||||
) : IPreferencesRepository {
|
||||
init {
|
||||
migrateOldPreferences()
|
||||
}
|
||||
|
||||
override fun customGameMode(): Minefield =
|
||||
preferencesInteractor.getCustomMode()
|
||||
preferencesManager.getCustomMode()
|
||||
|
||||
override fun updateCustomGameMode(minefield: Minefield) =
|
||||
preferencesInteractor.updateCustomMode(minefield)
|
||||
preferencesManager.updateCustomMode(minefield)
|
||||
|
||||
override fun getBoolean(key: String, defaultValue: Boolean): Boolean =
|
||||
preferencesInteractor.getBoolean(key, defaultValue)
|
||||
preferencesManager.getBoolean(key, defaultValue)
|
||||
|
||||
override fun putBoolean(key: String, value: Boolean) =
|
||||
preferencesInteractor.putBoolean(key, value)
|
||||
preferencesManager.putBoolean(key, value)
|
||||
|
||||
override fun getInt(key: String, defaultValue: Int): Int =
|
||||
preferencesInteractor.getInt(key, defaultValue)
|
||||
preferencesManager.getInt(key, defaultValue)
|
||||
|
||||
override fun putInt(key: String, value: Int) =
|
||||
preferencesInteractor.putInt(key, value)
|
||||
preferencesManager.putInt(key, value)
|
||||
|
||||
override fun useFlagAssistant(): Boolean =
|
||||
getBoolean("preference_assistant", true)
|
||||
|
@ -51,6 +58,19 @@ class PreferencesRepository(
|
|||
override fun useAnimations(): Boolean =
|
||||
getBoolean("preference_animation", true)
|
||||
|
||||
override fun useDoubleClickToOpen(): Boolean =
|
||||
getBoolean("preference_double_click_open", false)
|
||||
override fun controlType(): ControlStyle {
|
||||
val index = getInt("preference_control_type", -1)
|
||||
return ControlStyle.values().getOrNull(index) ?: ControlStyle.Standard
|
||||
}
|
||||
|
||||
override fun useControlType(controlStyle: ControlStyle) {
|
||||
putInt("preference_control_type", controlStyle.ordinal)
|
||||
}
|
||||
|
||||
private fun migrateOldPreferences() {
|
||||
if (getBoolean("preference_double_click_open", false)) {
|
||||
useControlType(ControlStyle.DoubleClick)
|
||||
preferencesManager.removeKey("preference_double_click_open")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">Accessibility</string>
|
||||
<string name="settings_large_areas">Use Large Areas</string>
|
||||
<string name="settings_large_areas_desc">Increases the touch area</string>
|
||||
<string name="settings_double_click">Double Click</string>
|
||||
<string name="double_click">Double Click</string>
|
||||
<string name="rating">Feedback</string>
|
||||
<string name="rating_menu">Feedback ❤</string>
|
||||
<string name="rating_message">If you like this game, please give us a feedback. It will help us a lot.</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">Přístupnost</string>
|
||||
<string name="settings_large_areas">Použít velké oblasti</string>
|
||||
<string name="settings_large_areas_desc">Zvyšuje dotykovou oblast</string>
|
||||
<string name="settings_double_click">Poklepání</string>
|
||||
<string name="double_click">Poklepání</string>
|
||||
<string name="rating">Komentář</string>
|
||||
<string name="rating_menu">Komentář ❤</string>
|
||||
<string name="rating_message">Pokud se vám tato hra líbí, dejte nám prosím zpětnou vazbu. Hodně nám to pomůže.</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">Barrierefreiheit</string>
|
||||
<string name="settings_large_areas">Große Flächen verwenden</string>
|
||||
<string name="settings_large_areas_desc">Vergrößert die Tastfläche</string>
|
||||
<string name="settings_double_click">Doppelklick</string>
|
||||
<string name="double_click">Doppelklick</string>
|
||||
<string name="rating">Rückmeldung</string>
|
||||
<string name="rating_menu">Rückmeldung ❤</string>
|
||||
<string name="rating_message">Wenn dir dieses Spiel gefällt, gib uns bitte eine Rückmeldung. Es wird uns sehr helfen.</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">Προσβασιμότητα</string>
|
||||
<string name="settings_large_areas">Χρησιμοποιήστε μεγάλες περιοχές</string>
|
||||
<string name="settings_large_areas_desc">Αύξηση της περιοχής κουμπιών</string>
|
||||
<string name="settings_double_click">Διπλό Κλικ</string>
|
||||
<string name="double_click">Διπλό Κλικ</string>
|
||||
<string name="rating">Αξιολόγηση</string>
|
||||
<string name="rating_menu">Αξιολόγηση ❤</string>
|
||||
<string name="rating_message">Αν σας αρέσει αυτό το παιχνίδι, παρακαλούμε δώστε μας τα σχόλιά σας. Θα μας βοηθήσει πολύ.</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">Accesibilidad</string>
|
||||
<string name="settings_large_areas">Usar áreas grandes</string>
|
||||
<string name="settings_large_areas_desc">Aumenta la zona táctil</string>
|
||||
<string name="settings_double_click">Doble clic</string>
|
||||
<string name="double_click">Doble clic</string>
|
||||
<string name="rating">Comentarios</string>
|
||||
<string name="rating_menu">Comentarios ❤</string>
|
||||
<string name="rating_message">Si te gusta este juego, por favor danos un comentario. Nos ayudará mucho.</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">Accessibilité</string>
|
||||
<string name="settings_large_areas">Utiliser des grandes cases</string>
|
||||
<string name="settings_large_areas_desc">Augmenter la zone d\'interaction</string>
|
||||
<string name="settings_double_click">Cliquer deux fois</string>
|
||||
<string name="double_click">Cliquer deux fois</string>
|
||||
<string name="rating">Retour d\'expérience</string>
|
||||
<string name="rating_menu">Retour d\'expérience ❤️</string>
|
||||
<string name="rating_message">Si vous aimez ce jeu, n\'hésitez pas à jour donner un retour. Ça nous serait très utile.</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">Accessibilità</string>
|
||||
<string name="settings_large_areas">Usa grandi aree</string>
|
||||
<string name="settings_large_areas_desc">Aumenta l\'area di tocco</string>
|
||||
<string name="settings_double_click">Doppio click</string>
|
||||
<string name="double_click">Doppio click</string>
|
||||
<string name="rating">Suggerimenti</string>
|
||||
<string name="rating_menu">Suggerimenti ❤</string>
|
||||
<string name="rating_message">Se ti piace questo gioco, per favore inviaci suggerimenti. Puoi aiutare a migliorarlo.</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">アクセシビリティ</string>
|
||||
<string name="settings_large_areas">範囲を広くする</string>
|
||||
<string name="settings_large_areas_desc">タッチ範囲を増やします</string>
|
||||
<string name="settings_double_click">ダブルクリック</string>
|
||||
<string name="double_click">ダブルクリック</string>
|
||||
<string name="rating">フィードバック</string>
|
||||
<string name="rating_menu">フィードバック ❤</string>
|
||||
<string name="rating_message">もしこのゲームを気に入ったなら、フィードバックをお願いします。私たちへの助けになるはずです。</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">Acessibilidade</string>
|
||||
<string name="settings_large_areas">Usar Área Grande</string>
|
||||
<string name="settings_large_areas_desc">Aumenta a área tocável</string>
|
||||
<string name="settings_double_click">Duplo Clique</string>
|
||||
<string name="double_click">Duplo Clique</string>
|
||||
<string name="rating">Avalie o app</string>
|
||||
<string name="rating_menu">Avaliar o app ❤</string>
|
||||
<string name="rating_message">Se você está gostando do jogo, por favor deixe um comentário! Isso nos ajuda muito.</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">Специальные возможности</string>
|
||||
<string name="settings_large_areas">Использовать большие области</string>
|
||||
<string name="settings_large_areas_desc">Увеличивает зону касания</string>
|
||||
<string name="settings_double_click">Два нажатия</string>
|
||||
<string name="double_click">Два нажатия</string>
|
||||
<string name="rating">Обратная связь</string>
|
||||
<string name="rating_menu">Обратная связь ❤</string>
|
||||
<string name="rating_message">Нравится игра? Оставьте отзыв, пожалуйста. Это нам очень поможет.</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">Erişilebilirlik</string>
|
||||
<string name="settings_large_areas">Geniş Alanlar Kullan</string>
|
||||
<string name="settings_large_areas_desc">Dokunma alanını artırır</string>
|
||||
<string name="settings_double_click">Çift Tıklama</string>
|
||||
<string name="double_click">Çift Tıklama</string>
|
||||
<string name="rating">Geri bildirim</string>
|
||||
<string name="rating_menu">Geri bildirim ❤</string>
|
||||
<string name="rating_message">Bu oyunu beğendiyseniz, lütfen bize bir geri bildirim verin. Bize çok yardımcı olacak.</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">Спеціальні можливості</string>
|
||||
<string name="settings_large_areas">Використовувати збільшену територію</string>
|
||||
<string name="settings_large_areas_desc">Збільшує площу дотику</string>
|
||||
<string name="settings_double_click">Подвійний клік</string>
|
||||
<string name="double_click">Подвійний клік</string>
|
||||
<string name="rating">Відгук</string>
|
||||
<string name="rating_menu">Відгук ❤</string>
|
||||
<string name="rating_message">Якщо вам подобається ця гра, то залиште нам свій відгук. Це нам дуже допоможе.</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">Trợ năng</string>
|
||||
<string name="settings_large_areas">Vùng chạm lớn</string>
|
||||
<string name="settings_large_areas_desc">Tăng kích cỡ các vùng chạm</string>
|
||||
<string name="settings_double_click">Nháy kép</string>
|
||||
<string name="double_click">Nháy kép</string>
|
||||
<string name="rating">Phản hồi</string>
|
||||
<string name="rating_menu">Phản hồi ❤</string>
|
||||
<string name="rating_message">Nếu bạn thích trò chơi này, hãy gửi phản hồi cho chúng tôi. Nhận xét của bạn sẽ giúp chúng tôi rất nhiều.</string>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<string name="settings_accessibility">无障碍</string>
|
||||
<string name="settings_large_areas">使用大区域</string>
|
||||
<string name="settings_large_areas_desc">增加触摸区域</string>
|
||||
<string name="settings_double_click">双击</string>
|
||||
<string name="double_click">双击</string>
|
||||
<string name="rating">反馈</string>
|
||||
<string name="rating_menu">反馈 ❤</string>
|
||||
<string name="rating_message">如果你喜欢这个游戏,请给我们反馈。这将对我们有很多帮助。</string>
|
||||
|
|
|
@ -91,7 +91,6 @@
|
|||
<string name="settings_accessibility">Accessibility</string>
|
||||
<string name="settings_large_areas">Use Large Areas</string>
|
||||
<string name="settings_large_areas_desc">Increases the touch area</string>
|
||||
<string name="settings_double_click">Double Click</string>
|
||||
<string name="rating">Feedback</string>
|
||||
<string name="rating_menu">Feedback ❤</string>
|
||||
<string name="rating_message">If you like this game, please give us a feedback. It will help us a lot.</string>
|
||||
|
@ -101,4 +100,12 @@
|
|||
<string name="total_time">Total Time</string>
|
||||
<string name="average_time">Average Time</string>
|
||||
<string name="performance">Performance</string>
|
||||
|
||||
<string name="control">Control</string>
|
||||
<string name="arrow">➞</string>
|
||||
<string name="single_click">Single Click</string>
|
||||
<string name="double_click">Double Click</string>
|
||||
<string name="long_press">Long Press</string>
|
||||
<string name="open_tile">Open Area</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
android:checked="false"
|
||||
android:defaultValue="false"
|
||||
android:key="preference_double_click_open"
|
||||
android:title="@string/settings_double_click"
|
||||
android:title="@string/double_click"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
|
|
Loading…
Reference in a new issue