first commit

powerreport
Laurent 3 years ago
parent baf755c4c8
commit 925ab12faf
  1. 14
      app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt
  2. 17
      app/src/main/java/net/pokeranalytics/android/calculus/Report.kt
  3. 217
      app/src/main/java/net/pokeranalytics/android/calculus/ReportWhistleBlower.kt
  4. 5
      app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt
  5. 2
      app/src/main/java/net/pokeranalytics/android/calculus/calcul/AggregationTypeExtensions.kt
  6. 2
      app/src/main/java/net/pokeranalytics/android/calculus/calcul/ComputedResultsExtensions.kt
  7. 2
      app/src/main/java/net/pokeranalytics/android/calculus/calcul/ReportDisplay.kt
  8. 2
      app/src/main/java/net/pokeranalytics/android/calculus/calcul/ReportExtensions.kt
  9. 2
      app/src/main/java/net/pokeranalytics/android/calculus/calcul/StatRepresentable.kt
  10. 7
      app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt
  11. 14
      app/src/main/java/net/pokeranalytics/android/model/Criteria.kt
  12. 21
      app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt
  13. 7
      app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt
  14. 50
      app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt
  15. 67
      app/src/main/java/net/pokeranalytics/android/model/realm/Performance.kt
  16. 2
      app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt
  17. 16
      app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt
  18. 2
      app/src/main/java/net/pokeranalytics/android/ui/activity/ProgressReportActivity.kt
  19. 2
      app/src/main/java/net/pokeranalytics/android/ui/activity/ReportCreationActivity.kt
  20. 7
      app/src/main/java/net/pokeranalytics/android/ui/activity/components/BaseActivity.kt
  21. 2
      app/src/main/java/net/pokeranalytics/android/ui/activity/components/ReportActivity.kt
  22. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportCreationFragment.kt
  23. 93
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt
  24. 9
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/BaseFragment.kt
  25. 3
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComposableTableReportFragment.kt
  26. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ProgressReportFragment.kt
  27. 2
      app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt
  28. 34
      app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
  29. 63
      app/src/main/java/net/pokeranalytics/android/ui/view/rows/ReportRow.kt
  30. 117
      app/src/main/java/net/pokeranalytics/android/ui/view/rows/StaticReport.kt
  31. 8
      app/src/main/java/net/pokeranalytics/android/ui/viewmodel/ReportViewModel.kt
  32. 19
      app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt
  33. 11
      app/src/main/res/drawable/circle_red.xml
  34. 1
      app/src/main/res/layout/fragment_reports.xml
  35. 70
      app/src/main/res/layout/row_title_badge_value.xml
  36. 1
      app/src/main/res/values/strings.xml

