Open area using double click
This commit is contained in:
parent
0322fa1d9c
commit
8be7333155
7 changed files with 163 additions and 66 deletions
|
@ -5,7 +5,9 @@ import android.graphics.Paint
|
|||
import android.graphics.RectF
|
||||
import android.graphics.Typeface
|
||||
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.R
|
||||
|
@ -37,7 +39,7 @@ class AreaAdapter(
|
|||
}
|
||||
|
||||
fun setClickEnabled(value: Boolean) {
|
||||
this.clickEnabled = value
|
||||
clickEnabled = value
|
||||
}
|
||||
|
||||
fun bindField(field: Sequence<Area>) {
|
||||
|
@ -45,62 +47,85 @@ class AreaAdapter(
|
|||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemCount() = field.size
|
||||
override fun getItemCount(): Int = field.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AreaViewHolder {
|
||||
val view = AreaView(parent.context)
|
||||
val holder = AreaViewHolder(view)
|
||||
return AreaViewHolder(view).apply {
|
||||
view.setOnDoubleClickListener(object : GestureDetector.OnDoubleTapListener {
|
||||
override fun onDoubleTap(e: MotionEvent?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
holder.itemView.setOnLongClickListener { target ->
|
||||
target.requestFocus()
|
||||
|
||||
val position = holder.adapterPosition
|
||||
if (position == RecyclerView.NO_POSITION) {
|
||||
Log.d(TAG, "Item no longer exists.")
|
||||
} else if (clickEnabled) {
|
||||
viewModel.onLongClick(position)
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
val position = holder.adapterPosition
|
||||
if (position == RecyclerView.NO_POSITION) {
|
||||
Log.d(TAG, "Item no longer exists.")
|
||||
} else if (clickEnabled) {
|
||||
viewModel.onClickArea(position)
|
||||
}
|
||||
}
|
||||
|
||||
holder.itemView.setOnKeyListener { _, keyCode, keyEvent ->
|
||||
var handled = false
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
when (keyEvent.action) {
|
||||
KeyEvent.ACTION_DOWN -> {
|
||||
longPressAt = System.currentTimeMillis()
|
||||
handled = true
|
||||
}
|
||||
KeyEvent.ACTION_UP -> {
|
||||
if (clickEnabled) {
|
||||
val value = System.currentTimeMillis() - longPressAt
|
||||
if (value > 300L) {
|
||||
view.performLongClick()
|
||||
} else {
|
||||
view.callOnClick()
|
||||
}
|
||||
override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
|
||||
val position = adapterPosition
|
||||
return when {
|
||||
position == RecyclerView.NO_POSITION -> {
|
||||
Log.d(TAG, "Item no longer exists.")
|
||||
false
|
||||
}
|
||||
clickEnabled -> {
|
||||
viewModel.onDoubleClickArea(position)
|
||||
true
|
||||
}
|
||||
else -> {
|
||||
false
|
||||
}
|
||||
longPressAt = System.currentTimeMillis()
|
||||
handled = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean = false
|
||||
})
|
||||
|
||||
itemView.setOnLongClickListener { target ->
|
||||
target.requestFocus()
|
||||
|
||||
val position = adapterPosition
|
||||
if (position == RecyclerView.NO_POSITION) {
|
||||
Log.d(TAG, "Item no longer exists.")
|
||||
} else if (clickEnabled) {
|
||||
viewModel.onLongClick(position)
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
itemView.setOnClickListener {
|
||||
val position = adapterPosition
|
||||
if (position == RecyclerView.NO_POSITION) {
|
||||
Log.d(TAG, "Item no longer exists.")
|
||||
} else if (clickEnabled) {
|
||||
viewModel.onClickArea(position)
|
||||
}
|
||||
}
|
||||
|
||||
handled
|
||||
}
|
||||
itemView.setOnKeyListener { _, keyCode, keyEvent ->
|
||||
var handled = false
|
||||
|
||||
return holder
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
when (keyEvent.action) {
|
||||
KeyEvent.ACTION_DOWN -> {
|
||||
longPressAt = System.currentTimeMillis()
|
||||
handled = true
|
||||
}
|
||||
KeyEvent.ACTION_UP -> {
|
||||
if (clickEnabled) {
|
||||
val value = System.currentTimeMillis() - longPressAt
|
||||
if (value > 300L) {
|
||||
view.performLongClick()
|
||||
} else {
|
||||
view.callOnClick()
|
||||
}
|
||||
}
|
||||
longPressAt = System.currentTimeMillis()
|
||||
handled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getItem(position: Int) = field[position]
|
||||
|
@ -109,8 +134,10 @@ class AreaAdapter(
|
|||
|
||||
override fun onBindViewHolder(holder: AreaViewHolder, position: Int) {
|
||||
val field = getItem(position)
|
||||
if (holder.itemView is AreaView) {
|
||||
holder.itemView.bindField(field, isAmbientMode, isLowBitAmbient, paintSettings)
|
||||
holder.run {
|
||||
if (itemView is AreaView) {
|
||||
itemView.bindField(field, isAmbientMode, isLowBitAmbient, paintSettings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
package dev.lucasnlm.antimine.common.level.view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import androidx.core.content.ContextCompat
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import dev.lucasnlm.antimine.common.level.repository.DrawableRepository
|
||||
import android.util.TypedValue
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import dev.lucasnlm.antimine.common.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.models.AreaPalette
|
||||
import dev.lucasnlm.antimine.common.level.models.Mark
|
||||
import dev.lucasnlm.antimine.common.level.repository.DrawableRepository
|
||||
|
||||
class AreaView : View {
|
||||
// Used on Wear OS
|
||||
|
@ -25,6 +28,7 @@ class AreaView : View {
|
|||
private lateinit var paintSettings: AreaPaintSettings
|
||||
private lateinit var palette: AreaPalette
|
||||
private val drawableRepository = DrawableRepository()
|
||||
private var gestureDetector: GestureDetector? = null
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
|
@ -36,6 +40,13 @@ class AreaView : View {
|
|||
isHapticFeedbackEnabled = true
|
||||
}
|
||||
|
||||
fun setOnDoubleClickListener(listener: GestureDetector.OnDoubleTapListener) {
|
||||
if (gestureDetector == null) {
|
||||
gestureDetector = GestureDetector(context, GestureDetector.SimpleOnGestureListener())
|
||||
}
|
||||
gestureDetector?.setOnDoubleTapListener(listener)
|
||||
}
|
||||
|
||||
fun bindField(area: Area, isAmbientMode: Boolean, isLowBitAmbient: Boolean, paintSettings: AreaPaintSettings) {
|
||||
this.paintSettings = paintSettings
|
||||
|
||||
|
@ -71,6 +82,10 @@ class AreaView : View {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent?): Boolean =
|
||||
(gestureDetector?.onTouchEvent(event) ?: false) || super.onTouchEvent(event)
|
||||
|
||||
private fun bindContentDescription(area: Area) {
|
||||
contentDescription = when {
|
||||
area.mark == Mark.Flag -> {
|
||||
|
|
|
@ -5,6 +5,6 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
|
||||
class AreaViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
init {
|
||||
this.itemView.isFocusable = true
|
||||
itemView.isFocusable = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ class GameViewModel(
|
|||
}
|
||||
|
||||
analyticsManager.sentEvent(Analytics.LongPressArea(index))
|
||||
} else {
|
||||
} else if (!preferencesRepository.useDoubleClickToOpen()) {
|
||||
levelFacade.openNeighbors(index).forEach { refreshIndex(it.id) }
|
||||
|
||||
analyticsManager.sentEvent(Analytics.LongPressMultipleArea(index))
|
||||
|
@ -159,17 +159,12 @@ class GameViewModel(
|
|||
updateGameState()
|
||||
}
|
||||
|
||||
fun onClickArea(index: Int) {
|
||||
fun onDoubleClickArea(index: Int) {
|
||||
if (levelFacade.turnOffAllHighlighted()) {
|
||||
refreshAll()
|
||||
}
|
||||
|
||||
if (levelFacade.hasMarkOn(index)) {
|
||||
levelFacade.removeMark(index).run {
|
||||
refreshIndex(id)
|
||||
}
|
||||
hapticFeedbackInteractor.toggleFlagFeedback()
|
||||
} else {
|
||||
if (preferencesRepository.useDoubleClickToOpen()) {
|
||||
if (!levelFacade.hasMines) {
|
||||
levelFacade.plantMinesExcept(index, true)
|
||||
}
|
||||
|
@ -191,6 +186,44 @@ class GameViewModel(
|
|||
analyticsManager.sentEvent(Analytics.PressArea(index))
|
||||
}
|
||||
|
||||
fun onClickArea(index: Int) {
|
||||
var openAnyArea = false
|
||||
|
||||
if (levelFacade.turnOffAllHighlighted()) {
|
||||
refreshAll()
|
||||
}
|
||||
|
||||
if (levelFacade.hasMarkOn(index)) {
|
||||
levelFacade.removeMark(index).run {
|
||||
refreshIndex(id)
|
||||
}
|
||||
hapticFeedbackInteractor.toggleFlagFeedback()
|
||||
} else if (!preferencesRepository.useDoubleClickToOpen()) {
|
||||
if (!levelFacade.hasMines) {
|
||||
levelFacade.plantMinesExcept(index, true)
|
||||
}
|
||||
|
||||
levelFacade.clickArea(index).run {
|
||||
refreshIndex(index, this)
|
||||
}
|
||||
|
||||
openAnyArea = true
|
||||
}
|
||||
|
||||
if (openAnyArea) {
|
||||
if (preferencesRepository.useFlagAssistant() && !levelFacade.hasAnyMineExploded()) {
|
||||
levelFacade.runFlagAssistant().forEach {
|
||||
Handler().post {
|
||||
refreshIndex(it.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateGameState()
|
||||
analyticsManager.sentEvent(Analytics.PressArea(index))
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshMineCount() = mineCount.postValue(levelFacade.remainingMines())
|
||||
|
||||
private fun updateGameState() {
|
||||
|
|
|
@ -14,6 +14,7 @@ interface IPreferencesRepository {
|
|||
fun useHapticFeedback(): Boolean
|
||||
fun useLargeAreas(): Boolean
|
||||
fun useAnimations(): Boolean
|
||||
fun useDoubleClickToOpen(): Boolean
|
||||
}
|
||||
|
||||
class PreferencesRepository(
|
||||
|
@ -49,4 +50,7 @@ class PreferencesRepository(
|
|||
|
||||
override fun useAnimations(): Boolean =
|
||||
getBoolean("preference_animation", true)
|
||||
|
||||
override fun useDoubleClickToOpen(): Boolean =
|
||||
getBoolean("preference_double_click_open", false)
|
||||
}
|
||||
|
|
|
@ -82,9 +82,12 @@
|
|||
<string name="settings_vibration_desc">Vibrates on explosion or flag toggle</string>
|
||||
<string name="settings_sound_desc">Makes a sound of explosion</string>
|
||||
<string name="settings_auto_flag_desc">Adds a flag on resolved mines automatically</string>
|
||||
<string name="settings_gameplay">Gameplay</string>
|
||||
<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="settings_double_click_desc">Open using 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>
|
||||
|
|
|
@ -1,7 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.preference.PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/settings_gameplay"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:checked="false"
|
||||
android:defaultValue="false"
|
||||
android:key="preference_double_click_open"
|
||||
android:title="@string/settings_double_click"
|
||||
android:summary="@string/settings_double_click_desc"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/settings_general"
|
||||
app:iconSpaceReserved="false">
|
||||
|
|
Loading…
Reference in a new issue