Work on board rows

hh
Laurent 6 years ago
parent 70df3195e5
commit 66730eb43e
  1. 14
      app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt
  2. 3
      app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt
  3. 79
      app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt
  4. 6
      app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt
  5. 112
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryAdapter.kt
  6. 9
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryFragment.kt
  7. 2
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/views/StreetCardView.kt
  8. 62
      app/src/main/res/layout/row_hand_cards.xml
  9. 3
      app/src/main/res/values/colors.xml
  10. 3
      app/src/main/res/values/styles.xml

@ -5,7 +5,7 @@ import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.handhistory.Action import net.pokeranalytics.android.model.realm.handhistory.Action
import net.pokeranalytics.android.model.realm.handhistory.HandHistory import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.ui.modules.handhistory.HandRowType import net.pokeranalytics.android.ui.modules.handhistory.HandRowType
import net.pokeranalytics.android.ui.modules.handhistory.views.StreetCardHeader import net.pokeranalytics.android.ui.modules.handhistory.views.StreetCardView
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import timber.log.Timber import timber.log.Timber
@ -31,11 +31,6 @@ class HHBuilder {
load() load()
} }
/***
* The map containing all actions present in a street
*/
// private val actionsPerStreet = hashMapOf<Street, List<ComputedAction>>()
/*** /***
* All actions sorted by index * All actions sorted by index
*/ */
@ -374,6 +369,9 @@ class HHBuilder {
} }
/***
* The list of row representables, generated from the hand history actions
*/
private var currentRowRepresentables = mutableListOf<RowRepresentable>() private var currentRowRepresentables = mutableListOf<RowRepresentable>()
fun rowRepresentables() : List<RowRepresentable> { fun rowRepresentables() : List<RowRepresentable> {
@ -391,8 +389,8 @@ class HHBuilder {
rows.add(CustomizableRowRepresentable(customViewType = HandRowType.HEADER, resId = street.resId)) rows.add(CustomizableRowRepresentable(customViewType = HandRowType.HEADER, resId = street.resId))
// Cards if not preflop // Cards if not preflop
if (street.totalBoardCards > 0) { if (street.totalBoardCards >= 0) {
rows.add(StreetCardHeader(street, this.handHistory.cardsForStreet(street), potSize)) rows.add(StreetCardView(street, this.handHistory.cardsForStreet(street), potSize))
} }
// Actions // Actions

@ -192,10 +192,9 @@ class PokerAnalyticsMigration : RealmMigration {
hhSchema.addField("year", Integer::class.java) hhSchema.addField("year", Integer::class.java)
hhSchema.addField("dayOfMonth", Integer::class.java) hhSchema.addField("dayOfMonth", Integer::class.java)
val cardSchema = schema.create("Card") val cardSchema = schema.create("Card")
cardSchema.addField("value", Int::class.java) cardSchema.addField("value", Int::class.java)
cardSchema.addField("suit", Int::class.java) cardSchema.addField("suitIdentifier", Int::class.java)
cardSchema.addField("index", Int::class.java) cardSchema.addField("index", Int::class.java)
hhSchema.addRealmListField("board", cardSchema) hhSchema.addRealmListField("board", cardSchema)

@ -1,16 +1,47 @@
package net.pokeranalytics.android.model.realm.handhistory package net.pokeranalytics.android.model.realm.handhistory
import android.content.Context
import android.text.SpannableString
import android.text.TextUtils
import android.text.style.ForegroundColorSpan
import io.realm.RealmObject import io.realm.RealmObject
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.exceptions.PAIllegalStateException
import timber.log.Timber
interface CardProperty interface CardProperty
fun List<Card>.formatted(context: Context) : CharSequence? {
var span: CharSequence? = null
this.forEach {
val formatted = it.formatted(context)
if (span == null) {
span = formatted
}
else {
span = TextUtils.concat(span, formatted)
}
}
return span
}
open class Card : RealmObject() { open class Card : RealmObject() {
companion object {
fun valueOf(value: Int, suit: Suit) : Card {
val card = Card()
card.value = value
card.suit = suit
Timber.d("suit = ${card.suitIdentifier}")
return card
}
}
class Value(var value: Int) : CardProperty { class Value(var value: Int) : CardProperty {
val formatted: String companion object {
get() {
fun format(value: Int) : String {
return when(value) { return when(value) {
0 -> "x" 0 -> "x"
in 2..9 -> "$value" in 2..9 -> "$value"
@ -21,8 +52,14 @@ open class Card : RealmObject() {
14 -> "A" 14 -> "A"
else -> throw PAIllegalStateException("card value '$value' not handled") else -> throw PAIllegalStateException("card value '$value' not handled")
} }
}
} }
val formatted: String
get() { return format(this.value) }
} }
enum class Suit(val value: String) : CardProperty { enum class Suit(val value: String) : CardProperty {
@ -30,7 +67,18 @@ open class Card : RealmObject() {
SPADES(""), SPADES(""),
HEART(""), HEART(""),
DIAMOND(""), DIAMOND(""),
CLOVER("") CLOVER("");
val color: Int
get() {
return when (this) {
UNDEFINED -> R.color.white
SPADES -> R.color.white_dark
HEART -> R.color.red
DIAMOND -> R.color.diamond
CLOVER -> R.color.clover
}
}
} }
/*** /***
@ -39,14 +87,37 @@ open class Card : RealmObject() {
*/ */
var value: Int = 0 var value: Int = 0
val formattedValue: String
get() { return Value.format(this.value) }
/*** /***
* The card suit: heart, spades... * The card suit: heart, spades...
*/ */
var suit: Int = 0 var suitIdentifier: Int = 0
var suit: Suit
get() {
return Suit.values()[this.suitIdentifier]
}
set(value) {
this.suitIdentifier = value.ordinal
}
/*** /***
* The card index in a list * The card index in a list
*/ */
var index: Int = 0 var index: Int = 0
fun formatted(context: Context) : SpannableString {
val spannable = SpannableString(this.formattedValue + this.suit.value)
spannable.setSpan(
ForegroundColorSpan(context.getColor(this.suit.color)),
0,
spannable.length,
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE
)
return spannable
}
} }

@ -94,6 +94,12 @@ open class HandHistory : RealmObject(), RowRepresentable, Identifiable, Filterab
fun configure(handSetup: HandSetup) { fun configure(handSetup: HandSetup) {
// this.board.addAll(listOf(Card.valueOf(2, Card.Suit.SPADES),
// Card.valueOf(14, Card.Suit.HEART),
// Card.valueOf(5, Card.Suit.CLOVER),
// Card.valueOf(10, Card.Suit.DIAMOND),
// Card.valueOf(12, Card.Suit.SPADES)))
handSetup.tableSize?.let { this.numberOfPlayers = it } handSetup.tableSize?.let { this.numberOfPlayers = it }
handSetup.smallBlind?.let { this.smallBlind = it } handSetup.smallBlind?.let { this.smallBlind = it }
handSetup.bigBlind?.let { this.bigBlind = it } handSetup.bigBlind?.let { this.bigBlind = it }

@ -9,21 +9,26 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button import android.widget.Button
import android.widget.EditText import android.widget.EditText
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.handhistory.ComputedAction import net.pokeranalytics.android.model.handhistory.ComputedAction
import net.pokeranalytics.android.model.handhistory.HHKeyboard import net.pokeranalytics.android.model.handhistory.HHKeyboard
import net.pokeranalytics.android.model.handhistory.Street
import net.pokeranalytics.android.model.realm.handhistory.formatted
import net.pokeranalytics.android.ui.adapter.BindableHolder import net.pokeranalytics.android.ui.adapter.BindableHolder
import net.pokeranalytics.android.ui.adapter.RecyclerAdapter import net.pokeranalytics.android.ui.adapter.RecyclerAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.extensions.hideKeyboard import net.pokeranalytics.android.ui.extensions.hideKeyboard
import net.pokeranalytics.android.ui.modules.handhistory.views.StreetCardView
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.holder.RowViewHolder import net.pokeranalytics.android.ui.view.holder.RowViewHolder
import net.pokeranalytics.android.ui.view.rowrepresentable.ViewIdentifier import net.pokeranalytics.android.ui.view.rowrepresentable.ViewIdentifier
import timber.log.Timber import timber.log.Timber
enum class HandRowType(var layoutRes: Int) : ViewIdentifier { enum class HandRowType(var layoutRes: Int) : ViewIdentifier {
HEADER(R.layout.row_header_title), HEADER(R.layout.row_header_title),
ACTION(R.layout.row_hand_action), ACTION(R.layout.row_hand_action),
@ -90,14 +95,35 @@ class HandHistoryAdapter(
// //
// } // }
abstract inner class RowHandHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var currentPosition = 0
fun color(isFocused: Boolean) : Int {
val color = if (isFocused) R.color.kaki else R.color.kaki_medium
return itemView.context.getColor(color)
}
fun editTextSelected(editText: EditText, selected: Boolean, tag: Int) {
editText.setBackgroundColor(color(selected))
val row = dataSource.rowRepresentableForPosition(currentPosition)
?: throw PAIllegalStateException("Row Representable not found at index: $currentPosition")
val alreadySelected = dataSource.isSelected(currentPosition, row, tag)
if (!alreadySelected && selected) {
delegate?.onRowSelected(currentPosition, row, tag)
}
}
}
/** /**
* Display a hand action * Display a hand action
*/ */
inner class RowHandAction(itemView: View) : RecyclerView.ViewHolder(itemView), inner class RowHandAction(itemView: View) : RowHandHolder(itemView),
BindableHolder { BindableHolder {
// private var listener = TextListener() // private var listener = TextListener()
private var currentPosition = 0
private var actionCanBeEdited = true private var actionCanBeEdited = true
private var amountCanBeEdited = true private var amountCanBeEdited = true
@ -129,7 +155,6 @@ class HandHistoryAdapter(
amountEditText.isFocusableInTouchMode = true amountEditText.isFocusableInTouchMode = true
amountEditText.requestFocus() amountEditText.requestFocus()
// requestFocusAndShowKeyboard(amountEditText)
editTextSelected(amountEditText, true, HHKeyboard.AMOUNT.ordinal) editTextSelected(amountEditText, true, HHKeyboard.AMOUNT.ordinal)
} }
@ -137,18 +162,6 @@ class HandHistoryAdapter(
return@setOnTouchListener true return@setOnTouchListener true
} }
// amountEditText.addTextChangedListener(this.listener)
}
}
private fun editTextSelected(editText: EditText, selected: Boolean, tag: Int) {
editText.setBackgroundColor(color(selected))
val row = dataSource.rowRepresentableForPosition(currentPosition)
?: throw PAIllegalStateException("Row Representable not found at index: $currentPosition")
val alreadySelected = dataSource.isSelected(currentPosition, row, tag)
if (!alreadySelected && selected) {
delegate?.onRowSelected(currentPosition, row, tag)
} }
} }
@ -164,11 +177,6 @@ class HandHistoryAdapter(
} }
} }
private fun color(isFocused: Boolean) : Int {
val color = if (isFocused) R.color.kaki else R.color.kaki_medium
return itemView.context.getColor(color)
}
override fun onBind(position: Int, row: RowRepresentable, adapter: RecyclerAdapter) { override fun onBind(position: Int, row: RowRepresentable, adapter: RecyclerAdapter) {
Timber.d("onbind @position = $position") Timber.d("onbind @position = $position")
@ -245,11 +253,73 @@ class HandHistoryAdapter(
/** /**
* Display a hand street * Display a hand street
*/ */
inner class RowHandStreet(itemView: View) : RecyclerView.ViewHolder(itemView), inner class RowHandStreet(itemView: View) : RowHandHolder(itemView),
BindableHolder { BindableHolder {
init {
// Flop
itemView.findViewById<EditText>(R.id.flopEditText)?.let { flopEditText ->
flopEditText.isFocusableInTouchMode = true
flopEditText.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_UP) {
// Both are required, otherwise requestFocus() fails
flopEditText.isFocusable = true
flopEditText.isFocusableInTouchMode = true
flopEditText.requestFocus()
editTextSelected(flopEditText, true, Street.FLOP.ordinal)
}
return@setOnTouchListener true
}
}
}
override fun onBind(position: Int, row: RowRepresentable, adapter: RecyclerAdapter) { override fun onBind(position: Int, row: RowRepresentable, adapter: RecyclerAdapter) {
val streetCardView = row as StreetCardView
val street = streetCardView.street
itemView.findViewById<EditText>(R.id.flopEditText)?.let { flopEditText ->
flopEditText.isFocusable = (street == Street.FLOP)
flopEditText.isVisible = true
val text = streetCardView.cards.take(3).formatted(itemView.context)
flopEditText.setText(text)
}
itemView.findViewById<EditText>(R.id.turnEditText)?.let { turnEditText ->
// turnEditText.isVisible = streetCardView.street.ordinal >= Street.TURN.ordinal
turnEditText.isFocusable = (street == Street.TURN)
if (streetCardView.cards.size >= 4) {
val text = streetCardView.cards[3].formatted(itemView.context)
turnEditText.setText(text)
} else {
turnEditText.text = null
}
} }
itemView.findViewById<EditText>(R.id.riverEditText)?.let { riverEditText ->
// riverEditText.isVisible = streetCardView.street.ordinal >= Street.RIVER.ordinal
riverEditText.isFocusable = (street == Street.RIVER)
if (streetCardView.cards.size >= 5) {
val text = streetCardView.cards[4].formatted(itemView.context)
riverEditText.setText(text)
} else {
riverEditText.text = null
}
}
}
} }
} }

@ -18,7 +18,7 @@ import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.RealmFragment import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.modules.handhistory.views.KeyboardListener import net.pokeranalytics.android.ui.modules.handhistory.views.KeyboardListener
import net.pokeranalytics.android.ui.modules.handhistory.views.StreetCardHeader import net.pokeranalytics.android.ui.modules.handhistory.views.StreetCardView
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
import net.pokeranalytics.android.util.extensions.findById import net.pokeranalytics.android.util.extensions.findById
@ -113,8 +113,13 @@ class HandHistoryFragment : RealmFragment(), RowRepresentableDataSource, RowRepr
Timber.d("Current selection is ${selection.index} / ${selection.keyboard}") Timber.d("Current selection is ${selection.index} / ${selection.keyboard}")
retrieveEditTextInputConnection(selection.index) retrieveEditTextInputConnection(selection.index)
when (it.keyboard) {
HHKeyboard.ACTION -> {
val positions = this.model.positionsForSelection() val positions = this.model.positionsForSelection()
this.keyboard.setPositions(positions) this.keyboard.setPositions(positions)
}
else -> {}
}
} ?: run { } ?: run {
this.keyboard.setEditText(null, null) this.keyboard.setEditText(null, null)
} }
@ -217,7 +222,7 @@ class HandHistoryFragment : RealmFragment(), RowRepresentableDataSource, RowRepr
else -> throw PAIllegalStateException("Unmanaged tag value: $tag") else -> throw PAIllegalStateException("Unmanaged tag value: $tag")
} }
} }
is StreetCardHeader -> { is StreetCardView -> {
HHKeyboard.CARD HHKeyboard.CARD
} }
else -> null else -> null