@ -9,8 +9,10 @@ import io.realm.RealmConfiguration
import io.realm.kotlin.where
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.calculus.ReportWhistleBlower
import net.pokeranalytics.android.model.migrations.Patcher
import net.pokeranalytics.android.model.migrations.PokerAnalyticsMigration
import net.pokeranalytics.android.model.realm.Performance
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.utils.Seed
import net.pokeranalytics.android.util.CrashLogging
@ -24,6 +26,8 @@ import java.util.*
class PokerAnalyticsApplication : Application() {
var reportWhistleBlower: ReportWhistleBlower? = null
companion object {
fun timeSinceInstall(context: Context): Long {
@ -72,11 +76,21 @@ class PokerAnalyticsApplication : Application() {
// this.createFakeSessions()
}
// Patch
Patcher.patchAll(this.applicationContext)
// Report
this.reportWhistleBlower = ReportWhistleBlower(this.applicationContext)
// Infos
val locale = Locale.getDefault()
CrashLogging.log("Country: ${locale.country}, language: ${locale.language}")
// @TODO remove
Realm.getDefaultInstance().executeTransaction {
it.delete(Performance::class.java)
}
}
/**

@ -31,6 +31,23 @@ class Report(var options: Calculator.Options) {
this._results.add(result)
}
fun max(stat: Stat): ComputedResults? {
var computedResults: ComputedResults? = null
var count = 0
var max = Double.MIN_VALUE
for (cr in this._results) {
cr.computedStat(stat)?.value?.let { value ->
count += 1
if (value > max) {
computedResults = cr
max = value
}
}
}
return if (count >= 2) { computedResults } else { null }
}
}

@ -0,0 +1,217 @@
package net.pokeranalytics.android.calculus
import android.content.Context
import android.os.CountDownTimer
import io.realm.Realm
import io.realm.RealmResults
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.model.realm.Performance
import net.pokeranalytics.android.model.realm.Result
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.view.rows.StaticReport
import timber.log.Timber
interface NewPerformanceListener {
fun newBestPerformanceHandler()
}
class ReportWhistleBlower(var context: Context) {
var sessions: RealmResults<Session>? = null
var results: RealmResults<Result>? = null
var timer: CountDownTimer? = null
// private lateinit var realm: Realm
private val currentNotifications: MutableList<String> = mutableListOf()
private val listeners: MutableList<NewPerformanceListener> = mutableListOf()
// companion object {
//
// @Volatile private var INSTANCE: ReportWhistleBlower? = null
//
// fun getInstance(context: Context, realm: Realm): ReportWhistleBlower =
// INSTANCE ?: synchronized(this) {
// INSTANCE ?: newInstance(context, realm).also { INSTANCE = it }
// }
//
// private fun newInstance(context: Context, realm: Realm): ReportWhistleBlower {
// return ReportWhistleBlower(context, realm)
// }
//
// }
init {
val realm = Realm.getDefaultInstance()
this.sessions = realm.where(Session::class.java).findAll()
this.sessions?.addChangeListener { _ ->
launchReports()
}
this.results = realm.where(Result::class.java).findAll()
this.results?.addChangeListener { _ ->
launchReports()
}
}
fun addListener(newPerformanceListener: NewPerformanceListener) {
this.listeners.add(newPerformanceListener)
}
private fun requestReportLaunch() {
synchronized(this) {
this.timer?.cancel()
this.timer = object : CountDownTimer(500L, 0L) {
override fun onTick(p0: Long) { }
override fun onFinish() {
launchReports()
timer = null
}
}
this.timer?.start()
}
}
private fun launchReports() {
// Basic
for (basicReport in StaticReport.basicReports) {
launchReport(basicReport)
}
val realm = Realm.getDefaultInstance()
// CustomField
val customFields = realm.where(CustomField::class.java)
.equalTo("type", CustomField.Type.LIST.uniqueIdentifier).findAll()
for (customField in customFields) {
launchReport(StaticReport.CustomFieldList(customField))
}
}
private fun launchReport(report: StaticReport) {
Timber.d(">>> launch report = $report")
val options = Calculator.Options(
stats = report.stats,
criterias = report.criteria
)
this.launchReportWithOptions(report, options)
}
private fun launchReportWithOptions(staticReport: StaticReport, options: Calculator.Options) {
GlobalScope.launch {
val realm = Realm.getDefaultInstance()
// realm.refresh()
val result = Calculator.computeStats(realm, options = options)
analyseReport(realm, staticReport, result)
realm.close()
}
}
private fun analyseReport(realm: Realm, staticReport: StaticReport, result: Report) {
when (staticReport.uniqueIdentifier) {
StaticReport.OptimalDuration.uniqueIdentifier -> analyseOptimalDuration(staticReport, result)
else -> analyseDefaultReport(realm, staticReport, result)
}
}
private fun analyseDefaultReport(realm: Realm, staticReport: StaticReport, result: Report) {
for (stat in result.options.stats) {
Timber.d("analyse stat: $stat for report: $staticReport")
result.max(stat)?.let { computedResults ->
val customField: CustomField? =
(staticReport as? StaticReport.CustomFieldList)?.customField
var query = realm.where(Performance::class.java)
.equalTo("statId", stat.uniqueIdentifier)
.equalTo("reportId", staticReport.uniqueIdentifier)
customField?.let {
query = query.equalTo("customFieldId", it.id)
}
val currentPerf = query.findFirst()
var storePerf = true
currentPerf?.let {
Timber.d("cr name = ${computedResults.group.query.getName(this.context)}")
currentPerf.name?.let {
if (computedResults.group.query.defaultName == it) {
storePerf = false
}
}
Timber.d("cr objectId = ${computedResults.group.query.objectId}")
currentPerf.objectId?.let {
if (computedResults.group.query.objectId == it) {
storePerf = false
}
}
if (storePerf) {
realm.executeTransaction {
currentPerf.name = computedResults.group.query.getName(this.context)
currentPerf.objectId = computedResults.group.query.objectId
currentPerf.customFieldId = customField?.id
}
this.notify(currentPerf)
}
}
if (currentPerf == null && storePerf) {
val performance = Performance(
staticReport,
stat,
computedResults.group.query.getName(this.context),
computedResults.group.query.objectId,
customField?.id,
null
)
realm.executeTransaction { it.copyToRealm(performance) }
this.notify(performance)
}
}
}
}
private fun analyseOptimalDuration(staticReport: StaticReport, result: Report) {
}
private fun notify(performance: Performance) {
this.currentNotifications.add(performance.id)
for (listener in this.listeners) {
listener.newBestPerformanceHandler()
}
}
fun has(performanceId: String): Boolean {
return this.currentNotifications.contains(performanceId)
}
}

@ -4,6 +4,7 @@ import android.content.Context
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.FormattingException
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.ui.fragment.PerformanceKey
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.NULL_TEXT
@ -22,7 +23,7 @@ class StatFormattingException(message: String) : Exception(message)
/**
* An enum representing all the types of Session statistics
*/
enum class Stat(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepresentable {
enum class Stat(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepresentable, PerformanceKey {
NET_RESULT(1),
BB_NET_RESULT(2),
@ -102,6 +103,8 @@ enum class Stat(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepres
}
override val value: String = this.uniqueIdentifier.toString()
override val resId: Int?
get() {
return when (this) {

@ -1,4 +1,4 @@
package net.pokeranalytics.android.calcul
package net.pokeranalytics.android.calculus.calcul
import net.pokeranalytics.android.calculus.AggregationType
import net.pokeranalytics.android.ui.graph.Graph

@ -1,4 +1,4 @@
package net.pokeranalytics.android.calcul
package net.pokeranalytics.android.calculus.calcul
import android.content.Context
import com.github.mikephil.charting.data.*

@ -1,4 +1,4 @@
package net.pokeranalytics.android.calcul
package net.pokeranalytics.android.calculus.calcul
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Calculator

@ -1,4 +1,4 @@
package net.pokeranalytics.android.calcul
package net.pokeranalytics.android.calculus.calcul
import android.content.Context
import com.github.mikephil.charting.data.BarDataSet

@ -1,4 +1,4 @@
package net.pokeranalytics.android.calcul
package net.pokeranalytics.android.calculus.calcul
import android.content.Context
import net.pokeranalytics.android.R

@ -1,5 +1,7 @@
package net.pokeranalytics.android.exceptions
import net.pokeranalytics.android.model.Criteria
class ModelException(message: String) : Exception(message)
class FormattingException(message: String) : Exception(message)
class RowRepresentableEditDescriptorException(message: String) : Exception(message)
@ -16,7 +18,10 @@ sealed class PokerAnalyticsException(message: String) : Exception(message) {
// object FilterMissingEntity: PokerAnalyticsException(message = "This queryWith has no entity initialized")
// object FilterUnhandledEntity : PokerAnalyticsException(message = "This entity is not filterable")
object QueryValueMapUnknown: PokerAnalyticsException(message = "fieldName is missing")
object QueryTypeUnhandled: PokerAnalyticsException(message = "queryWith type not handled")
class QueryTypeUnhandled(clazz: String) :
PokerAnalyticsException(message = "queryWith type not handled: $clazz")
class ComparisonCriteriaUnhandled(criteria: Criteria) :
PokerAnalyticsException(message = "Criteria type not handled: ${criteria.uniqueIdentifier}")
object QueryValueMapUnexpectedValue: PokerAnalyticsException(message = "valueMap null not expected")
object FilterElementExpectedValueMissing : PokerAnalyticsException(message = "queryWith is empty or null")
// data class FilterElementTypeMissing(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "queryWith element '$filterElementRow' type is missing")

@ -164,6 +164,7 @@ sealed class Criteria(override var uniqueIdentifier: Int) : IntIdentifiable, Row
object Tournament : SimpleCriteria(listOf(QueryCondition.IsTournament), 20)
data class ListCustomFields(override var customFieldId: String) : RealmCriteria(21), CustomFieldCriteria
data class ValueCustomFields(override var customFieldId: String) : ListCriteria(22), CustomFieldCriteria
object Duration : ListCriteria(23)
val queries: List<Query>
get() {
@ -243,12 +244,21 @@ sealed class Criteria(override var uniqueIdentifier: Int) : IntIdentifiable, Row
val queries = when (this.customFieldType(realm)) {
CustomField.Type.AMOUNT.uniqueIdentifier -> comparison<QueryCondition.CustomFieldAmountQuery, Double >()
CustomField.Type.NUMBER.uniqueIdentifier -> comparison<QueryCondition.CustomFieldNumberQuery, Double >()
else -> throw PokerAnalyticsException.QueryTypeUnhandled
else -> throw PokerAnalyticsException.ComparisonCriteriaUnhandled(this)
}
realm.close()
queries
}
else -> throw PokerAnalyticsException.QueryTypeUnhandled
is Duration -> {
(0..12).map { i ->
val more = QueryCondition.Duration(i * 60)
more.operator = QueryCondition.Operator.MORE
val less = QueryCondition.Duration((i + 1) * 60)
less.operator = QueryCondition.Operator.LESS
Query(more, less)
}
}
else -> throw PokerAnalyticsException.ComparisonCriteriaUnhandled(this)
}
}

@ -35,7 +35,7 @@ class Query {
return this
}
fun add(queryConditions: List<QueryCondition>): Query{
fun add(queryConditions: List<QueryCondition>): Query {
this._conditions.addAll(queryConditions)
return this
}
@ -52,10 +52,10 @@ class Query {
}
}
fun getName(context: Context): String {
fun getName(context: Context, separator: String = " + "): String {
return when (this._conditions.size) {
0 -> context.getString(R.string.all_sessions) // @todo should be dependant of the underlying type, ie. Session, Transaction...
else -> this._conditions.joinToString(" + ") { it.getDisplayNameWithValues(context) }
else -> this._conditions.joinToString(separator) { it.getDisplayNameWithValues(context) }
}
}
@ -100,4 +100,19 @@ class Query {
return this
}
/*
Returns the first object Id of any QueryCondition
*/
val objectId: String?
get() {
for (c in this._conditions) {
when (c) {
is QueryCondition.QueryDataCondition<*> -> {
c.objectId?.let { return it }
}
}
}
return null
}
}

@ -80,7 +80,7 @@ sealed class QueryCondition : RowRepresentable {
TransactionType::class.java -> AnyTransactionType()
TournamentName::class.java -> AnyTournamentName()
TournamentFeature::class.java -> AllTournamentFeature()
else -> throw PokerAnalyticsException.QueryTypeUnhandled
else -> throw PokerAnalyticsException.QueryTypeUnhandled((T::class.java).name)
}
}
@ -325,6 +325,11 @@ sealed class QueryCondition : RowRepresentable {
return query.equalTo("id", value).findFirst()?.name ?: NULL_TEXT
}
val objectId: String?
get() {
return this.listOfValues.firstOrNull()
}
}
interface DateTime {

@ -9,7 +9,7 @@ import java.util.*
class PokerAnalyticsMigration : RealmMigration {
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
// DynamicRealm exposes an editable schema
val schema = realm.schema
@ -21,7 +21,8 @@ class PokerAnalyticsMigration : RealmMigration {
if (currentVersion == 0) {
Timber.d("*** Running migration 1")
schema.get("Filter")?.addField("entityType", Int::class.java)?.setNullable("entityType", true)
schema.get("Filter")?.addField("entityType", Int::class.java)
?.setNullable("entityType", true)
schema.get("FilterElement")?.let {
it.setNullable("filterName", true)
it.setNullable("sectionName", true)
@ -83,7 +84,8 @@ class PokerAnalyticsMigration : RealmMigration {
if (currentVersion == 3) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.get("Result")?.addField("numberOfRebuy", Double::class.java)?.setNullable("numberOfRebuy", true)
schema.get("Result")?.addField("numberOfRebuy", Double::class.java)
?.setNullable("numberOfRebuy", true)
currentVersion++
}
@ -117,7 +119,8 @@ class PokerAnalyticsMigration : RealmMigration {
schema.get("CustomField")?.let {
it.addField("type", Integer::class.java).setNullable("type", false)
it.addField("duplicateValue", Boolean::class.java)
it.addField("sortCondition", Integer::class.java).setRequired("sortCondition", true)
it.addField("sortCondition", Integer::class.java)
.setRequired("sortCondition", true)
it.addRealmListField("entries", customFieldEntrySchema)
}
@ -134,7 +137,8 @@ class PokerAnalyticsMigration : RealmMigration {
schema.get("ReportSetup")?.let {
it.addRealmListField("statIds", Int::class.java).setNullable("statIds", true)
it.addRealmListField("criteriaCustomFieldIds", String::class.java)
it.addRealmListField("criteriaIds", Int::class.java).setNullable("criteriaIds", true)
it.addRealmListField("criteriaIds", Int::class.java)
.setNullable("criteriaIds", true)
it.removeField("filters")
schema.get("Filter")?.let { filterSchema ->
it.addRealmObjectField("filter", filterSchema)
@ -195,7 +199,8 @@ class PokerAnalyticsMigration : RealmMigration {
val cardSchema = schema.create("Card")
cardSchema.addField("value", Int::class.java).setRequired("value", false)
cardSchema.addField("suitIdentifier", Int::class.java).setRequired("suitIdentifier", false)
cardSchema.addField("suitIdentifier", Int::class.java)
.setRequired("suitIdentifier", false)
cardSchema.addField("index", Int::class.java)
hhSchema.addRealmListField("board", cardSchema)
@ -203,10 +208,12 @@ class PokerAnalyticsMigration : RealmMigration {
actionSchema.addField("streetIdentifier", Int::class.java)
actionSchema.addField("index", Int::class.java)
actionSchema.addField("position", Int::class.java)
actionSchema.addField("typeIdentifier", Int::class.java).setRequired("typeIdentifier", false)
actionSchema.addField("typeIdentifier", Int::class.java)
.setRequired("typeIdentifier", false)
actionSchema.addField("amount", Double::class.java).setRequired("amount", false)
actionSchema.addField("effectiveAmount", Double::class.java)
actionSchema.addField("positionRemainingStack", Double::class.java).setRequired("positionRemainingStack", false)
actionSchema.addField("positionRemainingStack", Double::class.java)
.setRequired("positionRemainingStack", false)
hhSchema.addRealmListField("actions", actionSchema)
val playerSetupSchema = schema.create("PlayerSetup")
@ -283,11 +290,24 @@ class PokerAnalyticsMigration : RealmMigration {
if (currentVersion == 12) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.get("TransactionType")?.let { tts ->
tts.addField("transferRate", Double::class.java).setNullable("transferRate", true)
tts.addField("transferRate", Double::class.java)
.setNullable("transferRate", true)
schema.get("Bankroll")?.let { bs ->
tts.addRealmObjectField("destination", bs)
} ?: throw PAIllegalStateException("Bankroll schema not found")
}
schema.create("Performance")?.let { schema ->
schema.addField("id", String::class.java).setRequired("id", true)
schema.addPrimaryKey("id")
schema.addField("reportId", Int::class.java).setRequired("report", true)
schema.addField("statId", Int::class.java).setRequired("stat", true)
schema.addField("name", String::class.java).setNullable("name", true)
schema.addField("objectId", String::class.java).setNullable("objectId", true)
schema.addField("customFieldId", String::class.java).setNullable("customFieldId", true)
schema.addField("value", Double::class.java).setNullable("value", true)
}
currentVersion++
}
@ -296,12 +316,12 @@ class PokerAnalyticsMigration : RealmMigration {
}
override fun equals(other: Any?): Boolean {
return other is RealmMigration
}
override fun equals(other: Any?): Boolean {
return other is RealmMigration
}
override fun hashCode(): Int {
return RealmMigration::javaClass.hashCode()
}
override fun hashCode(): Int {
return RealmMigration::javaClass.hashCode()
}
}

@ -0,0 +1,67 @@
package net.pokeranalytics.android.model.realm
import io.realm.Realm
import io.realm.RealmObject
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.ui.fragment.PerformanceKey
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rows.StaticReport
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.extensions.lookupForNameInAllTablesById
import java.util.*
open class Performance() : RealmObject(), RowRepresentable {
var id: String = UUID.randomUUID().toString()
constructor(
report: StaticReport,
stat: Stat,
name: String? = null,
objectId: String? = null,
customFieldId: String? = null,
value: Double? = null
) : this() {
this.reportId = report.uniqueIdentifier
this.statId = stat.uniqueIdentifier
this.name = name
this.objectId = objectId
this.customFieldId = customFieldId
this.value = value
}
var reportId: Int = 0
var statId: Int = 0
var name: String? = null
var objectId: String? = null
var customFieldId: String? = null
var value: Double? = null
fun toStaticReport(realm: Realm): StaticReport {
return StaticReport.newInstance(realm, this.reportId, this.customFieldId)
}
fun displayValue(realm: Realm): CharSequence {
this.name?.let { return it }
this.objectId?.let { realm.lookupForNameInAllTablesById(it) }
return NULL_TEXT
}
val performanceKey: PerformanceKey
get() {
return Stat.valueByIdentifier(this.statId)
}
val stat: Stat
get() {
return Stat.valueByIdentifier(this.statId)
}
override val resId: Int?
get() {
return this.performanceKey.resId
}
}

@ -6,7 +6,7 @@ import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.Criteria

@ -9,6 +9,7 @@ import com.google.android.material.bottomnavigation.BottomNavigationView
import io.realm.RealmResults
import net.pokeranalytics.android.BuildConfig
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.NewPerformanceListener
import net.pokeranalytics.android.databinding.ActivityHomeBinding
import net.pokeranalytics.android.model.realm.Currency
import net.pokeranalytics.android.ui.activity.components.BaseActivity
@ -16,7 +17,7 @@ import net.pokeranalytics.android.ui.adapter.HomePagerAdapter
import net.pokeranalytics.android.util.billing.AppGuard
class HomeActivity : BaseActivity() {
class HomeActivity : BaseActivity(), NewPerformanceListener {
companion object {
fun newInstance(context: Context, id: Int) {
@ -47,6 +48,7 @@ class HomeActivity : BaseActivity() {
displayFragment(4)
}
}
binding.navigation.getOrCreateBadge(item.itemId).isVisible = false
return@OnNavigationItemSelectedListener true
}
@ -77,6 +79,8 @@ class HomeActivity : BaseActivity() {
initUI()
checkFirstLaunch()
this.paApplication.reportWhistleBlower?.addListener(this)
}
private fun observeRealmObjects() {
@ -85,9 +89,9 @@ class HomeActivity : BaseActivity() {
// observe currency changes
this.currencies = realm.where(Currency::class.java).findAll()
this.currencies.addChangeListener { t, _ ->
this.currencies.addChangeListener { currencies, _ ->
realm.executeTransaction {
t.forEach {
currencies.forEach {
it.refreshRelatedRatedValues()
}
}
@ -108,6 +112,7 @@ class HomeActivity : BaseActivity() {
viewPager.offscreenPageLimit = 5
viewPager.enablePaging = false
viewPager.adapter = homePagerAdapter
}
/**
@ -131,4 +136,9 @@ class HomeActivity : BaseActivity() {
binding.viewPager.setCurrentItem(index, false)
}
override fun newBestPerformanceHandler() {
binding.navigation.getOrCreateBadge(R.id.navigation_reports).isVisible = true
binding.navigation.getOrCreateBadge(R.id.navigation_reports).number = 1
}
}

@ -4,7 +4,7 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.ui.activity.components.ReportActivity

@ -5,7 +5,7 @@ import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.ui.activity.components.BaseActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode

@ -13,11 +13,12 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import io.realm.Realm
import net.pokeranalytics.android.PokerAnalyticsApplication
import net.pokeranalytics.android.model.realm.Location
import net.pokeranalytics.android.util.CrashLogging
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.helpers.AppReviewManager
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.util.CrashLogging
import net.pokeranalytics.android.util.LocationManager
import net.pokeranalytics.android.util.PermissionRequest
@ -37,6 +38,9 @@ abstract class BaseActivity : AppCompatActivity() {
private var permissionRequest: PermissionRequest? = null
val paApplication: PokerAnalyticsApplication
get() { return this.application as PokerAnalyticsApplication }
val bottomSheetViewModel: RootBottomSheetViewModel by lazy {
ViewModelProvider(this).get(RootBottomSheetViewModel::class.java)
}
@ -125,6 +129,7 @@ abstract class BaseActivity : AppCompatActivity() {
fragmentTransaction.commit()
}
/**
* Return the realm instance
*/

@ -5,7 +5,7 @@ import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import net.pokeranalytics.android.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.ui.viewmodel.ReportViewModel

@ -7,7 +7,7 @@ import android.view.*
import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.Realm
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.databinding.FragmentReportCreationBinding

@ -1,6 +1,7 @@
package net.pokeranalytics.android.ui.fragment
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
@ -15,15 +16,16 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.NewPerformanceListener
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.databinding.FragmentReportsBinding
import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.combined
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.realm.Performance
import net.pokeranalytics.android.model.realm.ReportSetup
import net.pokeranalytics.android.ui.modules.datalist.DataListActivity
import net.pokeranalytics.android.ui.activity.ReportCreationActivity
import net.pokeranalytics.android.ui.activity.components.ReportActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode
@ -31,14 +33,29 @@ 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.modules.datalist.DataListActivity
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rows.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rows.ReportRow
import net.pokeranalytics.android.ui.view.rows.StaticReport
import net.pokeranalytics.android.util.NULL_TEXT
import timber.log.Timber
import java.util.*
class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
interface PerformanceKey {
val resId: Int?
val value: String
}
data class ReportSection(var report: StaticReport, var performances: MutableList<PerformanceRow>)
data class PerformanceRow(var performance: Performance, var report: StaticReport, var badge: Boolean): RowRepresentable {
override val resId: Int? = this.performance.resId
override val viewType: Int = RowViewType.TITLE_BADGE_VALUE.identifier
}
class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate, NewPerformanceListener {
private lateinit var reportSetups: RealmResults<ReportSetup>
private var adapterRows = mutableListOf<RowRepresentable>()
@ -144,8 +161,11 @@ class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSourc
ReportCreationActivity.newInstanceForResult(this, requireContext())
}
this.paApplication.reportWhistleBlower?.addListener(this)
}
// Rows
private fun updateRows() {
@ -154,21 +174,65 @@ class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSourc
adapterRows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.custom))
adapterRows.addAll(this.reportSetups)
}
adapterRows.addAll(ReportRow.getRows())
this.addStaticReportRows()
this.dataListAdapter.notifyDataSetChanged()
}
private fun addStaticReportRows() {
val sections = buildReportSections()
for (section in sections) {
adapterRows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = section.report.resId))
for (performance in section.performances) {
adapterRows.add(performance)
}
}
}
private fun buildReportSections(): List<ReportSection> {
val sections = mutableListOf<ReportSection>()
val performances = getRealm().where(Performance::class.java).findAll()
for (performance in performances) {
val report = performance.toStaticReport(getRealm())
val badge = this.paApplication.reportWhistleBlower?.has(performance.id) ?: false
val reportRow = PerformanceRow(performance, report, badge)
sections.firstOrNull { it.report == report }?.let { section ->
section.performances.add(reportRow)
} ?: run {
val section = ReportSection(report, mutableListOf(reportRow))
sections.add(section)
}
}
return sections
}
override fun adapterRows(): List<RowRepresentable> {
return this.adapterRows
}
override fun charSequenceForRow(row: RowRepresentable, context: Context): CharSequence {
return when (row) {
is PerformanceRow -> {
row.performance.displayValue(getRealm())
}
else -> NULL_TEXT
}
}
override fun onRowSelected(position: Int, row: RowRepresentable, tag: Int) {
super.onRowSelected(position, row, tag)
when (row) {
is ReportRow -> {
is PerformanceRow -> {
val reportName = row.localizedTitle(requireContext())
launchComputation(row.criteria, reportName)
launchComputation(row.report.criteria, reportName, row.performance.stat)
}
is ReportSetup -> {
val display = ReportDisplay.values()[row.display]
@ -180,17 +244,16 @@ class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSourc
/**
* Launch computation
*/
private fun launchComputation(criteriaList: List<Criteria>, reportName: String) {
private fun launchComputation(criteriaList: List<Criteria>, reportName: String, stat: Stat) {
if (criteriaList.combined().size < 2) {
Toast.makeText(context, R.string.less_then_2_values_for_display, Toast.LENGTH_LONG).show()
return
}
val requiredStats: List<Stat> = listOf(Stat.NET_RESULT)
val options = Calculator.Options(
progressValues = Calculator.Options.ProgressValues.STANDARD,
stats = requiredStats,
stats = listOf(stat),
criterias = criteriaList
)
@ -225,4 +288,14 @@ class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSourc
}
}
override fun newBestPerformanceHandler() {
Timber.d("newBestPerformanceHandler called")
requireActivity().runOnUiThread {
this.updateRows()
this.dataListAdapter.notifyDataSetChanged()
}
}
}

@ -7,17 +7,16 @@ import android.os.Bundle
import android.view.View
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import com.google.android.play.core.review.ReviewManagerFactory
import net.pokeranalytics.android.PokerAnalyticsApplication
import net.pokeranalytics.android.R
import net.pokeranalytics.android.util.CrashLogging
import net.pokeranalytics.android.ui.activity.components.BaseActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.util.CrashLogging
import java.io.File
import java.util.*
import kotlin.collections.ArrayList
abstract class BaseFragment : Fragment() {
@ -63,6 +62,10 @@ abstract class BaseFragment : Fragment() {
CrashLogging.log("$this.localClassName onActivityCreated")
}
val paApplication: PokerAnalyticsApplication
get() { return (this.requireActivity() as BaseActivity).paApplication }
/**
* Method called when the activity override onBackPressed and send the information to the fragment
*/

@ -12,14 +12,13 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.ComputableGroup
import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.databinding.FragmentComposableTableReportBinding
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.ui.activity.components.ReportActivity
import net.pokeranalytics.android.ui.adapter.DisplayDescriptor
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter

@ -15,7 +15,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calcul.*
import net.pokeranalytics.android.calculus.calcul.*
import net.pokeranalytics.android.calculus.AggregationType
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.Report

@ -17,7 +17,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calcul.defaultStatEntries
import net.pokeranalytics.android.calculus.calcul.defaultStatEntries
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.databinding.FragmentCalendarDetailsBinding

@ -18,7 +18,6 @@ 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.cell_calendar_time_unit.view.*
import kotlinx.android.synthetic.main.row_recycler.view.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.ComputedStat
import net.pokeranalytics.android.calculus.Stat
@ -33,12 +32,12 @@ import net.pokeranalytics.android.ui.adapter.BindableHolder
import net.pokeranalytics.android.ui.adapter.RecyclerAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.extensions.ChipGroupExtension
import net.pokeranalytics.android.ui.extensions.dp
import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.extensions.setTextFormat
import net.pokeranalytics.android.ui.fragment.PerformanceRow
import net.pokeranalytics.android.ui.graph.Graph
import net.pokeranalytics.android.ui.modules.bankroll.BankrollRowRepresentable
import net.pokeranalytics.android.ui.graph.setStyle
import net.pokeranalytics.android.ui.modules.bankroll.BankrollRowRepresentable
import net.pokeranalytics.android.ui.modules.calendar.CalendarItemCell
import net.pokeranalytics.android.ui.modules.calendar.CellResult
import net.pokeranalytics.android.ui.modules.handhistory.views.RowHandHistoryViewHolder
@ -69,6 +68,7 @@ enum class RowViewType(private var layoutRes: Int) : ViewIdentifier {
TITLE_VALUE(R.layout.row_title_value),
TITLE_VALUE_ARROW(R.layout.row_title_value_arrow),
TITLE_VALUE_ACTION(R.layout.row_title_value_action),
TITLE_BADGE_VALUE(R.layout.row_title_badge_value),
TITLE_SWITCH(R.layout.row_title_switch),
TITLE_GRID(R.layout.row_bottom_sheet_grid_title),
DATA(R.layout.row_title),
@ -123,6 +123,8 @@ enum class RowViewType(private var layoutRes: Int) : ViewIdentifier {
TITLE_SWITCH, TITLE_CHECK, TITLE_VALUE_CHECK, CONTENT, TITLE_SUBTITLE,
HEADER_SUBTITLE, DATA, BOTTOM_SHEET_DATA, LOADER -> RowViewHolder(layout)
TITLE_BADGE_VALUE -> RowBadgeViewHolder(layout)
// Row Session
ROW_SESSION -> RowSessionViewHolder(layout)
@ -666,5 +668,31 @@ enum class RowViewType(private var layoutRes: Int) : ViewIdentifier {
}
}
/**
* Display a player image view
*/
inner class RowBadgeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
BindableHolder {
override fun onBind(position: Int, row: RowRepresentable, adapter: RecyclerAdapter) {
if (row is PerformanceRow) {
itemView.findViewById<AppCompatTextView>(R.id.title)?.let {
it.text = row.localizedTitle(itemView.context)
}
itemView.findViewById<AppCompatTextView>(R.id.value)?.let {
it.text = adapter.dataSource.charSequenceForRow(row, itemView.context)
}
itemView.findViewById<AppCompatImageView>(R.id.badge)?.let {
it.isVisible = row.badge
}
val listener = View.OnClickListener {
adapter.delegate?.onRowSelected(position, row)
}
itemView.setOnClickListener(listener)
}
}
}
}

@ -1,63 +0,0 @@
package net.pokeranalytics.android.ui.view.rows
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
/**
* An enum managing the report rows
*/
enum class ReportRow : RowRepresentable {
BLINDS,
BUY_IN,
DAY_OF_WEEKS,
GENERAL,
LOCATIONS,
//NUMBER_OF_TABLES,
TOURNAMENT_TYPES,
GAME;
companion object {
/**
* Return the report rows
*/
fun getRows(): ArrayList<RowRepresentable> {
val rows = ArrayList<RowRepresentable>()
rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.comparison))
rows.addAll(values())
return rows
}
}
override val resId: Int?
get() {
return when (this) {
BLINDS -> R.string.blinds
BUY_IN -> R.string.buyin
DAY_OF_WEEKS -> R.string.day_of_the_week
GENERAL -> R.string.general
LOCATIONS -> R.string.locations
//NUMBER_OF_TABLES -> R.string.number_of_tables
TOURNAMENT_TYPES -> R.string.tournament_type_complete
GAME -> R.string.game
}
}
override val viewType: Int = RowViewType.TITLE_ARROW.ordinal
val criteria: List<Criteria>
get() {
return when (this) {
BLINDS -> listOf(Criteria.Stakes)
BUY_IN -> listOf(Criteria.TournamentFees)
DAY_OF_WEEKS -> listOf(Criteria.DaysOfWeek)
GENERAL -> listOf(Criteria.SessionTypes, Criteria.BankrollTypes)
LOCATIONS -> listOf(Criteria.Locations)
//NUMBER_OF_TABLES -> listOf() //TODO
TOURNAMENT_TYPES -> listOf(Criteria.TournamentTypes)
GAME -> listOf(Criteria.Games)
}
}
}

@ -0,0 +1,117 @@
package net.pokeranalytics.android.ui.view.rows
import android.content.Context
import io.realm.Realm
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.enumerations.IntIdentifiable
/**
* The list of possible static reports
*/
sealed class StaticReport(override var uniqueIdentifier: Int) : RowRepresentable, IntIdentifiable {
object General : StaticReport(1)
object Blinds : StaticReport(2)
object TournamentBuyin : StaticReport(3)
object DayOfWeek : StaticReport(4)
object Location : StaticReport(5)
object TournamentType : StaticReport(6)
object Game : StaticReport(7)
object TableSize : StaticReport(8)
object Duration : StaticReport(9)
object OptimalDuration : StaticReport(10)
data class CustomFieldList(var customField: CustomField) : StaticReport(11)
companion object {
fun newInstance(realm: Realm, reportIdentifier: Int, customFieldId: String?): StaticReport {
return when (reportIdentifier) {
1 -> General
2 -> Blinds
3 -> TournamentBuyin
4 -> DayOfWeek
5 -> Location
6 -> TournamentType
7 -> Game
8 -> TableSize
9 -> Duration
10 -> OptimalDuration
11 -> {
customFieldId?.let { id ->
realm.where(CustomField::class.java).equalTo("id", customFieldId).findFirst()?.let { customField ->
CustomFieldList(customField)
} ?: run { throw PAIllegalStateException("Custom field not found: $id") }
} ?: run { throw PAIllegalStateException("Missing custom field id") }
}
else -> throw PAIllegalStateException("Can't create StaticReport. id = $reportIdentifier, cfid = $customFieldId")
}
}
val basicReports: Set<StaticReport> = setOf(Blinds) // setOf(General, Blinds, TournamentBuyin,
// DayOfWeek, Location, TournamentType, Game, TableSize, Duration, OptimalDuration)
}
override fun getDisplayName(context: Context): String {
return when (this) {
is CustomFieldList -> this.customField.getDisplayName(context)
else -> this.resId?.let { context.getString(it) } ?: NULL_TEXT
}
}
override val resId: Int?
get() {
return when (this) {
Blinds -> R.string.blinds
TournamentBuyin -> R.string.buyin
DayOfWeek -> R.string.day_of_the_week
General -> R.string.general
Location -> R.string.locations
//NUMBER_OF_TABLES -> R.string.number_of_tables
TournamentType -> R.string.tournament_type_complete
Game -> R.string.game
TableSize -> R.string.table_size
Duration -> R.string.duration
OptimalDuration -> R.string.optimal_duration
is CustomFieldList -> null
}
}
override val viewType: Int = RowViewType.TITLE_ARROW.ordinal
val criteria: List<Criteria>
get() {
return when (this) {
Blinds -> listOf(Criteria.Stakes)
TournamentBuyin -> listOf(Criteria.TournamentFees)
DayOfWeek -> listOf(Criteria.DaysOfWeek)
General -> listOf(Criteria.SessionTypes, Criteria.BankrollTypes)
Location -> listOf(Criteria.Locations)
//NUMBER_OF_TABLES -> listOf() //TODO
TournamentType -> listOf(Criteria.TournamentTypes)
Game -> listOf(Criteria.Games)
TableSize -> listOf(Criteria.TableSizes)
Duration -> listOf(Criteria.Duration)
OptimalDuration -> listOf()
is CustomFieldList -> listOf(Criteria.ListCustomFields(this.customField.id))
}
}
val stats: List<Stat>
get() {
return when (this) {
OptimalDuration -> listOf(Stat.AVERAGE_NET_BB)
else -> listOf(Stat.NET_RESULT, Stat.HOURLY_RATE)
}
}
}

@ -3,10 +3,10 @@ package net.pokeranalytics.android.ui.viewmodel
import android.content.Context
import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.LineDataSet
import net.pokeranalytics.android.calcul.ReportDisplay
import net.pokeranalytics.android.calcul.barEntries
import net.pokeranalytics.android.calcul.lineEntries
import net.pokeranalytics.android.calcul.multiLineEntries
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.calcul.barEntries
import net.pokeranalytics.android.calculus.calcul.lineEntries
import net.pokeranalytics.android.calculus.calcul.multiLineEntries
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.ui.activity.components.ReportParameters

@ -13,7 +13,11 @@ fun <T : RealmModel>Realm.count(clazz: Class<T>) : Long {
}
fun <T : Identifiable> Realm.findById(clazz: Class<T>, id: String) : T? {
return this.where(clazz).equalTo("id", id).findFirst()
return this.where(clazz).findById(id)
}
fun <T : Identifiable> RealmQuery<T>.findById(id: String) : T? {
return this.equalTo("id", id).findFirst()
}
inline fun <reified T: Identifiable> Realm.findById(id: String) : T? {
@ -130,3 +134,16 @@ fun < T : RealmModel> Realm.find(clazz: Class<T>, searchContent: String?) : Real
inline fun <reified T : Filterable> Realm.findAll(query: Query, sortField: String? = null): RealmResults<T> {
return Filter.queryOn(this, query, sortField)
}
fun Realm.lookupForNameInAllTablesById(id: String): String? {
val classNames = this.schema.all.map { it.className }
for (className in classNames) {
val clazz = Class.forName(className)
if (clazz.isInstance(NameManageable::class.java)) {
val c = clazz.asSubclass(RealmModel::class.java)
val instance = this.where(c).equalTo("id", id).findFirst()
return (instance as? NameManageable)?.name
}
}
return null
}

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/red" />
<size
android:width="12dp"
android:height="12dp" />
</shape>

@ -29,6 +29,7 @@
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingBottom="72dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

@ -0,0 +1,70 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?selectableItemBackground">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title"
style="@style/PokerAnalyticsTheme.TextView.RowTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/guidelineStart"
app:layout_constraintTop_toTopOf="parent"
tools:text="Title" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/badge"
android:visibility="invisible"
android:layout_width="8dp"
android:layout_height="8dp"
android:src="@drawable/circle_red"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/value"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/value"
style="@style/PokerAnalyticsTheme.TextView.RowValue"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:ellipsize="end"
android:gravity="end"
android:maxLines="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/nextArrow"
app:layout_constraintTop_toTopOf="parent"
tools:text="Value" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guidelineStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="16dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guidelineEnd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="8dp" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/nextArrow"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_arrow_right"
android:tint="@color/grey_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/guidelineEnd"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -824,5 +824,6 @@
<string name="errors">error(s)</string>
<string name="transfer">Transfer</string>
<string name="destination">Destination</string>
<string name="optimal_duration">Optimal duration</string>
</resources>

Loading…
Cancel
Save