Open area using double click

This commit is contained in:
Lucas Lima 2020-04-02 10:18:20 -03:00
parent 0322fa1d9c
commit 8be7333155
7 changed files with 163 additions and 66 deletions

View file

@ -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)
}
}
}

View file

@ -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 -> {

View file

@ -5,6 +5,6 @@ import androidx.recyclerview.widget.RecyclerView
class AreaViewHolder(view: View) : RecyclerView.ViewHolder(view) {
init {
this.itemView.isFocusable = true
itemView.isFocusable = true
}
}

View file

@ -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() {

View file

@ -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)
}

View file

@ -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>

View file

@ -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">