@ -6,7 +6,7 @@ import net.pokeranalytics.android.model.handhistory.Street
import net.pokeranalytics.android.model.realm.handhistory.Card import net.pokeranalytics.android.model.realm.handhistory.Card
import net.pokeranalytics.android.ui.modules.handhistory.HandRowType import net.pokeranalytics.android.ui.modules.handhistory.HandRowType
class StreetCardHeader(var street: Street, var cards: List<Card>, var potSize: Double) : HandHistoryRow { class StreetCardView(var street: Street, var cards: List<Card>, var potSize: Double) : HandHistoryRow {
override val viewType: Int = HandRowType.STREET.ordinal override val viewType: Int = HandRowType.STREET.ordinal

@ -6,32 +6,58 @@
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/flopEditText" android:id="@+id/flopEditText"
android:layout_width="180dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_weight="3"
android:layout_marginTop="16dp" android:layout_margin="4dp"/>
android:layout_marginBottom="16dp"
android:imeOptions="actionDone"
android:maxLines="1" />
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/turnEditText" android:id="@+id/turnEditText"
android:layout_width="60dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_weight="1"
android:layout_marginTop="16dp" android:layout_margin="4dp" />
android:layout_marginBottom="16dp"
android:imeOptions="actionDone"
android:maxLines="1" />
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/riverEditText" android:id="@+id/riverEditText"
android:layout_width="60dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_weight="1"
android:layout_marginTop="16dp" android:layout_margin="4dp" />
android:layout_marginBottom="16dp"
android:imeOptions="actionDone" <Space
android:maxLines="1" /> android:layout_width="0dp"
android:layout_height="44dp"
android:layout_weight="3" />
<!-- <androidx.appcompat.widget.AppCompatEditText-->
<!-- android:id="@+id/flopEditText"-->
<!-- android:layout_width="180dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginStart="16dp"-->
<!-- android:layout_marginTop="16dp"-->
<!-- android:layout_marginBottom="16dp"-->
<!-- android:imeOptions="actionDone"-->
<!-- android:maxLines="1" />-->
<!-- <androidx.appcompat.widget.AppCompatEditText-->
<!-- android:id="@+id/turnEditText"-->
<!-- android:layout_width="60dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginStart="16dp"-->
<!-- android:layout_marginTop="16dp"-->
<!-- android:layout_marginBottom="16dp"-->
<!-- android:imeOptions="actionDone"-->
<!-- android:maxLines="1" />-->
<!-- <androidx.appcompat.widget.AppCompatEditText-->
<!-- android:id="@+id/riverEditText"-->
<!-- android:layout_width="60dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginStart="16dp"-->
<!-- android:layout_marginTop="16dp"-->
<!-- android:layout_marginBottom="16dp"-->
<!-- android:imeOptions="actionDone"-->
<!-- android:maxLines="1" />-->
</LinearLayout> </LinearLayout>

@ -54,4 +54,7 @@
<color name="player_color_8">#ff573d</color> <color name="player_color_8">#ff573d</color>
<color name="player_color_9">#ff971e</color> <color name="player_color_9">#ff971e</color>
<color name="diamond">#ff971e</color>
<color name="clover">#71fb94</color>
</resources> </resources>

@ -354,6 +354,9 @@
<item name="backgroundTint">@color/kaki_medium</item> <item name="backgroundTint">@color/kaki_medium</item>
</style> </style>
<style name="PokerAnalyticsTheme.CardButton" parent="Widget.MaterialComponents.Button">
</style>
<style name="PokerAnalyticsTheme.SecondaryButton" parent="PokerAnalyticsTheme.Button"> <style name="PokerAnalyticsTheme.SecondaryButton" parent="PokerAnalyticsTheme.Button">
<item name="backgroundTint">@color/white</item> <item name="backgroundTint">@color/white</item>
</style> </style>

Loading…
Cancel
Save