From 925ab12faff65deaa3cf758efdd9a7a4d2f8cd00 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 17 Oct 2022 18:09:26 +0200 Subject: [PATCH] first commit --- .../android/PokerAnalyticsApplication.kt | 14 ++ .../pokeranalytics/android/calculus/Report.kt | 17 ++ .../android/calculus/ReportWhistleBlower.kt | 217 ++++++++++++++++++ .../pokeranalytics/android/calculus/Stat.kt | 5 +- .../calcul/AggregationTypeExtensions.kt | 2 +- .../calcul/ComputedResultsExtensions.kt | 2 +- .../{ => calculus}/calcul/ReportDisplay.kt | 2 +- .../{ => calculus}/calcul/ReportExtensions.kt | 2 +- .../calcul/StatRepresentable.kt | 2 +- .../android/exceptions/Exceptions.kt | 7 +- .../pokeranalytics/android/model/Criteria.kt | 14 +- .../android/model/filter/Query.kt | 21 +- .../android/model/filter/QueryCondition.kt | 7 +- .../migrations/PokerAnalyticsMigration.kt | 50 ++-- .../android/model/realm/Performance.kt | 67 ++++++ .../android/model/realm/ReportSetup.kt | 2 +- .../android/ui/activity/HomeActivity.kt | 16 +- .../ui/activity/ProgressReportActivity.kt | 2 +- .../ui/activity/ReportCreationActivity.kt | 2 +- .../ui/activity/components/BaseActivity.kt | 7 +- .../ui/activity/components/ReportActivity.kt | 2 +- .../ui/fragment/ReportCreationFragment.kt | 2 +- .../android/ui/fragment/ReportsFragment.kt | 93 +++++++- .../ui/fragment/components/BaseFragment.kt | 9 +- .../report/ComposableTableReportFragment.kt | 3 +- .../fragment/report/ProgressReportFragment.kt | 2 +- .../calendar/CalendarDetailsFragment.kt | 2 +- .../android/ui/view/RowViewType.kt | 34 ++- .../android/ui/view/rows/ReportRow.kt | 63 ----- .../android/ui/view/rows/StaticReport.kt | 117 ++++++++++ .../android/ui/viewmodel/ReportViewModel.kt | 8 +- .../util/extensions/RealmExtensions.kt | 19 +- app/src/main/res/drawable/circle_red.xml | 11 + app/src/main/res/layout/fragment_reports.xml | 1 + .../main/res/layout/row_title_badge_value.xml | 70 ++++++ app/src/main/res/values/strings.xml | 1 + 36 files changed, 770 insertions(+), 125 deletions(-) create mode 100644 app/src/main/java/net/pokeranalytics/android/calculus/ReportWhistleBlower.kt rename app/src/main/java/net/pokeranalytics/android/{ => calculus}/calcul/AggregationTypeExtensions.kt (85%) rename app/src/main/java/net/pokeranalytics/android/{ => calculus}/calcul/ComputedResultsExtensions.kt (98%) rename app/src/main/java/net/pokeranalytics/android/{ => calculus}/calcul/ReportDisplay.kt (96%) rename app/src/main/java/net/pokeranalytics/android/{ => calculus}/calcul/ReportExtensions.kt (97%) rename app/src/main/java/net/pokeranalytics/android/{ => calculus}/calcul/StatRepresentable.kt (98%) create mode 100644 app/src/main/java/net/pokeranalytics/android/model/realm/Performance.kt delete mode 100644 app/src/main/java/net/pokeranalytics/android/ui/view/rows/ReportRow.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/ui/view/rows/StaticReport.kt create mode 100644 app/src/main/res/drawable/circle_red.xml create mode 100644 app/src/main/res/layout/row_title_badge_value.xml diff --git a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt index e77f8e48..7f875bb1 100644 --- a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt +++ b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt @@ -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) + } + } /** diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt index 9d9114c4..1a78461b 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt @@ -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 } + } + } diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/ReportWhistleBlower.kt b/app/src/main/java/net/pokeranalytics/android/calculus/ReportWhistleBlower.kt new file mode 100644 index 00000000..47468433 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/calculus/ReportWhistleBlower.kt @@ -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? = null + var results: RealmResults? = null + + var timer: CountDownTimer? = null + +// private lateinit var realm: Realm + + private val currentNotifications: MutableList = mutableListOf() + + private val listeners: MutableList = 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) + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt index fd928866..ce2f7019 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt @@ -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) { diff --git a/app/src/main/java/net/pokeranalytics/android/calcul/AggregationTypeExtensions.kt b/app/src/main/java/net/pokeranalytics/android/calculus/calcul/AggregationTypeExtensions.kt similarity index 85% rename from app/src/main/java/net/pokeranalytics/android/calcul/AggregationTypeExtensions.kt rename to app/src/main/java/net/pokeranalytics/android/calculus/calcul/AggregationTypeExtensions.kt index ad0df1f2..b61d9e6d 100644 --- a/app/src/main/java/net/pokeranalytics/android/calcul/AggregationTypeExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/calcul/AggregationTypeExtensions.kt @@ -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 diff --git a/app/src/main/java/net/pokeranalytics/android/calcul/ComputedResultsExtensions.kt b/app/src/main/java/net/pokeranalytics/android/calculus/calcul/ComputedResultsExtensions.kt similarity index 98% rename from app/src/main/java/net/pokeranalytics/android/calcul/ComputedResultsExtensions.kt rename to app/src/main/java/net/pokeranalytics/android/calculus/calcul/ComputedResultsExtensions.kt index b232713c..211924ec 100644 --- a/app/src/main/java/net/pokeranalytics/android/calcul/ComputedResultsExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/calcul/ComputedResultsExtensions.kt @@ -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.* diff --git a/app/src/main/java/net/pokeranalytics/android/calcul/ReportDisplay.kt b/app/src/main/java/net/pokeranalytics/android/calculus/calcul/ReportDisplay.kt similarity index 96% rename from app/src/main/java/net/pokeranalytics/android/calcul/ReportDisplay.kt rename to app/src/main/java/net/pokeranalytics/android/calculus/calcul/ReportDisplay.kt index f74185ce..21586bfc 100644 --- a/app/src/main/java/net/pokeranalytics/android/calcul/ReportDisplay.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/calcul/ReportDisplay.kt @@ -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 diff --git a/app/src/main/java/net/pokeranalytics/android/calcul/ReportExtensions.kt b/app/src/main/java/net/pokeranalytics/android/calculus/calcul/ReportExtensions.kt similarity index 97% rename from app/src/main/java/net/pokeranalytics/android/calcul/ReportExtensions.kt rename to app/src/main/java/net/pokeranalytics/android/calculus/calcul/ReportExtensions.kt index ab4d639b..bd8d1cd4 100644 --- a/app/src/main/java/net/pokeranalytics/android/calcul/ReportExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/calcul/ReportExtensions.kt @@ -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 diff --git a/app/src/main/java/net/pokeranalytics/android/calcul/StatRepresentable.kt b/app/src/main/java/net/pokeranalytics/android/calculus/calcul/StatRepresentable.kt similarity index 98% rename from app/src/main/java/net/pokeranalytics/android/calcul/StatRepresentable.kt rename to app/src/main/java/net/pokeranalytics/android/calculus/calcul/StatRepresentable.kt index c69d9158..5febf527 100644 --- a/app/src/main/java/net/pokeranalytics/android/calcul/StatRepresentable.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/calcul/StatRepresentable.kt @@ -1,4 +1,4 @@ -package net.pokeranalytics.android.calcul +package net.pokeranalytics.android.calculus.calcul import android.content.Context import net.pokeranalytics.android.R diff --git a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt index ce70fd9c..c86980ed 100644 --- a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt +++ b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt @@ -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") diff --git a/app/src/main/java/net/pokeranalytics/android/model/Criteria.kt b/app/src/main/java/net/pokeranalytics/android/model/Criteria.kt index 4ff1f0a1..ff34c848 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/Criteria.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/Criteria.kt @@ -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 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() CustomField.Type.NUMBER.uniqueIdentifier -> comparison() - 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) } } diff --git a/app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt b/app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt index fba3bfa6..8a8b583f 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt @@ -35,7 +35,7 @@ class Query { return this } - fun add(queryConditions: List): Query{ + fun add(queryConditions: List): 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 + } + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt b/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt index ebf26095..d07b804c 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt @@ -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 { diff --git a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt index 3f95f187..318ad366 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt @@ -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() + } } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Performance.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Performance.kt new file mode 100644 index 00000000..f7207ce1 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Performance.kt @@ -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 + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt index d629b314..a088a991 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt @@ -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 diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt index 1e1135bb..79016757 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt @@ -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 + } + } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/ProgressReportActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/ProgressReportActivity.kt index fb74ba3b..7cc2dd56 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/ProgressReportActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/ProgressReportActivity.kt @@ -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 diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/ReportCreationActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/ReportCreationActivity.kt index 4be93839..1d3ec2bd 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/ReportCreationActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/ReportCreationActivity.kt @@ -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 diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/components/BaseActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/components/BaseActivity.kt index 9d05ae22..7ebfac34 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/components/BaseActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/components/BaseActivity.kt @@ -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 */ diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/components/ReportActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/components/ReportActivity.kt index 254396af..7baa9b0c 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/components/ReportActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/components/ReportActivity.kt @@ -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 diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportCreationFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportCreationFragment.kt index 556edb90..8ac65939 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportCreationFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportCreationFragment.kt @@ -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 diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt index dc162ae6..381af597 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt @@ -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) +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 private var adapterRows = mutableListOf() @@ -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 { + + val sections = mutableListOf() + + 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 { 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, reportName: String) { + private fun launchComputation(criteriaList: List, 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 = 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() + } + + } + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/BaseFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/BaseFragment.kt index fcf15f08..1ca1d073 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/BaseFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/BaseFragment.kt @@ -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 */ diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComposableTableReportFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComposableTableReportFragment.kt index 937950a0..9f054949 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComposableTableReportFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComposableTableReportFragment.kt @@ -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 diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ProgressReportFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ProgressReportFragment.kt index 03845dd8..c4a863ce 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ProgressReportFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ProgressReportFragment.kt @@ -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 diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt index d80d71e0..cca49923 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt @@ -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 diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt index 04b17577..3a3e94f3 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt @@ -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(R.id.title)?.let { + it.text = row.localizedTitle(itemView.context) + } + itemView.findViewById(R.id.value)?.let { + it.text = adapter.dataSource.charSequenceForRow(row, itemView.context) + } + itemView.findViewById(R.id.badge)?.let { + it.isVisible = row.badge + } + + val listener = View.OnClickListener { + adapter.delegate?.onRowSelected(position, row) + } + itemView.setOnClickListener(listener) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rows/ReportRow.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rows/ReportRow.kt deleted file mode 100644 index 5ef8c002..00000000 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/rows/ReportRow.kt +++ /dev/null @@ -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 { - val rows = ArrayList() - 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 - 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) - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rows/StaticReport.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rows/StaticReport.kt new file mode 100644 index 00000000..bfc2d752 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/rows/StaticReport.kt @@ -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 = 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 + 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 + get() { + return when (this) { + OptimalDuration -> listOf(Stat.AVERAGE_NET_BB) + else -> listOf(Stat.NET_RESULT, Stat.HOURLY_RATE) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/viewmodel/ReportViewModel.kt b/app/src/main/java/net/pokeranalytics/android/ui/viewmodel/ReportViewModel.kt index 5e2bd226..2dd6ddc1 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/viewmodel/ReportViewModel.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/viewmodel/ReportViewModel.kt @@ -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 diff --git a/app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt b/app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt index 85012d46..21d1236e 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt @@ -13,7 +13,11 @@ fun Realm.count(clazz: Class) : Long { } fun Realm.findById(clazz: Class, id: String) : T? { - return this.where(clazz).equalTo("id", id).findFirst() + return this.where(clazz).findById(id) +} + +fun RealmQuery.findById(id: String) : T? { + return this.equalTo("id", id).findFirst() } inline fun Realm.findById(id: String) : T? { @@ -130,3 +134,16 @@ fun < T : RealmModel> Realm.find(clazz: Class, searchContent: String?) : Real inline fun Realm.findAll(query: Query, sortField: String? = null): RealmResults { 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 +} \ No newline at end of file diff --git a/app/src/main/res/drawable/circle_red.xml b/app/src/main/res/drawable/circle_red.xml new file mode 100644 index 00000000..60e3c7e0 --- /dev/null +++ b/app/src/main/res/drawable/circle_red.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_reports.xml b/app/src/main/res/layout/fragment_reports.xml index 92b63906..761f3dba 100644 --- a/app/src/main/res/layout/fragment_reports.xml +++ b/app/src/main/res/layout/fragment_reports.xml @@ -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" diff --git a/app/src/main/res/layout/row_title_badge_value.xml b/app/src/main/res/layout/row_title_badge_value.xml new file mode 100644 index 00000000..2afdd7ee --- /dev/null +++ b/app/src/main/res/layout/row_title_badge_value.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 54486f52..b1f1a75a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -824,5 +824,6 @@ error(s) Transfer Destination + Optimal duration