parent
1c9932cd32
commit
b1fa2d9020
@ -0,0 +1,212 @@ |
||||
package net.pokeranalytics.android.ui.adapter |
||||
|
||||
import android.text.Editable |
||||
import android.text.TextWatcher |
||||
import android.view.LayoutInflater |
||||
import android.view.View |
||||
import android.view.ViewGroup |
||||
import android.widget.Button |
||||
import android.widget.EditText |
||||
import androidx.recyclerview.widget.DiffUtil |
||||
import androidx.recyclerview.widget.RecyclerView |
||||
import net.pokeranalytics.android.R |
||||
import net.pokeranalytics.android.exceptions.PAIllegalStateException |
||||
import net.pokeranalytics.android.model.handhistory.ComputedAction |
||||
import net.pokeranalytics.android.model.handhistory.HHKeyboard |
||||
import net.pokeranalytics.android.ui.view.RowRepresentable |
||||
import net.pokeranalytics.android.ui.view.holder.RowViewHolder |
||||
import net.pokeranalytics.android.ui.view.rowrepresentable.ViewIdentifier |
||||
import net.pokeranalytics.android.util.extensions.formatted |
||||
import timber.log.Timber |
||||
|
||||
enum class HandRowType(var layoutRes: Int) : ViewIdentifier { |
||||
HEADER(R.layout.row_header_title), |
||||
ACTION(R.layout.row_hand_action), |
||||
STREET(R.layout.row_hand_cards); |
||||
|
||||
override val identifier: Int |
||||
get() { return this.ordinal } |
||||
} |
||||
|
||||
class HandHistoryAdapter( |
||||
override var dataSource: RowRepresentableDataSource, |
||||
override var delegate: RowRepresentableDelegate? = null) : |
||||
RecyclerView.Adapter<RecyclerView.ViewHolder>(), RecyclerAdapter { |
||||
|
||||
override fun getItemViewType(position: Int): Int { |
||||
return this.dataSource.viewTypeForPosition(position) |
||||
} |
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { |
||||
val rowType: HandRowType = HandRowType.values()[viewType] |
||||
val layout = LayoutInflater.from(parent.context).inflate(rowType.layoutRes, parent, false) |
||||
return when (rowType) { |
||||
HandRowType.HEADER -> RowViewHolder(layout) |
||||
HandRowType.ACTION -> RowHandAction(layout) |
||||
HandRowType.STREET -> RowHandStreet(layout) |
||||
} |
||||
} |
||||
|
||||
override fun getItemCount(): Int { |
||||
return this.dataSource.numberOfRows() |
||||
} |
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { |
||||
this.dataSource.rowRepresentableForPosition(position)?.let { rowRepresentable -> |
||||
(holder as BindableHolder).onBind(position, rowRepresentable, this) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Refresh the row in the adapter |
||||
*/ |
||||
fun refreshRow(row: RowRepresentable) { |
||||
val rows = this.dataSource.adapterRows() |
||||
val index = rows?.indexOf(row) ?: -1 |
||||
if (index >= 0) { |
||||
notifyItemChanged(index) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Update UI |
||||
*/ |
||||
fun updateRows(diffResult: DiffUtil.DiffResult) { |
||||
diffResult.dispatchUpdatesTo(this) |
||||
} |
||||
|
||||
inner class TextListener : TextWatcher { |
||||
|
||||
var position: Int = 0 |
||||
|
||||
override fun afterTextChanged(s: Editable?) {} |
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} |
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { |
||||
val row = dataSource.rowRepresentableForPosition(position) |
||||
?: throw PAIllegalStateException("Row Representable not found at index: $position") |
||||
delegate?.onRowValueChanged(s.toString(), row) |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Display a hand action |
||||
*/ |
||||
inner class RowHandAction(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { |
||||
|
||||
private var listener = TextListener() |
||||
|
||||
init { |
||||
itemView.findViewById<EditText>(R.id.actionEditText)?.let { actionEditText -> |
||||
actionEditText.setOnFocusChangeListener { v, hasFocus -> |
||||
Timber.d("focus change = $hasFocus at position = $position") |
||||
actionEditText.setBackgroundColor(color(hasFocus)) |
||||
} |
||||
} |
||||
|
||||
itemView.findViewById<EditText>(R.id.amountEditText)?.let { amountEditText -> |
||||
|
||||
amountEditText.setOnFocusChangeListener { v, hasFocus -> |
||||
Timber.d("focus change = $hasFocus at position = $position") |
||||
amountEditText.setBackgroundColor(color(hasFocus)) |
||||
} |
||||
|
||||
amountEditText.addTextChangedListener(this.listener) |
||||
|
||||
} |
||||
} |
||||
|
||||
private fun color(isFocused: Boolean) : Int { |
||||
val color = if (isFocused) R.color.green_light else R.color.kaki_medium |
||||
return itemView.context.getColor(color) |
||||
} |
||||
|
||||
override fun onBind(position: Int, row: RowRepresentable, adapter: RecyclerAdapter) { |
||||
this.listener.position = position |
||||
|
||||
val computedAction = row as ComputedAction |
||||
|
||||
// Position |
||||
itemView.findViewById<Button>(R.id.positionButton)?.let { button -> |
||||
button.text = computedAction.position.value |
||||
} |
||||
|
||||
// Action |
||||
itemView.findViewById<EditText>(R.id.actionEditText)?.let { actionEditText -> |
||||
val tag = HHKeyboard.ACTION.ordinal |
||||
|
||||
actionEditText.isFocusable = computedAction.actionTypeCanBeEdited |
||||
val selected = adapter.dataSource.isSelected(position, row, tag) |
||||
// Timber.d("Action at $position is selected: $selected") |
||||
actionEditText.setBackgroundColor(color(selected)) |
||||
|
||||
computedAction.action.type?.resId?.let { |
||||
actionEditText.setText(it) |
||||
} ?: run { |
||||
actionEditText.text = null |
||||
} |
||||
|
||||
if (selected) actionEditText.requestFocus() else actionEditText.clearFocus() |
||||
|
||||
// actionEditText.setOnClickListener { |
||||
// |
||||
// Timber.d("OnClickListener at $position") |
||||
// val isSelected = adapter.dataSource.isSelected(position, row, tag) |
||||
// if (!isSelected && computedAction.actionTypeCanBeEdited) { |
||||
// actionEditText.requestFocus() |
||||
// adapter.delegate?.onRowSelected(position, row, tag) |
||||
// actionEditText.setBackgroundColor(color(true)) |
||||
// } else { |
||||
// adapter.delegate?.onRowDeselected(position, row) |
||||
// actionEditText.setBackgroundColor(color(false)) |
||||
// } |
||||
// } |
||||
// actionEditText.setOnFocusChangeListener { v, hasFocus -> |
||||
// Timber.d("focus change = $hasFocus at position = $position") |
||||
// actionEditText.setBackgroundColor(color(hasFocus)) |
||||
// } |
||||
|
||||
} |
||||
|
||||
// Amount |
||||
itemView.findViewById<EditText>(R.id.amountEditText)?.let { amountEditText -> |
||||
val tag = HHKeyboard.AMOUNT.ordinal |
||||
|
||||
val selected = adapter.dataSource.isSelected(position, row, tag) |
||||
Timber.d("Amount at $position is selected: $selected") |
||||
amountEditText.setBackgroundColor(color(selected)) |
||||
|
||||
amountEditText.setText(computedAction.action.amount?.formatted()) |
||||
|
||||
if (selected) amountEditText.requestFocus() else amountEditText.clearFocus() |
||||
|
||||
// amountEditText.setOnTouchListener { v, event -> |
||||
// Timber.d("OnClickListener at $position") |
||||
// adapter.delegate?.onRowSelected(position, row, tag) |
||||
// amountEditText.setBackgroundColor(color(true)) |
||||
// return@setOnTouchListener true |
||||
// } |
||||
// amountEditText.setOnFocusChangeListener { v, hasFocus -> |
||||
// Timber.d("focus change = $hasFocus at position = $position") |
||||
// amountEditText.setBackgroundColor(color(hasFocus)) |
||||
// } |
||||
// amountEditText.addTextChangedListener { |
||||
// adapter.delegate?.onRowValueChanged(it.toString(), row) |
||||
// } |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Display a hand street |
||||
*/ |
||||
inner class RowHandStreet(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { |
||||
override fun onBind(position: Int, row: RowRepresentable, adapter: RecyclerAdapter) { |
||||
|
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,26 @@ |
||||
package net.pokeranalytics.android.ui.adapter |
||||
|
||||
import android.view.View |
||||
import net.pokeranalytics.android.ui.view.RowRepresentable |
||||
|
||||
interface RecyclerAdapter { |
||||
var dataSource: RowRepresentableDataSource |
||||
var delegate: RowRepresentableDelegate? |
||||
} |
||||
|
||||
interface RowRepresentableDelegate { |
||||
fun onRowSelected(position: Int, row: RowRepresentable, tag: Int = 0) {} |
||||
fun onRowDeselected(position: Int, row: RowRepresentable) {} |
||||
fun onRowValueChanged(value: Any?, row: RowRepresentable) {} |
||||
fun onRowDeleted(row: RowRepresentable) {} |
||||
fun onRowLongClick(itemView: View, row: RowRepresentable, position: Int) {} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* An interface used to factor the configuration of RecyclerView.ViewHolder |
||||
*/ |
||||
interface BindableHolder { |
||||
fun onBind(position: Int, row: RowRepresentable, adapter: RecyclerAdapter) { |
||||
} |
||||
} |
||||
@ -0,0 +1,170 @@ |
||||
package net.pokeranalytics.android.ui.view.holder |
||||
|
||||
import android.view.View |
||||
import androidx.appcompat.widget.AppCompatImageView |
||||
import androidx.appcompat.widget.AppCompatTextView |
||||
import androidx.appcompat.widget.SwitchCompat |
||||
import androidx.core.content.ContextCompat |
||||
import androidx.recyclerview.widget.RecyclerView |
||||
import net.pokeranalytics.android.R |
||||
import net.pokeranalytics.android.calculus.ComputedStat |
||||
import net.pokeranalytics.android.calculus.Stat |
||||
import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager |
||||
import net.pokeranalytics.android.ui.adapter.BindableHolder |
||||
import net.pokeranalytics.android.ui.adapter.RecyclerAdapter |
||||
import net.pokeranalytics.android.ui.extensions.addCircleRipple |
||||
import net.pokeranalytics.android.ui.extensions.setTextFormat |
||||
import net.pokeranalytics.android.ui.fragment.BankrollRowRepresentable |
||||
import net.pokeranalytics.android.ui.view.RowRepresentable |
||||
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable |
||||
import timber.log.Timber |
||||
|
||||
|
||||
/** |
||||
* Display a generic row (titleResId, value, container) |
||||
*/ |
||||
@SuppressWarnings("ResourceType") |
||||
class RowViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { |
||||
|
||||
override fun onBind(position: Int, row: RowRepresentable, adapter: RecyclerAdapter) { |
||||
|
||||
when (row) { |
||||
|
||||
is BankrollRowRepresentable -> { |
||||
|
||||
// Title |
||||
itemView.findViewById<AppCompatTextView>(R.id.title)?.let { |
||||
it.text = row.localizedTitle(itemView.context) |
||||
} |
||||
|
||||
Timber.d("RowViewHolder > bind > reportForBankroll") |
||||
BankrollReportManager.reportForBankroll(row.bankrollId) { report -> |
||||
|
||||
itemView.findViewById<AppCompatTextView>(R.id.title)?.let { |
||||
it.text = row.localizedTitle(itemView.context) |
||||
} |
||||
val computedStat = |
||||
ComputedStat(Stat.NET_RESULT, report.total, currency = report.currency) |
||||
itemView.findViewById<AppCompatTextView?>(R.id.value) |
||||
?.setTextFormat(computedStat.format(), itemView.context) |
||||
} |
||||
|
||||
val listener = View.OnClickListener { |
||||
adapter.delegate?.onRowSelected(position, row) |
||||
} |
||||
itemView.findViewById<View?>(R.id.container)?.setOnClickListener(listener) |
||||
} |
||||
is CustomizableRowRepresentable -> { |
||||
|
||||
// Customizable Row |
||||
|
||||
// Title |
||||
itemView.findViewById<AppCompatTextView>(R.id.title)?.let { |
||||
it.text = row.localizedTitle(itemView.context) |
||||
} |
||||
|
||||
// Value |
||||
itemView.findViewById<AppCompatTextView?>(R.id.value)?.let { |
||||
if (row.computedStat != null) { |
||||
val format = row.computedStat!!.format() |
||||
it.setTextFormat(format, itemView.context) |
||||
} else if (row.value != null) { |
||||
it.text = row.value |
||||
} |
||||
} |
||||
|
||||
// Listener |
||||
row.isSelectable?.let { isSelectable -> |
||||
if (isSelectable) { |
||||
val listener = View.OnClickListener { |
||||
adapter.delegate?.onRowSelected(position, row) |
||||
} |
||||
itemView.findViewById<View?>(R.id.container) |
||||
?.setOnClickListener(listener) |
||||
} |
||||
} |
||||
|
||||
} |
||||
else -> { |
||||
|
||||
// Classic row |
||||
|
||||
// Title |
||||
itemView.findViewById<AppCompatTextView?>(R.id.title)?.let { |
||||
if (row.resId != null) { |
||||
it.text = row.localizedTitle(itemView.context) |
||||
} else { |
||||
it.text = row.getDisplayName(itemView.context) |
||||
} |
||||
} |
||||
|
||||
// Value |
||||
itemView.findViewById<AppCompatTextView?>(R.id.value)?.let { |
||||
it.text = adapter.dataSource.stringForRow(row, itemView.context) |
||||
} |
||||
|
||||
// Icon |
||||
itemView.findViewById<AppCompatImageView?>(R.id.icon)?.let { imageView -> |
||||
imageView.setImageDrawable(null) |
||||
row.imageRes?.let { imageRes -> |
||||
imageView.setImageResource(imageRes) |
||||
} |
||||
} |
||||
|
||||
// Action |
||||
itemView.findViewById<AppCompatImageView>(R.id.action)?.let { imageView -> |
||||
imageView.setImageDrawable(null) |
||||
row.imageRes?.let { imageRes -> |
||||
imageView.visibility = View.VISIBLE |
||||
imageView.setImageResource(imageRes) |
||||
} |
||||
row.imageTint?.let { color -> |
||||
imageView.setColorFilter( |
||||
ContextCompat.getColor( |
||||
imageView.context, |
||||
color |
||||
) |
||||
) |
||||
} |
||||
if (row.imageClickable == true) { |
||||
imageView.addCircleRipple() |
||||
imageView.setOnClickListener { |
||||
adapter.delegate?.onRowSelected(position, row, 1) |
||||
} |
||||
} else { |
||||
imageView.setBackgroundResource(0) |
||||
} |
||||
} |
||||
|
||||
// Listener |
||||
val listener = View.OnClickListener { |
||||
itemView.findViewById<SwitchCompat?>(R.id.switchView)?.let { |
||||
if (adapter.dataSource.isEnabled(row, 0)) { |
||||
it.isChecked = !it.isChecked |
||||
} |
||||
} ?: run { |
||||
adapter.delegate?.onRowSelected(position, row) |
||||
} |
||||
} |
||||
|
||||
itemView.findViewById<View?>(R.id.container)?.setOnClickListener(listener) |
||||
} |
||||
} |
||||
|
||||
// Switch |
||||
itemView.findViewById<SwitchCompat?>(R.id.switchView)?.let { |
||||
it.isChecked = adapter.dataSource.boolForRow(row) |
||||
it.isEnabled = adapter.dataSource.isEnabled(row, 0) |
||||
it.setOnCheckedChangeListener { _, isChecked -> |
||||
adapter.delegate?.onRowValueChanged(isChecked, row) |
||||
} |
||||
} |
||||
|
||||
// Selected row |
||||
itemView.findViewById<AppCompatImageView?>(R.id.check)?.let { |
||||
it.isSelected = adapter.dataSource.isSelected(position, row, 0) |
||||
} |
||||
|
||||
|
||||
} |
||||
} |
||||
Loading…
Reference in new issue