Fixing bankroll report updates

csv
Laurent 7 years ago
parent 31c2038857
commit 4c79b32c1f
  1. 2
      app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt
  2. 1
      app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt
  3. 9
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollCalculator.kt
  4. 63
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReport.kt
  5. 114
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReportManager.kt
  6. 1
      app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt
  7. 41
      app/src/main/java/net/pokeranalytics/android/ui/activity/BankrollActivity.kt
  8. 2
      app/src/main/java/net/pokeranalytics/android/ui/activity/components/Codes.kt
  9. 131
      app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollDetailsFragment.kt
  10. 115
      app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollFragment.kt
  11. 13
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt
  12. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt
  13. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/DeletableItemFragment.kt
  14. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/DataManagerFragment.kt
  15. 8
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/TransactionDataFragment.kt
  16. 85
      app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
  17. 12
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/BankrollMainRow.kt
  18. 11
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/CustomizableRowRepresentable.kt
  19. 9
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/GraphRow.kt

@ -60,7 +60,7 @@ class PokerAnalyticsApplication : Application() {
if (BuildConfig.DEBUG) {
Timber.d("UserPreferences.defaultCurrency: ${UserDefaults.currency.symbol}")
// this.createFakeSessions()
this.createFakeSessions()
}
Patcher.patchAll(this.applicationContext)

@ -177,6 +177,7 @@ enum class Stat(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepres
private val threshold: Double
get() {
return when (this) {
RISK_OF_RUIN -> 5.0
WIN_RATIO -> 50.0
else -> 0.0
}

@ -5,7 +5,9 @@ import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.ComputableGroup
import net.pokeranalytics.android.calculus.ComputedResults
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.util.extensions.findById
class BankrollCalculator {
@ -18,7 +20,10 @@ class BankrollCalculator {
val report = BankrollReport(setup)
val bankrolls: List<Bankroll> =
if (setup.bankroll != null) listOf(setup.bankroll)
if (setup.bankrollId != null) {
val bankroll = realm.findById<Bankroll>(setup.bankrollId) ?: throw PAIllegalStateException("Bankroll not found with id=${setup.bankrollId}")
listOf(bankroll)
}
else realm.where(Bankroll::class.java).findAll()
var initialValue = 0.0
@ -41,7 +46,7 @@ class BankrollCalculator {
report.transactionsNet = transactionNet
report.initial = initialValue
val query = setup.query
val query = setup.query(realm)
val transactions = Filter.queryOn<Transaction>(realm, query)
report.addDatedItems(transactions)

@ -3,54 +3,22 @@ package net.pokeranalytics.android.calculus.bankroll
import android.content.Context
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineDataSet
import io.realm.Realm
import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.DatedValue
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.ui.graph.DataSetFactory
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.extensions.findById
import java.util.*
import kotlin.collections.HashMap
//object BankrollReportManager {
//
// var mainReport: BankrollReport? = null
// var reports: MutableMap<String, BankrollReport> = mutableMapOf()
//
// fun udpateBankrolls(bankrolls: List<Bankroll>) {
// this.invalidateMainReport()
// bankrolls.forEach {
// this.reports.remove(it.id)
// }
// }
//
// fun deleteBankrolls(bankrolls: List<Bankroll>) {
// this.invalidateMainReport()
// bankrolls.forEach {
// this.reports.remove(it.id)
// }
// }
//
// private fun invalidateMainReport() {
// this.mainReport = null
// }
//
// private fun launchReports(bankrolls: List<Bankroll>) {
//
// this.mainReport = BankrollCalculator.computeReport()
//
//
// }
//
//}
/**
* This class holds the results from the BankrollCalculator computations
* It has all the information required for the Bankroll various displays
*/
class BankrollReport(var setup: BankrollReportSetup) : RowRepresentable {
class BankrollReport(var setup: BankrollReportSetup) {
/**
* The value of the bankroll
@ -150,15 +118,6 @@ class BankrollReport(var setup: BankrollReportSetup) : RowRepresentable {
*/
private var evolutionItems: MutableList<DatedValue> = mutableListOf()
override val viewType: Int
get() {
return if (setup.bankroll == null) {
RowViewType.LEGEND_DEFAULT.ordinal
} else {
RowViewType.TITLE_VALUE_ARROW.ordinal
}
}
/**
* Adds a list of dated items to the evolution items used to get the bankroll graph
*/
@ -176,7 +135,7 @@ class BankrollReport(var setup: BankrollReportSetup) : RowRepresentable {
var bucket = this.transactionBuckets[type.id]
if (bucket == null) {
val b = TransactionBucket(this.setup.virtualBankroll)
val b = TransactionBucket(type.name, this.setup.virtualBankroll)
this.transactionBuckets[type.id] = b
bucket = b
}
@ -200,7 +159,7 @@ class BankrollReport(var setup: BankrollReportSetup) : RowRepresentable {
this.evolutionItems.sortBy { it.date }
var total = 0.0
var total = this.initial
this.evolutionItems.forEach {
total += it.amount
val point = BRGraphPoint(total, it.date, it)
@ -228,7 +187,7 @@ class BankrollReport(var setup: BankrollReportSetup) : RowRepresentable {
* A class describing the parameters required to launch a bankroll report
*
*/
class BankrollReportSetup(val bankroll: Bankroll? = null, val from: Date? = null, val to: Date? = null) {
class BankrollReportSetup(val bankrollId: String? = null, val from: Date? = null, val to: Date? = null) {
/**
* Returns whether the setup concerns the virtual bankroll,
@ -236,17 +195,17 @@ class BankrollReportSetup(val bankroll: Bankroll? = null, val from: Date? = null
*/
val virtualBankroll: Boolean
get() {
return this.bankroll == null
return this.bankrollId == null
}
/**
* the query used to get bankroll transactions
*/
val query: Query
get() {
fun query(realm: Realm): Query {
val query = Query()
this.bankroll?.let {
this.bankrollId?.let {
val bankroll = realm.findById<Bankroll>(it) ?: throw IllegalStateException("Bankroll not found with id $it")
val bankrollCondition = QueryCondition.AnyBankroll(bankroll)
query.add(bankrollCondition)
}
@ -276,7 +235,7 @@ class BankrollReportSetup(val bankroll: Bankroll? = null, val from: Date? = null
/**
* A TransactionBucket holds a list of _transactions and computes its amount sum
*/
class TransactionBucket(useRate: Boolean = false) {
class TransactionBucket(var name: String, useRate: Boolean = false) {
/**
* Whether the bankroll rate should be used

@ -0,0 +1,114 @@
package net.pokeranalytics.android.calculus.bankroll
import io.realm.Realm
import io.realm.RealmResults
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.model.realm.Transaction
import timber.log.Timber
import java.util.*
import kotlin.coroutines.CoroutineContext
object BankrollReportManager {
val coroutineContext: CoroutineContext
get() = Dispatchers.Main
private var reports: MutableMap<String?, BankrollReport> = mutableMapOf()
private var computableResults: RealmResults<ComputableResult>
private var bankrolls: RealmResults<Bankroll>
private var transactions: RealmResults<Transaction>
init {
val realm = Realm.getDefaultInstance()
computableResults = realm.where(ComputableResult::class.java).findAll()
bankrolls = realm.where(Bankroll::class.java).findAll()
transactions = realm.where(Transaction::class.java).findAll()
initializeListeners()
realm.close()
}
/**
* Listens to all objects that might have an impact on any bankroll report
*/
private fun initializeListeners() {
this.computableResults.addChangeListener { t, changeSet ->
val indexes = changeSet.changes.plus(changeSet.insertions).toList()
val bankrolls = indexes.mapNotNull { t[it]?.session?.bankroll }.toSet()
this.updateBankrolls(bankrolls)
}
this.bankrolls.addChangeListener { t, changeSet ->
val indexes = changeSet.changes.plus(changeSet.insertions).toList()
val bankrolls = indexes.mapNotNull { t[it] }.toSet()
this.updateBankrolls(bankrolls)
}
this.transactions.addChangeListener { t, changeSet ->
val indexes = changeSet.changes.plus(changeSet.insertions).toList()
val bankrolls = indexes.mapNotNull { t[it]?.bankroll }.toSet()
this.updateBankrolls(bankrolls)
}
}
fun reportForBankroll(bankrollId: String?, handler: (BankrollReport) -> Unit) {
// if the report exists, return it
val existingReport: BankrollReport? = this.reports[bankrollId]
if (existingReport != null) {
handler(existingReport)
return
}
// otherwise compute it
GlobalScope.launch(coroutineContext) {
var report: BankrollReport? = null
val scope = GlobalScope.async {
val s = Date()
Timber.d(">>>>> start computing bankroll...")
val realm = Realm.getDefaultInstance()
val setup = BankrollReportSetup(bankrollId)
report = BankrollCalculator.computeReport(realm, setup)
realm.close()
val e = Date()
val duration = (e.time - s.time) / 1000.0
Timber.d(">>>>> ended in $duration seconds")
}
scope.await()
report?.let { handler(it) }
}
}
/**
* Notifies the manager of cases not managed by RealmResults listener, such as deletions
*/
fun notifyBankrollReportImpact(bankrollId: String) {
this.reports.remove(bankrollId)
this.reports.remove(null)
}
private fun updateBankrolls(bankrolls: Set<Bankroll>) {
this.invalidateReport(bankrolls)
}
private fun invalidateReport(bankrolls: Set<Bankroll>) {
this.reports.remove(null)
bankrolls.forEach { br ->
this.reports.remove(br.id)
}
}
}

@ -10,6 +10,7 @@ class ConfigurationException(message: String) : Exception(message)
class EnumIdentifierNotFoundException(message: String) : Exception(message)
class MisconfiguredSavableEnumException(message: String) : Exception(message)
class PAIllegalStateException(message: String) : Exception(message)
sealed class PokerAnalyticsException(message: String) : Exception(message) {
object FilterElementUnknownName : PokerAnalyticsException(message = "No filterElement name was found to identify the queryCondition")

@ -37,47 +37,6 @@ class BankrollActivity : PokerAnalyticsActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_bankroll)
// this.computableResults = getRealm().where(ComputableResult::class.java).findAll() // ComputableResult are existing only if sessions are ended
// this.computableResults.addChangeListener { t, changeSet ->
//
// val bankrolls = mutableSetOf<Bankroll>()
// val indexes = mutableSetOf<Int>()
// indexes.addAll(changeSet.changes.toList())
// indexes.addAll(changeSet.insertions.toList())
// indexes.addAll(changeSet.deletions.toList())
// indexes.forEach { index ->
// t[index]?.session?.bankroll?.let { br ->
// bankrolls.add(br)
// }
// }
// this.computeBankrollReports(bankrolls)
// }
// this.bankrolls = getRealm().where(Bankroll::class.java).findAll() // ComputableResult are existing only if sessions are ended
// this.bankrolls.addChangeListener { _, changeSet ->
//
//
//
//
//
// }
// this.transactions = getRealm().where(Transaction::class.java).findAll() // ComputableResult are existing only if sessions are ended
// this.transactions.addChangeListener { t, changeSet ->
//
// val bankrolls = mutableSetOf<Bankroll>()
// val indexes = mutableSetOf<Int>()
// indexes.addAll(changeSet.changes.toList())
// indexes.addAll(changeSet.insertions.toList())
// indexes.addAll(changeSet.deletions.toList())
// indexes.forEach { index ->
// if (t.isNotEmpty()) {
// t[index]?.bankroll?.let { br ->
// bankrolls.add(br)
// }
// }
// }
// this.computeBankrollReports(bankrolls)
// }
}
fun computeBankrollReports(bankrolls: Collection<Bankroll>) {

@ -2,6 +2,8 @@ package net.pokeranalytics.android.ui.activity.components
enum class RequestCode(var value: Int) {
DEFAULT(1),
BANKROLL_DETAILS(700),
BANKROLL_CREATE(701),
NEW_SESSION(800),
NEW_TRANSACTION(801),
NEW_REPORT(802),

@ -10,18 +10,22 @@ import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.ComputedStat
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.bankroll.BankrollReport
import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.util.extensions.findById
class BankrollDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
class BankrollDetailsFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
companion object {
@ -32,27 +36,32 @@ class BankrollDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentable
*/
fun newInstance(bankrollReport: BankrollReport): BankrollDetailsFragment {
val fragment = BankrollDetailsFragment()
fragment.bankrollReport = bankrollReport
fragment.bankrollId = bankrollReport.setup.bankrollId
// fragment.bankrollReport = bankrollReport
return fragment
}
}
private var bankrollId: String? = null
private lateinit var bankroll: Bankroll
private var rows: ArrayList<RowRepresentable> = ArrayList()
private lateinit var bankrollAdapter: RowRepresentableAdapter
private lateinit var bankrollReport: BankrollReport
// private lateinit var bankrollReport: BankrollReport
private var bankrollDetailsMenu: Menu? = null
private var rows: ArrayList<RowRepresentable> = ArrayList()
// Life Cycle
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_bankroll_details, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initUI()
initData()
initUI()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@ -62,40 +71,35 @@ class BankrollDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentable
activity?.setResult(RESULT_OK, data)
activity?.finish()
} else {
updateMenuUI()
BankrollReportManager.reportForBankroll(this.bankrollId) {
updateUI(it)
}
}
}
}
override fun adapterRows(): List<RowRepresentable>? {
return rows
private fun updateUI(bankrollReport: BankrollReport) {
this.initRows(bankrollReport)
this.updateMenuUI(bankrollReport)
this.bankrollAdapter.notifyDataSetChanged()
}
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
/**
* Init data
*/
private fun initData() {
this.bankrollId?.let { id ->
this.bankroll = getRealm().findById(id) ?: throw PAIllegalStateException("Bankroll not found, id=$id")
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
menu?.clear()
inflater?.inflate(R.menu.toolbar_comparison_chart, menu)
this.bankrollDetailsMenu = menu
updateMenuUI()
super.onCreateOptionsMenu(menu, inflater)
BankrollReportManager.reportForBankroll(this.bankrollId) {
updateUI(it)
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item!!.itemId) {
R.id.settings -> editBankroll()
}
return true
}
// Business
/**
* Init data
*/
private fun initData() {
private fun initRows(bankrollReport: BankrollReport) {
rows.clear()
@ -105,23 +109,47 @@ class BankrollDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentable
val netComputedStat = ComputedStat(Stat.NET_RESULT, bankrollReport.netResult)
val netBankedComputedStat = ComputedStat(Stat.NET_RESULT, bankrollReport.netBanked)
rows.add(CustomizableRowRepresentable(RowViewType.TITLE_VALUE, resId = R.string.bankroll, computedStat = totalComputedStat))
rows.add(CustomizableRowRepresentable(RowViewType.TITLE_VALUE, resId = R.string.net_result, computedStat = netComputedStat))
rows.add(CustomizableRowRepresentable(RowViewType.TITLE_VALUE, resId = R.string.net_banked, computedStat = netBankedComputedStat))
rows.add(
CustomizableRowRepresentable(
RowViewType.TITLE_VALUE,
resId = R.string.bankroll,
computedStat = totalComputedStat
)
)
rows.add(
CustomizableRowRepresentable(
RowViewType.TITLE_VALUE,
resId = R.string.net_result,
computedStat = netComputedStat
)
)
rows.add(
CustomizableRowRepresentable(
RowViewType.TITLE_VALUE,
resId = R.string.net_banked,
computedStat = netBankedComputedStat
)
)
if (bankrollReport.transactionBuckets.isNotEmpty()) {
rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.operations))
bankrollReport.transactionBuckets.keys.forEach { key ->
bankrollReport.transactionBuckets[key]?.let { transactionBucket ->
val typeName = transactionBucket.transactions.firstOrNull()?.type?.getDisplayName(requireContext())
val typeName = transactionBucket.name
val computedStat = ComputedStat(Stat.NET_RESULT, transactionBucket.total)
rows.add(CustomizableRowRepresentable(RowViewType.TITLE_VALUE, title = typeName, computedStat = computedStat))
rows.add(
CustomizableRowRepresentable(
RowViewType.TITLE_VALUE,
title = typeName,
computedStat = computedStat
)
)
}
}
}
}
/**
* Init UI
*/
@ -129,8 +157,6 @@ class BankrollDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentable
setDisplayHomeAsUpEnabled(true)
updateMenuUI()
bankrollAdapter = RowRepresentableAdapter(this, this)
val viewManager = LinearLayoutManager(requireContext())
@ -145,23 +171,52 @@ class BankrollDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentable
/**
* Update menu UI
*/
private fun updateMenuUI() {
private fun updateMenuUI(bankrollReport: BankrollReport) {
if (bankrollReport.setup.virtualBankroll) {
setToolbarTitle(getString(R.string.total))
bankrollDetailsMenu?.findItem(R.id.settings)?.isVisible = false
} else {
setToolbarTitle(bankrollReport.setup.bankroll?.name)
setToolbarTitle(this.bankroll.name)
bankrollDetailsMenu?.findItem(R.id.settings)?.isVisible = true
}
}
// StaticRowRepresentableDataSource
override fun adapterRows(): List<RowRepresentable>? {
return rows
}
// RowRepresentableDelegate
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
menu?.clear()
inflater?.inflate(R.menu.toolbar_comparison_chart, menu)
this.bankrollDetailsMenu = menu
// updateMenuUI()
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item!!.itemId) {
R.id.settings -> editBankroll()
}
return true
}
// Business
/**
* Open Bankroll edit activity
*/
private fun editBankroll() {
EditableDataActivity.newInstanceForResult(this, LiveData.BANKROLL, bankrollReport.setup.bankroll?.id, REQUEST_CODE_EDIT)
EditableDataActivity.newInstanceForResult(this, LiveData.BANKROLL, this.bankrollId, REQUEST_CODE_EDIT)
}
}

@ -16,39 +16,34 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.ComputedStat
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.bankroll.BankrollCalculator
import net.pokeranalytics.android.calculus.bankroll.BankrollReport
import net.pokeranalytics.android.calculus.bankroll.BankrollReportSetup
import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.ui.activity.BankrollDetailsActivity
import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.activity.GraphActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.DeletableItemFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.GraphRow
import net.pokeranalytics.android.ui.view.rowrepresentable.*
import net.pokeranalytics.android.util.extensions.sorted
import timber.log.Timber
import java.util.*
import kotlin.collections.ArrayList
interface BankrollRowRepresentable : RowRepresentable {
var bankrollId: String?
}
class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
companion object {
const val REQUEST_CODE_DETAILS = 100
const val REQUEST_CODE_CREATE = 101
/**
* Create new instance
*/
@ -61,14 +56,14 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
}
private var rows: ArrayList<RowRepresentable> = ArrayList()
private var bankrollReportForRow: HashMap<RowRepresentable, BankrollReport> = HashMap()
private var bankrollRowRepresentables: HashMap<String?, List<BankrollRowRepresentable>> = HashMap()
private var lastItemClickedPosition: Int = 0
private var lastItemClickedId: String = ""
// private var lastItemClickedId: String = ""
private var deletedRow: RowRepresentable? = null
private lateinit var bankrolls: RealmResults<Bankroll>
override fun deletableItems() : List<Deletable> {
override fun deletableItems(): List<Deletable> {
return this.bankrolls
}
@ -85,21 +80,26 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
initData()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_DETAILS && resultCode == Activity.RESULT_OK) {
if (requestCode == RequestCode.BANKROLL_DETAILS.value && resultCode == Activity.RESULT_OK) {
val itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName)
itemToDeleteId?.let { id ->
GlobalScope.launch(Dispatchers.Main) {
delay(300)
deleteItem(dataListAdapter, bankrolls, id)
// update view
BankrollReportManager.notifyBankrollReportImpact(id)
dataListAdapter.notifyDataSetChanged()
}
}
} else if (requestCode == REQUEST_CODE_CREATE && resultCode == Activity.RESULT_OK) {
} else if (requestCode == RequestCode.BANKROLL_CREATE.value && resultCode == Activity.RESULT_OK) {
//TODO: Refresh bankrolls
initData()
} else {
dataListAdapter.notifyDataSetChanged()
}
}
@ -109,20 +109,19 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
}
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
lastItemClickedPosition = position
when (row) {
is BankrollRowRepresentable -> {
BankrollReportManager.reportForBankroll(row.bankrollId) { bankrollReport ->
// lastItemClickedId = row.bankrollId ?: ""
BankrollDetailsActivity.newInstanceForResult(this, bankrollReport, RequestCode.BANKROLL_DETAILS.value)
}
}
is GraphRow -> {
val lineDataSet = row.dataSet as LineDataSet
GraphActivity.newInstance(requireContext(), listOf(lineDataSet), title = getString(R.string.bankroll))
}
else -> {
if (bankrollReportForRow.containsKey(row)) {
bankrollReportForRow[row]?.let { bankrollReport ->
lastItemClickedPosition = position
lastItemClickedId = (row as? Identifiable)?.id ?: ""
BankrollDetailsActivity.newInstanceForResult(this, bankrollReport, REQUEST_CODE_DETAILS)
}
}
}
}
}
@ -137,48 +136,25 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
this.bankrolls = realm.sorted()
rows.clear()
bankrollReportForRow.clear()
GlobalScope.launch {
// Virtual bankroll
val graphRow = BankrollGraphRow()
rows.add(0, graphRow)
val mainRow = BankrollMainRow()
rows.add(mainRow)
launch(Dispatchers.Main) {
// TODO: Improve that
// We are in the main thread...
val startDate = Date()
// Graph
val globalBankrollReportSetup = BankrollReportSetup()
val globalBankrollReport = BankrollCalculator.computeReport(getRealm(), globalBankrollReportSetup)
rows.add(0, GraphRow(dataSet = globalBankrollReport.lineDataSet(requireContext())))
rows.add(globalBankrollReport)
bankrollReportForRow[globalBankrollReport] = globalBankrollReport
bankrollRowRepresentables[null] = listOf(graphRow, mainRow)
// Bankrolls
rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.bankrolls))
Timber.d("initData: ${System.currentTimeMillis() - startDate.time}ms")
// val bankrolls = LiveData.Bankroll.items(getRealm()) as RealmResults<Bankroll>
bankrolls.forEach { bankroll ->
val bankrollReportSetup = BankrollReportSetup(bankroll)
val bankrollReport = BankrollCalculator.computeReport(getRealm(), bankrollReportSetup)
val computedStat = ComputedStat(Stat.NET_RESULT, bankrollReport.total)
val row =
CustomizableRowRepresentable(RowViewType.TITLE_VALUE_ARROW, title = bankroll.name, computedStat = computedStat, isSelectable = true)
row.id = bankroll.id
val row = BankrollTotalRow(bankroll.id, bankroll.name)
rows.add(row)
bankrollReportForRow[row] = bankrollReport
bankrollRowRepresentables[bankroll.id] = listOf(row)
}
if (!isDetached) {
dataListAdapter.notifyDataSetChanged()
}
}
}
}
@ -200,13 +176,18 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
}
addButton.setOnClickListener {
EditableDataActivity.newInstanceForResult(this@BankrollFragment, dataType = LiveData.BANKROLL, primaryKey = null, requestCode = REQUEST_CODE_CREATE)
EditableDataActivity.newInstanceForResult(
this@BankrollFragment,
dataType = LiveData.BANKROLL,
primaryKey = null,
requestCode = RequestCode.BANKROLL_CREATE.value
)
}
}
override fun updateUIAfterDeletion(itemPosition: Int) {
lastItemClickedPosition = rows.indexOfFirst { if (it is Identifiable) it.id == lastItemClickedId else false }
deletedRow = rows.find { if (it is Identifiable) it.id == lastItemClickedId else false }
override fun updateUIAfterDeletion(itemId: String, itemPosition: Int) {
lastItemClickedPosition = rows.indexOfFirst { if (it is BankrollRowRepresentable) it.bankrollId == itemId else false }
deletedRow = rows.find { if (it is BankrollRowRepresentable) it.bankrollId == itemId else false }
rows.removeAt(lastItemClickedPosition)
dataListAdapter.notifyItemRemoved(lastItemClickedPosition)
}
@ -217,12 +198,14 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
// We are recreating a Bankroll report because the last one is invalid => the bankroll of the setup has been deleted
deletedRow?.let { row ->
val bankrollReportSetup = BankrollReportSetup(newItem as Bankroll)
val bankrollReport = BankrollCalculator.computeReport(getRealm(), bankrollReportSetup)
bankrollReportForRow[row] = bankrollReport
// val bankrollReportSetup = BankrollReportSetup(newItem as Bankroll)
// val bankrollReport = BankrollCalculator.computeReport(getRealm(), bankrollReportSetup)
// bankrollReportForRow[row] = bankrollReport
rows.add(lastItemClickedPosition, row)
dataListAdapter.notifyItemInserted(lastItemClickedPosition)
dataListAdapter.notifyDataSetChanged() // update both virtual + ex-deleted
// dataListAdapter.notifyItemInserted(lastItemClickedPosition)
}
}

@ -33,6 +33,7 @@ import net.pokeranalytics.android.ui.interfaces.FilterableType
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.billing.AppGuard
import java.text.SimpleDateFormat
import java.util.*
@ -274,12 +275,12 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
*/
private fun createNewSession(isTournament: Boolean) {
// val sessionCount = this.feedSessionAdapter.realmResults.size
// if (!AppGuard.isProUser && sessionCount >= AppGuard.MAX_SESSIONS_BEFORE_REQUESTING_SUBSCRIPTION) { // && !BuildConfig.DEBUG
// Toast.makeText(context, "Please subscribe!", Toast.LENGTH_LONG).show()
// BillingActivity.newInstanceForResult(requireContext())
// return
// }
val sessionCount = this.feedSessionAdapter.realmResults.size
if (!AppGuard.isProUser && sessionCount >= AppGuard.MAX_SESSIONS_BEFORE_REQUESTING_SUBSCRIPTION) { // && !BuildConfig.DEBUG
Toast.makeText(context, "Please subscribe!", Toast.LENGTH_LONG).show()
BillingActivity.newInstance(requireContext())
return
}
if (Date().after(betaLimitDate)) {
this.showEndOfBetaMessage()

@ -10,6 +10,7 @@ import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.DiffUtil
import kotlinx.android.synthetic.main.fragment_session.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.extensions.SessionState
import net.pokeranalytics.android.model.extensions.getState
@ -344,6 +345,9 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate {
* Delete a session
*/
private fun deleteSession() {
currentSession.bankroll?.id?.let { id ->
BankrollReportManager.notifyBankrollReportImpact(id)
}
currentSession.delete()
activity?.finish()
}

@ -92,7 +92,7 @@ abstract class DeletableItemFragment : RealmFragment() {
itemToDelete.deleteFromRealm()
}
itemHasBeenReInserted = false
updateUIAfterDeletion(itemPosition)
updateUIAfterDeletion(itemId, itemPosition)
showUndoSnackBar()
} else {
dataListAdapter.notifyItemChanged(itemPosition)
@ -133,7 +133,7 @@ abstract class DeletableItemFragment : RealmFragment() {
/**
* Called once the object has been deleted
*/
open fun updateUIAfterDeletion(itemPosition: Int) {
open fun updateUIAfterDeletion(itemId: String, itemPosition: Int) {
dataListAdapter.notifyItemRemoved(itemPosition)
}

@ -128,6 +128,8 @@ open class DataManagerFragment : RealmFragment() {
*/
protected open fun deleteData() {
this.willDeleteData()
val realm = this.getRealm()
if (this.item.isValidForDelete(realm)) {
@ -145,6 +147,8 @@ open class DataManagerFragment : RealmFragment() {
}
}
open fun willDeleteData() { }
/**
* Finish the activity with a result
*/

@ -6,6 +6,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.model.realm.TransactionType
@ -115,4 +116,11 @@ class TransactionDataFragment : EditableDataFragment(), StaticRowRepresentableDa
}
}
override fun willDeleteData() {
super.willDeleteData()
this.transaction?.bankroll?.id?.let { id ->
BankrollReportManager.notifyBankrollReportImpact(id)
}
}
}

@ -14,10 +14,7 @@ import androidx.core.widget.ContentLoadingProgressBar
import androidx.recyclerview.widget.RecyclerView
import com.github.mikephil.charting.charts.BarChart
import com.github.mikephil.charting.charts.LineChart
import com.github.mikephil.charting.data.BarData
import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet
import com.github.mikephil.charting.data.*
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import kotlinx.android.synthetic.main.row_feed_session.view.*
@ -25,7 +22,7 @@ import kotlinx.android.synthetic.main.row_transaction.view.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.ComputedStat
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.bankroll.BankrollReport
import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager
import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.Transaction
@ -34,6 +31,7 @@ import net.pokeranalytics.android.ui.extensions.ChipGroupExtension
import net.pokeranalytics.android.ui.extensions.addCircleRipple
import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.extensions.setTextFormat
import net.pokeranalytics.android.ui.fragment.BankrollRowRepresentable
import net.pokeranalytics.android.ui.graph.AxisFormatting
import net.pokeranalytics.android.ui.graph.setStyle
import net.pokeranalytics.android.ui.view.rowrepresentable.*
@ -139,7 +137,30 @@ enum class RowViewType(private var layoutRes: Int) {
inner class RowViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {
if (row is CustomizableRowRepresentable) {
when (row) {
is BankrollRowRepresentable -> {
// Title
itemView.findViewById<AppCompatTextView>(R.id.title)?.let {
it.text = row.localizedTitle(itemView.context)
}
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)
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
@ -168,7 +189,8 @@ enum class RowViewType(private var layoutRes: Int) {
}
}
} else {
}
else -> {
// Classic row
@ -227,6 +249,7 @@ enum class RowViewType(private var layoutRes: Int) {
itemView.findViewById<View?>(R.id.container)?.setOnClickListener(listener)
}
}
// Switch
itemView.findViewById<SwitchCompat?>(R.id.switchView)?.let {
@ -346,14 +369,9 @@ enum class RowViewType(private var layoutRes: Int) {
/**
* Display a graph
*/
inner class GraphViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
BindableHolder {
override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {
if (row is GraphRow) {
row.dataSet?.let { dataSet ->
inner class GraphViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
private fun loadWithDataSet(dataSet: DataSet<*>) {
val context = itemView.context
val chartView = when (dataSet) {
@ -380,9 +398,23 @@ enum class RowViewType(private var layoutRes: Int) {
chartView.setTouchEnabled(false)
}
// chartView.highlightValue((entries.size - 1).toFloat(), 0)
}
override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {
when (row) {
is BankrollGraphRow -> {
BankrollReportManager.reportForBankroll(row.bankrollId) { report ->
val dataSet = report.lineDataSet(itemView.context)
row.dataSet = dataSet
loadWithDataSet(dataSet)
}
}
is GraphRow -> {
row.dataSet?.let { dataSet ->
loadWithDataSet(dataSet)
}
}
}
// Listener
@ -400,22 +432,27 @@ enum class RowViewType(private var layoutRes: Int) {
BindableHolder {
override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {
if (row is BankrollReport) {
itemView.findViewById<AppCompatTextView>(R.id.stat1Name)?.let {
it.text = itemView.context.getString(R.string.total)
}
if (row is BankrollRowRepresentable) {
BankrollReportManager.reportForBankroll(row.bankrollId) { report ->
itemView.findViewById<AppCompatTextView>(R.id.stat1Value)?.let {
val formattedStat = ComputedStat(Stat.NET_RESULT, row.total).format()
val formattedStat = ComputedStat(Stat.NET_RESULT, report.total).format()
it.setTextFormat(formattedStat, itemView.context)
}
itemView.findViewById<AppCompatTextView>(R.id.stat2Name)?.let {
it.text = itemView.context.getString(R.string.risk_of_ruin)
}
itemView.findViewById<AppCompatTextView>(R.id.stat2Value)?.let {
val riskOfRuin = row.riskOfRuin ?: 0.0
val riskOfRuin = report.riskOfRuin ?: 0.0
val formattedStat = ComputedStat(Stat.RISK_OF_RUIN, riskOfRuin).format()
it.setTextFormat(formattedStat, itemView.context)
}
}
itemView.findViewById<AppCompatTextView>(R.id.stat1Name)?.let {
it.text = itemView.context.getString(R.string.total)
}
itemView.findViewById<AppCompatTextView>(R.id.stat2Name)?.let {
it.text = itemView.context.getString(R.string.risk_of_ruin)
}
val listener = View.OnClickListener {
adapter.delegate?.onRowSelected(position, row)
}

@ -0,0 +1,12 @@
package net.pokeranalytics.android.ui.view.rowrepresentable
import net.pokeranalytics.android.ui.fragment.BankrollRowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
class BankrollMainRow : BankrollRowRepresentable {
override var bankrollId: String? = null
override val viewType: Int = RowViewType.LEGEND_DEFAULT.ordinal
}

@ -3,9 +3,20 @@ package net.pokeranalytics.android.ui.view.rowrepresentable
import android.content.Context
import net.pokeranalytics.android.calculus.ComputedStat
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.ui.fragment.BankrollRowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
class BankrollTotalRow(override var bankrollId: String?, var name: String) : BankrollRowRepresentable {
override val viewType: Int = RowViewType.TITLE_VALUE_ARROW.ordinal
override fun localizedTitle(context: Context): String {
return name
}
}
/**
* A class to display a titleResId (and a value) as a Row Representable object
*/

@ -3,11 +3,18 @@ package net.pokeranalytics.android.ui.view.rowrepresentable
import com.github.mikephil.charting.data.DataSet
import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.ui.fragment.BankrollRowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
class GraphRow(var dataSet: DataSet<*>?, var title: String? = null, var report: Report? = null, var stat: Stat? = null) : RowRepresentable {
class BankrollGraphRow : GraphRow(null, null, null, null), BankrollRowRepresentable {
override var bankrollId: String? = null
}
open class GraphRow(var dataSet: DataSet<*>? = null, var title: String? = null, var report: Report? = null, var stat: Stat? = null) : RowRepresentable {
override val viewType: Int
get() = RowViewType.GRAPH.ordinal

Loading…
Cancel
Save