Merge remote-tracking branch 'origin/dev' into dev

feature/top10
Razmig Sarkissian 7 years ago
commit 69070565e4
  1. 2
      app/build.gradle
  2. 4
      app/src/androidTest/java/net/pokeranalytics/android/performanceTests/PerfsInstrumentedUnitTest.kt
  3. 22
      app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatsInstrumentedUnitTest.kt
  4. 5
      app/src/main/AndroidManifest.xml
  5. 122
      app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt
  6. 180
      app/src/main/java/net/pokeranalytics/android/calculus/Report.kt
  7. 33
      app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt
  8. 8
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  9. 2
      app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt
  10. 1
      app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt
  11. 55
      app/src/main/java/net/pokeranalytics/android/ui/activity/ReportDetailsActivity.kt
  12. 21
      app/src/main/java/net/pokeranalytics/android/ui/activity/StatisticDetailsActivity.kt
  13. 14
      app/src/main/java/net/pokeranalytics/android/ui/adapter/HomePagerAdapter.kt
  14. 69
      app/src/main/java/net/pokeranalytics/android/ui/adapter/ReportPagerAdapter.kt
  15. 10
      app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarDetailsFragment.kt
  16. 8
      app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarFragment.kt
  17. 3
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ComparisonChartFragment.kt
  18. 1
      app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt
  19. 1
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt
  20. 122
      app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt
  21. 92
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportDetailsFragment.kt
  22. 43
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt
  23. 1
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt
  24. 116
      app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticDetailsFragment.kt
  25. 28
      app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt
  26. 4
      app/src/main/java/net/pokeranalytics/android/ui/view/LegendView.kt
  27. 4
      app/src/main/java/net/pokeranalytics/android/ui/view/NoPagingViewPager.kt
  28. 2
      app/src/main/java/net/pokeranalytics/android/ui/view/SessionRowView.kt
  29. 2
      app/src/main/java/net/pokeranalytics/android/ui/view/TransactionRowView.kt
  30. 20
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/ReportRow.kt
  31. 2
      app/src/main/res/layout/activity_home.xml
  32. 7
      app/src/main/res/layout/activity_report_details.xml
  33. 14
      app/src/main/res/layout/activity_statistic_details.xml
  34. 16
      app/src/main/res/layout/fragment_calendar_details.xml
  35. 25
      app/src/main/res/layout/fragment_evograph.xml
  36. 62
      app/src/main/res/layout/fragment_report_details.xml
  37. 45
      app/src/main/res/layout/fragment_statistic_details.xml
  38. 2
      app/src/main/res/values/styles.xml
  39. 2
      app/src/test/java/net/pokeranalytics/android/ExampleUnitTest.kt
  40. 4
      build.gradle
  41. 4
      gradle/wrapper/gradle-wrapper.properties

@ -35,7 +35,7 @@ android {
buildTypes { buildTypes {
release { release {
minifyEnabled false minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
applicationVariants.all { variant -> applicationVariants.all { variant ->
variant.outputs.all { output -> variant.outputs.all { output ->

@ -53,7 +53,7 @@ class PerfsInstrumentedUnitTest : RealmInstrumentedUnitTest() {
Timber.d("computableResults: ${computableResults.size}") Timber.d("computableResults: ${computableResults.size}")
Timber.d("sets: ${sets.size}") Timber.d("sets: ${sets.size}")
val stats: List<Stat> = listOf(Stat.NETRESULT, Stat.AVERAGE) val stats: List<Stat> = listOf(Stat.NET_RESULT, Stat.AVERAGE)
val group = ComputableGroup("test", listOf(), stats) val group = ComputableGroup("test", listOf(), stats)
val options = Calculator.Options() val options = Calculator.Options()
@ -62,7 +62,7 @@ class PerfsInstrumentedUnitTest : RealmInstrumentedUnitTest() {
val results: ComputedResults = Calculator.compute(realm, group, options) val results: ComputedResults = Calculator.compute(realm, group, options)
Timber.d("*** ended in ${System.currentTimeMillis() - start} milliseconds") Timber.d("*** ended in ${System.currentTimeMillis() - start} milliseconds")
val sum = results.computedStat(Stat.NETRESULT) val sum = results.computedStat(Stat.NET_RESULT)
Timber.d("*** NET RESULT: ${sum?.value}") Timber.d("*** NET RESULT: ${sum?.value}")
val average = results.computedStat(Stat.AVERAGE) val average = results.computedStat(Stat.AVERAGE)

@ -81,7 +81,7 @@ class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() {
val results: ComputedResults = Calculator.compute(realm, group, options) val results: ComputedResults = Calculator.compute(realm, group, options)
val delta = 0.01 val delta = 0.01
val sum = results.computedStat(Stat.NETRESULT) val sum = results.computedStat(Stat.NET_RESULT)
if (sum != null) { if (sum != null) {
assertEquals(200.0, sum.value, delta) assertEquals(200.0, sum.value, delta)
} else { } else {
@ -250,7 +250,7 @@ class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() {
} }
val stats: List<Stat> = listOf(Stat.NETRESULT, Stat.AVERAGE) val stats: List<Stat> = listOf(Stat.NET_RESULT, Stat.AVERAGE)
val group = ComputableGroup("test", listOf(), stats) val group = ComputableGroup("test", listOf(), stats)
val options = Calculator.Options() val options = Calculator.Options()
@ -317,7 +317,7 @@ class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() {
realm.commitTransaction() realm.commitTransaction()
val stats: List<Stat> = listOf(Stat.NETRESULT, Stat.AVERAGE) val stats: List<Stat> = listOf(Stat.NET_RESULT, Stat.AVERAGE)
val group = ComputableGroup("test", listOf(), stats) val group = ComputableGroup("test", listOf(), stats)
val options = Calculator.Options() val options = Calculator.Options()
@ -400,7 +400,7 @@ class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() {
realm.commitTransaction() realm.commitTransaction()
val stats: List<Stat> = listOf(Stat.NETRESULT, Stat.AVERAGE) val stats: List<Stat> = listOf(Stat.NET_RESULT, Stat.AVERAGE)
val group = ComputableGroup("test", listOf(), stats) val group = ComputableGroup("test", listOf(), stats)
val options = Calculator.Options() val options = Calculator.Options()
@ -420,7 +420,7 @@ class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() {
s1.deleteFromRealm() s1.deleteFromRealm()
} }
val stats2: List<Stat> = listOf(Stat.NETRESULT, Stat.AVERAGE) val stats2: List<Stat> = listOf(Stat.NET_RESULT, Stat.AVERAGE)
val group2 = ComputableGroup("test", listOf(), stats2) val group2 = ComputableGroup("test", listOf(), stats2)
val results2: ComputedResults = Calculator.compute(realm, group2, options) val results2: ComputedResults = Calculator.compute(realm, group2, options)
@ -545,7 +545,7 @@ class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() {
s1.endDate = null s1.endDate = null
} }
val stats: List<Stat> = listOf(Stat.NETRESULT, Stat.AVERAGE) val stats: List<Stat> = listOf(Stat.NET_RESULT, Stat.AVERAGE)
val group = ComputableGroup("test", listOf(), stats) val group = ComputableGroup("test", listOf(), stats)
val options = Calculator.Options() val options = Calculator.Options()
@ -597,12 +597,12 @@ class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() {
} }
val stats: List<Stat> = listOf(Stat.NETRESULT) val stats: List<Stat> = listOf(Stat.NET_RESULT)
val group = ComputableGroup("test", listOf(), stats) val group = ComputableGroup("test", listOf(), stats)
val options = Calculator.Options() val options = Calculator.Options()
val results: ComputedResults = Calculator.compute(realm, group, options) val results: ComputedResults = Calculator.compute(realm, group, options)
val netResult = results.computedStat(Stat.NETRESULT) val netResult = results.computedStat(Stat.NET_RESULT)
assertEquals(250.0, netResult?.value) assertEquals(250.0, netResult?.value)
} }
@ -633,12 +633,12 @@ class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() {
} }
val stats: List<Stat> = listOf(Stat.NETRESULT) val stats: List<Stat> = listOf(Stat.NET_RESULT)
val group = ComputableGroup("test", listOf(), stats) val group = ComputableGroup("test", listOf(), stats)
val options = Calculator.Options() val options = Calculator.Options()
val results: ComputedResults = Calculator.compute(realm, group, options) val results: ComputedResults = Calculator.compute(realm, group, options)
val netResult = results.computedStat(Stat.NETRESULT) val netResult = results.computedStat(Stat.NET_RESULT)
assertEquals(250.0, netResult?.value) assertEquals(250.0, netResult?.value)
println("currency set rate real test starts here") println("currency set rate real test starts here")
@ -654,7 +654,7 @@ class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() {
val updatedGroup = ComputableGroup("test", listOf(), stats) val updatedGroup = ComputableGroup("test", listOf(), stats)
val updatedResults: ComputedResults = Calculator.compute(realm, updatedGroup, options) val updatedResults: ComputedResults = Calculator.compute(realm, updatedGroup, options)
val updatedNetResult = updatedResults.computedStat(Stat.NETRESULT) val updatedNetResult = updatedResults.computedStat(Stat.NET_RESULT)
assertEquals(650.0, updatedNetResult?.value) assertEquals(650.0, updatedNetResult?.value)
} }

@ -49,6 +49,11 @@
android:launchMode="singleTop" android:launchMode="singleTop"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity
android:name=".ui.activity.ReportDetailsActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity <activity
android:name=".ui.activity.CalendarDetailsActivity" android:name=".ui.activity.CalendarDetailsActivity"
android:launchMode="singleTop" android:launchMode="singleTop"

@ -27,7 +27,8 @@ class Calculator {
class Options( class Options(
display: Display = Display.TABLE, display: Display = Display.TABLE,
evolutionValues: EvolutionValues = EvolutionValues.NONE, evolutionValues: EvolutionValues = EvolutionValues.NONE,
stats: List<Stat> = listOf() stats: List<Stat> = listOf(),
aggregationType: AggregationType? = null
) { ) {
/** /**
@ -53,6 +54,7 @@ class Calculator {
var display: Display = display var display: Display = display
var evolutionValues: EvolutionValues = evolutionValues var evolutionValues: EvolutionValues = evolutionValues
var displayedStats: List<Stat> = stats var displayedStats: List<Stat> = stats
var aggregationType: AggregationType? = aggregationType
/** /**
* This function determines whether the standard deviation should be computed * This function determines whether the standard deviation should be computed
@ -80,21 +82,37 @@ class Calculator {
return this.displayedStats.contains(DAYS_PLAYED) return this.displayedStats.contains(DAYS_PLAYED)
} }
val shouldManageMultiGroupProgressValues: Boolean
get() {
if (this.aggregationType != null) {
return this.aggregationType == AggregationType.MONTH || this.aggregationType == AggregationType.YEAR
} else {
return false
}
}
} }
companion object { companion object {
fun computeStatsWithEvolutionByAggregationType( fun computeStatsWithEvolutionByAggregationType(
realm: Realm, realm: Realm,
stat: Stat,
group: ComputableGroup, group: ComputableGroup,
aggregationType: AggregationType aggregationType: AggregationType,
stats: List<Stat>? = null
): Report { ): Report {
val options = Options(evolutionValues = Options.EvolutionValues.STANDARD) val options = Options(evolutionValues = Options.EvolutionValues.STANDARD, aggregationType = aggregationType)
options.displayedStats = listOf(stat)
if (aggregationType == AggregationType.DURATION) { if (aggregationType == AggregationType.DURATION) {
options.evolutionValues = Options.EvolutionValues.TIMED options.evolutionValues = Options.EvolutionValues.TIMED
} }
stats?.let {
options.displayedStats = stats
}
return when (aggregationType) { return when (aggregationType) {
AggregationType.SESSION, AggregationType.DURATION -> this.computeGroups(realm, listOf(group), options) AggregationType.SESSION, AggregationType.DURATION -> this.computeGroups(realm, listOf(group), options)
AggregationType.MONTH -> { AggregationType.MONTH -> {
@ -116,6 +134,8 @@ class Calculator {
): Report { ): Report {
val computableGroups: MutableList<ComputableGroup> = mutableListOf() val computableGroups: MutableList<ComputableGroup> = mutableListOf()
var previousGroup: ComputableGroup? = null
comparators.combined().forEach { comparatorConditions -> comparators.combined().forEach { comparatorConditions ->
val allConditions = mutableListOf<QueryCondition>() val allConditions = mutableListOf<QueryCondition>()
@ -123,8 +143,10 @@ class Calculator {
allConditions.addAll(comparatorConditions) allConditions.addAll(comparatorConditions)
val group = ComputableGroup(allConditions.name(), allConditions) val group = ComputableGroup(allConditions.name(), allConditions)
group.comparedGroup = previousGroup
computableGroups.add(group) computableGroups.add(group)
previousGroup = group
} }
if (computableGroups.size == 0) { if (computableGroups.size == 0) {
@ -140,7 +162,7 @@ class Calculator {
*/ */
fun computeGroups(realm: Realm, groups: List<ComputableGroup>, options: Options): Report { fun computeGroups(realm: Realm, groups: List<ComputableGroup>, options: Options): Report {
val report = Report() val report = Report(options)
groups.forEach { group -> groups.forEach { group ->
val s = Date() val s = Date()
@ -151,7 +173,7 @@ class Calculator {
val results: ComputedResults = this.compute(realm, group, options = options) val results: ComputedResults = this.compute(realm, group, options = options)
// Computes the compared sessionGroup if existing // Computes the compared sessionGroup if existing
val comparedGroup = group.comparedComputables val comparedGroup = group.comparedGroup
if (comparedGroup != null) { if (comparedGroup != null) {
val comparedResults = this.compute(realm, comparedGroup, options = options) val comparedResults = this.compute(realm, comparedGroup, options = options)
group.comparedComputedResults = comparedResults group.comparedComputedResults = comparedResults
@ -175,29 +197,70 @@ class Calculator {
*/ */
fun compute(realm: Realm, computableGroup: ComputableGroup, options: Options): ComputedResults { fun compute(realm: Realm, computableGroup: ComputableGroup, options: Options): ComputedResults {
val results = ComputedResults(computableGroup, options.shouldManageMultiGroupProgressValues)
val computables = computableGroup.computables(realm) val computables = computableGroup.computables(realm)
Timber.d(">>>> Start computing group ${computableGroup.name}, ${computables.size} computables") Timber.d(">>>> Start computing group ${computableGroup.name}, ${computables.size} computables")
results.addStat(NUMBER_OF_GAMES, computables.size.toDouble())
if (options.computeLongestStreak) { if (options.computeLongestStreak) {
computables.sort("session.startDate") computables.sort("session.startDate")
} }
val results = ComputedResults(computableGroup)
val sum = computables.sum(ComputableResult.Field.RATED_NET.identifier).toDouble() val sum = computables.sum(ComputableResult.Field.RATED_NET.identifier).toDouble()
results.addStat(NET_RESULT, sum)
val totalHands = computables.sum(ComputableResult.Field.ESTIMATED_HANDS.identifier).toDouble() val totalHands = computables.sum(ComputableResult.Field.ESTIMATED_HANDS.identifier).toDouble()
results.addStat(HANDS_PLAYED, totalHands)
val bbSum = computables.sum(ComputableResult.Field.BB_NET.identifier).toDouble() val bbSum = computables.sum(ComputableResult.Field.BB_NET.identifier).toDouble()
results.addStat(BB_NET_RESULT, bbSum)
val bbSessionCount = computables.sum(ComputableResult.Field.HAS_BIG_BLIND.identifier).toInt() val bbSessionCount = computables.sum(ComputableResult.Field.HAS_BIG_BLIND.identifier).toInt()
results.addStat(BB_SESSION_COUNT, bbSessionCount.toDouble())
val winningSessionCount = computables.sum(ComputableResult.Field.IS_POSITIVE.identifier).toInt() val winningSessionCount = computables.sum(ComputableResult.Field.IS_POSITIVE.identifier).toInt()
results.addStat(WINNING_SESSION_COUNT, winningSessionCount.toDouble())
val totalBuyin = computables.sum(ComputableResult.Field.RATED_BUYIN.identifier).toDouble() val totalBuyin = computables.sum(ComputableResult.Field.RATED_BUYIN.identifier).toDouble()
results.addStat(TOTAL_BUYIN, totalBuyin)
val maxNetResult = computables.max(ComputableResult.Field.RATED_NET.identifier)?.toDouble() val maxNetResult = computables.max(ComputableResult.Field.RATED_NET.identifier)?.toDouble()
maxNetResult?.let {
results.addStat(MAXIMUM_NETRESULT, it)
}
val minNetResult = computables.min(ComputableResult.Field.RATED_NET.identifier)?.toDouble() val minNetResult = computables.min(ComputableResult.Field.RATED_NET.identifier)?.toDouble()
minNetResult?.let {
results.addStat(MINIMUM_NETRESULT, it)
}
Stat.netBBPer100Hands(bbSum, totalHands)?.let { netBB100 ->
results.addStat(NET_BB_PER_100_HANDS, netBB100)
}
Stat.returnOnInvestment(sum, totalBuyin)?.let { roi ->
results.addStat(ROI, roi)
}
if (options.computeLocationsPlayed) { if (options.computeLocationsPlayed) {
results.addStat(LOCATIONS_PLAYED, computables.distinctBy { it.session?.location?.id }.size.toDouble()) results.addStat(LOCATIONS_PLAYED, computables.distinctBy { it.session?.location?.id }.size.toDouble())
} }
var average = 0.0 // also used for standard deviation later
if (computables.size > 0) {
average = sum / computables.size.toDouble()
val winRatio = winningSessionCount.toDouble() / computables.size.toDouble()
val avgBuyin = totalBuyin / computables.size.toDouble()
results.addStats(
setOf(
ComputedStat(AVERAGE, average),
ComputedStat(WIN_RATIO, winRatio),
ComputedStat(AVERAGE_BUYIN, avgBuyin)
)
)
}
val shouldIterateOverComputables = val shouldIterateOverComputables =
(options.evolutionValues == Options.EvolutionValues.STANDARD || options.computeLongestStreak) (options.evolutionValues == Options.EvolutionValues.STANDARD || options.computeLongestStreak)
@ -242,7 +305,7 @@ class Calculator {
val session = val session =
computable.session ?: throw IllegalStateException("Computing lone ComputableResult") computable.session ?: throw IllegalStateException("Computing lone ComputableResult")
results.addEvolutionValue(tSum, stat = NETRESULT, data = session) results.addEvolutionValue(tSum, stat = NET_RESULT, data = session)
results.addEvolutionValue(tSum / index, stat = AVERAGE, data = session) results.addEvolutionValue(tSum / index, stat = AVERAGE, data = session)
results.addEvolutionValue(index.toDouble(), stat = NUMBER_OF_GAMES, data = session) results.addEvolutionValue(index.toDouble(), stat = NUMBER_OF_GAMES, data = session)
results.addEvolutionValue(tBBSum / tBBSessionCount, stat = AVERAGE_NET_BB, data = session) results.addEvolutionValue(tBBSum / tBBSessionCount, stat = AVERAGE_NET_BB, data = session)
@ -275,6 +338,7 @@ class Calculator {
} }
val sessionSets = computableGroup.sessionSets(realm) val sessionSets = computableGroup.sessionSets(realm)
results.addStat(NUMBER_OF_SETS, sessionSets.size.toDouble())
var gHourlyDuration: Double? = null var gHourlyDuration: Double? = null
var gBBSum: Double? = null var gBBSum: Double? = null
@ -343,7 +407,7 @@ class Calculator {
} }
Options.EvolutionValues.TIMED -> { Options.EvolutionValues.TIMED -> {
results.addEvolutionValue(tRatedNetSum, tHourlyDuration, NETRESULT, sessionSet) results.addEvolutionValue(tRatedNetSum, tHourlyDuration, NET_RESULT, sessionSet)
results.addEvolutionValue(tHourlyRate, tHourlyDuration, HOURLY_RATE, sessionSet) results.addEvolutionValue(tHourlyRate, tHourlyDuration, HOURLY_RATE, sessionSet)
results.addEvolutionValue( results.addEvolutionValue(
tIndex.toDouble(), tIndex.toDouble(),
@ -389,23 +453,7 @@ class Calculator {
} }
var average = 0.0
var hourlyRate = 0.0 var hourlyRate = 0.0
if (computables.size > 0) {
average = sum / computables.size.toDouble()
val winRatio = winningSessionCount.toDouble() / computables.size.toDouble()
val avgBuyin = totalBuyin / computables.size
results.addStats(
setOf(
ComputedStat(AVERAGE, average),
ComputedStat(WIN_RATIO, winRatio),
ComputedStat(AVERAGE_BUYIN, avgBuyin)
)
)
}
if (gHourlyDuration != null) { if (gHourlyDuration != null) {
hourlyRate = sum / gHourlyDuration hourlyRate = sum / gHourlyDuration
@ -424,29 +472,6 @@ class Calculator {
results.addStat(AVERAGE_NET_BB, gBBSum / bbSessionCount) results.addStat(AVERAGE_NET_BB, gBBSum / bbSessionCount)
} }
// Create stats
results.addStats(
setOf(
ComputedStat(NETRESULT, sum),
ComputedStat(NUMBER_OF_SETS, sessionSets.size.toDouble()),
ComputedStat(NUMBER_OF_GAMES, computables.size.toDouble()),
ComputedStat(HANDS_PLAYED, totalHands)
)
)
Stat.returnOnInvestment(sum, totalBuyin)?.let { roi ->
results.addStat(ROI, roi)
}
Stat.netBBPer100Hands(bbSum, totalHands)?.let { netBB100 ->
results.addStat(NET_BB_PER_100_HANDS, netBB100)
}
maxNetResult?.let { max ->
results.addStat(MAXIMUM_NETRESULT, max)
}
minNetResult?.let { min ->
results.addStat(MINIMUM_NETRESULT, min)
}
maxDuration?.let { maxd -> maxDuration?.let { maxd ->
results.addStat(MAXIMUM_DURATION, maxd) // (milliseconds to hours) results.addStat(MAXIMUM_DURATION, maxd) // (milliseconds to hours)
} }
@ -489,7 +514,6 @@ class Calculator {
} }
} }
class SSStats(sessionSet: SessionSet, conditions: List<QueryCondition>) { // Session Set Stats class SSStats(sessionSet: SessionSet, conditions: List<QueryCondition>) { // Session Set Stats

@ -14,59 +14,64 @@ import net.pokeranalytics.android.model.realm.SessionSet
/** /**
* The class returned after performing calculation in the Calculator object * The class returned after performing calculation in the Calculator object
*/ */
class Report() { class Report(var options: Calculator.Options) {
/**
* The mutable list of ComputedResults, one for each group of data
*/
private var _results: MutableList<ComputedResults> = mutableListOf() private var _results: MutableList<ComputedResults> = mutableListOf()
val results: List<ComputedResults> = this._results val results: List<ComputedResults> = this._results
// private var groups: MutableList<ComputableGroup> = mutableListOf() /**
// * Adds a new result to the list of ComputedResults
// var options: Calculator.Options = options */
//
// fun addGroup(group: ComputableGroup) {
// this.groups.add(group)
// }
//
// fun addGroups(groups: Collection<ComputableGroup>) {
// this.groups.addAll(groups)
// }
fun addResults(result: ComputedResults) { fun addResults(result: ComputedResults) {
this._results.add(result) this._results.add(result)
} }
/**
* Returns the list of entries corresponding to the provided [stat]
* One value will be returned by result
*/
fun lineEntries(stat: Stat): List<Entry> { fun lineEntries(stat: Stat): List<Entry> {
val entries = mutableListOf<Entry>() val entries = mutableListOf<Entry>()
this._results.forEachIndexed { index, results -> this._results.forEachIndexed { index, results ->
val cs = results.computedStat(stat) results.computedStat(stat)?.progressValue?.let { evoValue ->
cs?.let { computedStat -> entries.add(Entry(index.toFloat(), evoValue.toFloat(), results))
entries.add(Entry(index.toFloat(), computedStat.value.toFloat(), results))
} }
} }
return entries return entries
} }
fun barEntries(stat: Stat): List<Entry> { fun barEntries(stat: Stat? = null): List<Entry> {
val entries = mutableListOf<Entry>() val entries = mutableListOf<Entry>()
val statToUse = stat ?: options.displayedStats.firstOrNull()
this._results.forEachIndexed { index, results ->
val cs = results.computedStat(stat) statToUse?.let {
cs?.let { computedStat -> this._results.forEachIndexed { index, results ->
val barEntry = BarEntry(index.toFloat(), computedStat.value.toFloat(), results) val cs = results.computedStat(it)
entries.add(barEntry) cs?.let { computedStat ->
val barEntry = BarEntry(index.toFloat(), computedStat.value.toFloat(), results)
entries.add(barEntry)
}
} }
} }
return entries return entries
} }
fun multiLineEntries(stat: Stat): List<List<Entry>> { fun multiLineEntries(): List<List<Entry>> {
val entries = mutableListOf<List<Entry>>() val entries = mutableListOf<List<Entry>>()
this._results.forEach { result ->
val entryList = result.singleLineEntries(stat) options.displayedStats.forEach { stat ->
entries.add(entryList) this._results.forEach { result ->
val entryList = result.singleLineEntries(stat)
entries.add(entryList)
}
} }
return entries return entries
} }
@ -138,7 +143,7 @@ class ComputableGroup(name: String, conditions: List<QueryCondition> = listOf(),
/** /**
* A subgroup used to compute stat variation * A subgroup used to compute stat variation
*/ */
var comparedComputables: ComputableGroup? = null var comparedGroup: ComputableGroup? = null
/** /**
* The computed stats of the comparable sessionGroup * The computed stats of the comparable sessionGroup
@ -157,7 +162,7 @@ class ComputableGroup(name: String, conditions: List<QueryCondition> = listOf(),
} }
class ComputedResults(group: ComputableGroup) : StatEntry { class ComputedResults(group: ComputableGroup, shouldManageMultiGroupProgressValues: Boolean = false) : StatEntry {
/** /**
* The session group used to computed the stats * The session group used to computed the stats
@ -170,6 +175,8 @@ class ComputedResults(group: ComputableGroup) : StatEntry {
// The map containing all evolution numericValues for all stats // The map containing all evolution numericValues for all stats
private var _evolutionValues: MutableMap<Stat, MutableList<Point>> = mutableMapOf() private var _evolutionValues: MutableMap<Stat, MutableList<Point>> = mutableMapOf()
private var shouldManageMultiGroupProgressValues = shouldManageMultiGroupProgressValues
fun allStats(): Collection<ComputedStat> { fun allStats(): Collection<ComputedStat> {
return this._computedStats.values return this._computedStats.values
} }
@ -198,13 +205,109 @@ class ComputedResults(group: ComputableGroup) : StatEntry {
} }
fun addStat(stat: Stat, value: Double, secondValue: Double? = null) { fun addStat(stat: Stat, value: Double, secondValue: Double? = null) {
this._computedStats[stat] = ComputedStat(stat, value, secondValue = secondValue) val computedStat = ComputedStat(stat, value, secondValue = secondValue)
this.addComputedStat(computedStat)
} }
fun addStats(computedStats: Set<ComputedStat>) { fun addStats(computedStats: Set<ComputedStat>) {
computedStats.forEach { computedStats.forEach {
this._computedStats[it.stat] = it this.addComputedStat(it)
}
}
/**
* Adds a [computedStat] to the list of stats
* Also computes evolution values using the previously computed values
*/
private fun addComputedStat(computedStat: ComputedStat) {
if (this.shouldManageMultiGroupProgressValues) {
computedStat.progressValue = computedStat.value // useful for first occurence, otherwise overridden
// Computes the evolution value for the stat
this.group.comparedComputedResults?.let { result ->
result.computedStat(computedStat.stat)?.let { previousComputedStat ->
val previousValue = previousComputedStat.progressValue ?: previousComputedStat.value
when (computedStat.stat) {
Stat.NET_RESULT, Stat.DURATION, Stat.BB_NET_RESULT, Stat.BB_SESSION_COUNT,
Stat.WINNING_SESSION_COUNT, Stat.TOTAL_BUYIN, Stat.HANDS_PLAYED -> {
computedStat.progressValue = previousValue + computedStat.value
}
else -> {}
}
}
}
} }
this._computedStats[computedStat.stat] = computedStat
}
fun consolidateEvolutionStat() {
val netResult = this.computedStat(Stat.NET_RESULT)?.secondValue
val bbNetResult = this.computedStat(Stat.BB_NET_RESULT)?.secondValue
val duration = this.computedStat(Stat.DURATION)?.secondValue
val numberOfGames = this.computedStat(Stat.NUMBER_OF_GAMES)?.secondValue
val numberOfSets = this.computedStat(Stat.NUMBER_OF_SETS)?.secondValue
val handsPlayed = this.computedStat(Stat.HANDS_PLAYED)?.secondValue
val winningCount = this.computedStat(Stat.WINNING_SESSION_COUNT)?.secondValue
val bbSessionCount = this.computedStat(Stat.BB_SESSION_COUNT)?.secondValue
val totalBuyin = this.computedStat(Stat.TOTAL_BUYIN)?.secondValue
this.allStats().forEach { computedStat ->
when (computedStat.stat) {
Stat.HOURLY_RATE -> {
if (netResult != null && duration != null) {
computedStat.secondValue = netResult / duration
}
}
Stat.AVERAGE -> {
if (netResult != null && numberOfGames != null) {
computedStat.secondValue = netResult / numberOfGames
}
}
Stat.AVERAGE_DURATION -> {
if (duration != null && numberOfSets != null) {
computedStat.secondValue = duration / numberOfSets
}
}
Stat.NET_BB_PER_100_HANDS -> {
if (bbNetResult != null && handsPlayed != null) {
computedStat.secondValue = Stat.netBBPer100Hands(bbNetResult, handsPlayed)
}
}
Stat.HOURLY_RATE_BB -> {
if (bbNetResult != null && duration != null) {
computedStat.secondValue = bbNetResult / duration
}
}
Stat.AVERAGE_NET_BB -> {
if (bbNetResult != null && bbSessionCount != null) {
computedStat.secondValue = bbNetResult / bbSessionCount
}
}
Stat.WIN_RATIO -> {
if (winningCount != null && numberOfGames != null) {
computedStat.secondValue = winningCount / numberOfGames
}
}
Stat.AVERAGE_BUYIN -> {
if (totalBuyin != null && numberOfGames != null) {
computedStat.secondValue = totalBuyin / numberOfGames
}
}
Stat.ROI -> {
if (totalBuyin != null && netResult != null) {
computedStat.secondValue = Stat.returnOnInvestment(netResult, totalBuyin)
}
}
}
}
} }
fun computedStat(stat: Stat): ComputedStat? { fun computedStat(stat: Stat): ComputedStat? {
@ -222,6 +325,9 @@ class ComputedResults(group: ComputableGroup) : StatEntry {
} }
fun finalize(options: Calculator.Options) { fun finalize(options: Calculator.Options) {
this.consolidateEvolutionStat()
if (options.evolutionValues != Calculator.Options.EvolutionValues.NONE) { if (options.evolutionValues != Calculator.Options.EvolutionValues.NONE) {
// Sort points as a distribution // Sort points as a distribution
@ -234,13 +340,6 @@ class ComputedResults(group: ComputableGroup) : StatEntry {
} }
} }
/**
* Returns the number of computed stats
*/
fun numberOfStats(): Int {
return this._computedStats.size
}
// MPAndroidChart // MPAndroidChart
fun defaultStatEntries(stat: Stat): List<Entry> { fun defaultStatEntries(stat: Stat): List<Entry> {
@ -289,8 +388,9 @@ class ComputedResults(group: ComputableGroup) : StatEntry {
override val entryTitle: String = this.group.name override val entryTitle: String = this.group.name
override fun formattedValue(stat: Stat, context: Context): TextFormat { override fun formattedValue(stat: Stat, context: Context): TextFormat {
this.computedStat(stat)?.let { this.computedStat(stat)?.progressValue?.let {
return it.format(context) return stat.format(it, context = context)
// return it.format(context)
} ?: run { } ?: run {
throw IllegalStateException("Missing stat in results") throw IllegalStateException("Missing stat in results")
} }

@ -55,7 +55,8 @@ enum class AggregationType {
*/ */
enum class Stat : RowRepresentable { enum class Stat : RowRepresentable {
NETRESULT, NET_RESULT,
BB_NET_RESULT,
HOURLY_RATE, HOURLY_RATE,
AVERAGE, AVERAGE,
NUMBER_OF_SETS, NUMBER_OF_SETS,
@ -77,7 +78,10 @@ enum class Stat : RowRepresentable {
MAXIMUM_NETRESULT, MAXIMUM_NETRESULT,
MINIMUM_NETRESULT, MINIMUM_NETRESULT,
MAXIMUM_DURATION, MAXIMUM_DURATION,
DAYS_PLAYED DAYS_PLAYED,
WINNING_SESSION_COUNT,
BB_SESSION_COUNT,
TOTAL_BUYIN,
; ;
/** /**
@ -111,7 +115,8 @@ enum class Stat : RowRepresentable {
override val resId: Int? override val resId: Int?
get() { get() {
return when (this) { return when (this) {
NETRESULT -> R.string.net_result NET_RESULT -> R.string.net_result
BB_NET_RESULT -> R.string.total_net_result_bb_
HOURLY_RATE -> R.string.average_hour_rate HOURLY_RATE -> R.string.average_hour_rate
AVERAGE -> R.string.average AVERAGE -> R.string.average
NUMBER_OF_SETS -> R.string.number_of_sessions NUMBER_OF_SETS -> R.string.number_of_sessions
@ -134,6 +139,7 @@ enum class Stat : RowRepresentable {
MINIMUM_NETRESULT -> R.string.min_net_result MINIMUM_NETRESULT -> R.string.min_net_result
MAXIMUM_DURATION -> R.string.longest_session MAXIMUM_DURATION -> R.string.longest_session
DAYS_PLAYED -> R.string.days_played DAYS_PLAYED -> R.string.days_played
else -> throw IllegalStateException("Stat ${this.name} name required but undefined")
} }
} }
@ -149,7 +155,7 @@ enum class Stat : RowRepresentable {
when (this) { when (this) {
// Amounts + red/green // Amounts + red/green
NETRESULT, HOURLY_RATE, AVERAGE, MAXIMUM_NETRESULT, MINIMUM_NETRESULT -> { NET_RESULT, HOURLY_RATE, AVERAGE, MAXIMUM_NETRESULT, MINIMUM_NETRESULT -> {
val color = if (value >= this.threshold) R.color.green else R.color.red val color = if (value >= this.threshold) R.color.green else R.color.red
return TextFormat(value.toCurrency(currency), color) return TextFormat(value.toCurrency(currency), color)
} }
@ -193,7 +199,9 @@ enum class Stat : RowRepresentable {
val resId = when (this) { val resId = when (this) {
AVERAGE, AVERAGE_DURATION, NET_BB_PER_100_HANDS, AVERAGE, AVERAGE_DURATION, NET_BB_PER_100_HANDS,
HOURLY_RATE_BB, AVERAGE_NET_BB, ROI, WIN_RATIO, HOURLY_RATE -> R.string.average HOURLY_RATE_BB, AVERAGE_NET_BB, ROI, WIN_RATIO, HOURLY_RATE -> R.string.average
NETRESULT, DURATION -> R.string.total NUMBER_OF_SETS -> R.string.number_of_sessions
NUMBER_OF_GAMES -> R.string.number_of_records
NET_RESULT, DURATION -> R.string.total
STANDARD_DEVIATION -> R.string.net_result STANDARD_DEVIATION -> R.string.net_result
STANDARD_DEVIATION_HOURLY -> R.string.hour_rate_without_pauses STANDARD_DEVIATION_HOURLY -> R.string.hour_rate_without_pauses
STANDARD_DEVIATION_BB_PER_100_HANDS -> R.string.net_result_bb_per_100_hands STANDARD_DEVIATION_BB_PER_100_HANDS -> R.string.net_result_bb_per_100_hands
@ -217,7 +225,7 @@ enum class Stat : RowRepresentable {
val aggregationTypes: List<AggregationType> val aggregationTypes: List<AggregationType>
get() { get() {
return when (this) { return when (this) {
NETRESULT -> listOf(AggregationType.SESSION, AggregationType.MONTH, AggregationType.YEAR, AggregationType.DURATION) NET_RESULT -> listOf(AggregationType.SESSION, AggregationType.MONTH, AggregationType.YEAR, AggregationType.DURATION)
NUMBER_OF_GAMES, NUMBER_OF_SETS -> listOf(AggregationType.MONTH, AggregationType.YEAR) NUMBER_OF_GAMES, NUMBER_OF_SETS -> listOf(AggregationType.MONTH, AggregationType.YEAR)
else -> listOf(AggregationType.SESSION, AggregationType.MONTH, AggregationType.YEAR) else -> listOf(AggregationType.SESSION, AggregationType.MONTH, AggregationType.YEAR)
} }
@ -239,6 +247,14 @@ enum class Stat : RowRepresentable {
} }
} }
val shouldShowNumberOfSessions: Boolean
get() {
return when (this) {
NUMBER_OF_GAMES, NUMBER_OF_SETS -> false
else -> true
}
}
override val viewType: Int = RowViewType.TITLE_VALUE.ordinal override val viewType: Int = RowViewType.TITLE_VALUE.ordinal
} }
@ -253,6 +269,11 @@ class ComputedStat(var stat: Stat, var value: Double, var secondValue: Double? =
} }
} }
/**
* The value used to get evolution dataset
*/
var progressValue: Double? = null
/** /**
* The variation of the stat * The variation of the stat
*/ */

@ -583,7 +583,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
CustomizableRowRepresentable( CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG, RowViewType.HEADER_TITLE_AMOUNT_BIG,
title = getFormattedDuration(), title = getFormattedDuration(),
computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0, currency = currency) computedStat = ComputedStat(Stat.NET_RESULT, result?.net ?: 0.0, currency = currency)
) )
) )
rows.add(SeparatorRow()) rows.add(SeparatorRow())
@ -593,7 +593,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
CustomizableRowRepresentable( CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG, RowViewType.HEADER_TITLE_AMOUNT_BIG,
resId = R.string.pause, resId = R.string.pause,
computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0, currency = currency) computedStat = ComputedStat(Stat.NET_RESULT, result?.net ?: 0.0, currency = currency)
) )
) )
rows.add(SeparatorRow()) rows.add(SeparatorRow())
@ -603,7 +603,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
CustomizableRowRepresentable( CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG, RowViewType.HEADER_TITLE_AMOUNT_BIG,
title = getFormattedDuration(), title = getFormattedDuration(),
computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0, currency = currency) computedStat = ComputedStat(Stat.NET_RESULT, result?.net ?: 0.0, currency = currency)
) )
) )
rows.add( rows.add(
@ -926,7 +926,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
this.result?.let { result -> this.result?.let { result ->
val value: Double? = when (stat) { val value: Double? = when (stat) {
Stat.NETRESULT, Stat.AVERAGE, Stat.STANDARD_DEVIATION -> result.net Stat.NET_RESULT, Stat.AVERAGE, Stat.STANDARD_DEVIATION -> result.net
Stat.NUMBER_OF_GAMES, Stat.NUMBER_OF_SETS -> 1.0 Stat.NUMBER_OF_GAMES, Stat.NUMBER_OF_SETS -> 1.0
Stat.AVERAGE_BUYIN -> result.buyin Stat.AVERAGE_BUYIN -> result.buyin
Stat.ROI -> { Stat.ROI -> {

@ -120,7 +120,7 @@ open class SessionSet() : RealmObject(), Timed, Filterable {
override fun formattedValue(stat: Stat, context: Context) : TextFormat { override fun formattedValue(stat: Stat, context: Context) : TextFormat {
return when (stat) { return when (stat) {
Stat.NETRESULT, Stat.AVERAGE -> stat.format(this.ratedNet, currency = null, context = context) Stat.NET_RESULT, Stat.AVERAGE -> stat.format(this.ratedNet, currency = null, context = context)
Stat.DURATION, Stat.AVERAGE_DURATION -> stat.format(this.netDuration.toDouble(), currency = null, context = context) Stat.DURATION, Stat.AVERAGE_DURATION -> stat.format(this.netDuration.toDouble(), currency = null, context = context)
Stat.HOURLY_RATE -> stat.format(this.hourlyRate, currency = null, context = context) Stat.HOURLY_RATE -> stat.format(this.hourlyRate, currency = null, context = context)
Stat.HANDS_PLAYED -> stat.format(this.estimatedHands, currency = null, context = context) Stat.HANDS_PLAYED -> stat.format(this.estimatedHands, currency = null, context = context)

@ -73,6 +73,7 @@ class HomeActivity : PokerAnalyticsActivity() {
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menu?.clear()
menuInflater.inflate(R.menu.toolbar_home, menu) menuInflater.inflate(R.menu.toolbar_home, menu)
this.homeMenu = menu this.homeMenu = menu
//TODO: Change queryWith button visibility //TODO: Change queryWith button visibility

@ -0,0 +1,55 @@
package net.pokeranalytics.android.ui.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.fragment.ReportDetailsFragment
class ReportDetailsActivity : PokerAnalyticsActivity() {
companion object {
// Unparcel fails when setting a custom Parcelable object on Entry so we use a static reference to passe objects
private var report: Report? = null
private var reportTitle: String = ""
/**
* Default constructor
*/
fun newInstance(context: Context, report: Report, reportTitle: String) {
//parameters = GraphParameters(stat, group, report)
this.report = report
this.reportTitle = reportTitle
val intent = Intent(context, ReportDetailsActivity::class.java)
context.startActivity(intent)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_report_details)
initUI()
}
/**
* Init UI
*/
private fun initUI() {
report?.let {
val fragmentTransaction = supportFragmentManager.beginTransaction()
val reportDetailsFragment = ReportDetailsFragment.newInstance(it, reportTitle)
fragmentTransaction.add(R.id.reportDetailsContainer, reportDetailsFragment)
fragmentTransaction.commit()
report = null
}
}
}

@ -8,9 +8,12 @@ import net.pokeranalytics.android.calculus.ComputableGroup
import net.pokeranalytics.android.calculus.Report import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.fragment.GraphParameters import net.pokeranalytics.android.ui.fragment.StatisticDetailsFragment
class StatisticsDetailsParameters(var stat: Stat, var computableGroup: ComputableGroup, var report: Report) {
}
class StatisticDetailsActivity : PokerAnalyticsActivity() { class StatisticDetailsActivity : PokerAnalyticsActivity() {
private enum class IntentKey(val keyName: String) { private enum class IntentKey(val keyName: String) {
@ -21,14 +24,14 @@ class StatisticDetailsActivity : PokerAnalyticsActivity() {
companion object { companion object {
// Unparcel fails when setting a custom Parcelable object on Entry so we use a static reference to passe objects // Unparcel fails when setting a custom Parcelable object on Entry so we use a static reference to passe objects
var parameters: GraphParameters? = null private var parameters: StatisticsDetailsParameters? = null
var displayAggregationChoices: Boolean = true private var displayAggregationChoices: Boolean = true
/** /**
* Default constructor * Default constructor
*/ */
fun newInstance(context: Context, stat: Stat, group: ComputableGroup, report: Report, displayAggregationChoices: Boolean = true) { fun newInstance(context: Context, stat: Stat, group: ComputableGroup, report: Report, displayAggregationChoices: Boolean = true) {
parameters = GraphParameters(stat, group, report) parameters = StatisticsDetailsParameters(stat, group, report)
this.displayAggregationChoices = displayAggregationChoices this.displayAggregationChoices = displayAggregationChoices
val intent = Intent(context, StatisticDetailsActivity::class.java) val intent = Intent(context, StatisticDetailsActivity::class.java)
context.startActivity(intent) context.startActivity(intent)
@ -47,6 +50,16 @@ class StatisticDetailsActivity : PokerAnalyticsActivity() {
*/ */
private fun initUI() { private fun initUI() {
val fragmentTransaction = supportFragmentManager.beginTransaction()
val statisticDetailsFragment = StatisticDetailsFragment()
fragmentTransaction.add(R.id.statisticDetailsContainer, statisticDetailsFragment)
fragmentTransaction.commit()
parameters?.let {
statisticDetailsFragment.setData(it.stat, it.computableGroup, it.report, displayAggregationChoices)
parameters = null
}
} }
} }

@ -17,12 +17,6 @@ class HomePagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAda
override fun getItem(position: Int): PokerAnalyticsFragment { override fun getItem(position: Int): PokerAnalyticsFragment {
return when (position) { return when (position) {
//CLEAN
/*
0 -> HistoryFragment.newInstance()
1 -> StatsFragment.newInstance()
2 -> SettingsFragment.newInstance()
*/
0 -> HistoryFragment.newInstance() 0 -> HistoryFragment.newInstance()
1 -> StatsFragment.newInstance() 1 -> StatsFragment.newInstance()
2 -> CalendarFragment.newInstance() 2 -> CalendarFragment.newInstance()
@ -33,7 +27,7 @@ class HomePagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAda
} }
override fun getCount(): Int { override fun getCount(): Int {
return 5//3 return 5
} }
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) { override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
@ -49,12 +43,6 @@ class HomePagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAda
override fun getItemPosition(obj: Any): Int { override fun getItemPosition(obj: Any): Int {
return when (obj) { return when (obj) {
//CLEAN
/*
HistoryFragment::class.java -> 0
StatsFragment::class.java -> 1
SettingsFragment::class.java -> 2
*/
HistoryFragment::class.java -> 0 HistoryFragment::class.java -> 0
StatsFragment::class.java -> 1 StatsFragment::class.java -> 1
CalendarFragment::class.java -> 2 CalendarFragment::class.java -> 2

@ -0,0 +1,69 @@
package net.pokeranalytics.android.ui.adapter
import android.content.Context
import android.util.SparseArray
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.viewpager.widget.PagerAdapter
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.ui.fragment.GraphFragment
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import java.lang.ref.WeakReference
/**
* Home Adapter
*/
class ReportPagerAdapter(val context: Context, val fragmentManager: FragmentManager, val report: Report) : FragmentStatePagerAdapter(fragmentManager) {
var weakReferences = SparseArray<WeakReference<PokerAnalyticsFragment>>()
override fun getItem(position: Int): PokerAnalyticsFragment {
return when (position) {
0 -> GraphFragment.newInstance(report)
1 -> GraphFragment.newInstance(report)
2 -> GraphFragment.newInstance(report)
else -> GraphFragment.newInstance()
}
}
override fun getCount(): Int {
return 3
}
override fun getPageTitle(position: Int): CharSequence? {
return when(position) {
0 -> context.getString(R.string.bar)
1 -> context.getString(R.string.line)
2 -> context.getString(R.string.table)
else -> ""
}
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
super.destroyItem(container, position, `object`)
weakReferences.remove(position)
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val fragment = super.instantiateItem(container, position) as PokerAnalyticsFragment
weakReferences.put(position, WeakReference(fragment))
return fragment
}
override fun getItemPosition(obj: Any): Int {
return PagerAdapter.POSITION_UNCHANGED
}
/**
* Return the fragment at the position key
*/
fun getFragment(key: Int): PokerAnalyticsFragment? {
if (weakReferences.get(key) != null) {
return weakReferences.get(key).get()
}
return null
}
}

@ -52,7 +52,7 @@ class CalendarDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentable
private var sessionTypeCondition: QueryCondition? = null private var sessionTypeCondition: QueryCondition? = null
private var rowRepresentables: ArrayList<RowRepresentable> = ArrayList() private var rowRepresentables: ArrayList<RowRepresentable> = ArrayList()
//private var stat: Stat = Stat.NETRESULT //private var stat: Stat = Stat.NET_RESULT
//private var entries: List<Entry> = ArrayList() //private var entries: List<Entry> = ArrayList()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@ -183,14 +183,14 @@ class CalendarDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentable
rowRepresentables.clear() rowRepresentables.clear()
rowRepresentables.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.net_result)) rowRepresentables.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.net_result))
rowRepresentables.add(GraphRow(report, Stat.NETRESULT)) rowRepresentables.add(GraphRow(report, Stat.NET_RESULT))
rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.NETRESULT), it.computedStat(Stat.HOURLY_RATE))) rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.NET_RESULT), it.computedStat(Stat.HOURLY_RATE)))
rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.LOCATIONS_PLAYED), it.computedStat(Stat.LONGEST_STREAKS))) rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.LOCATIONS_PLAYED), it.computedStat(Stat.LONGEST_STREAKS)))
rowRepresentables.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.distribution)) rowRepresentables.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.distribution))
rowRepresentables.add(GraphRow(report, Stat.NETRESULT)) rowRepresentables.add(GraphRow(report, Stat.NET_RESULT))
rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.WIN_RATIO), it.computedStat(Stat.MAXIMUM_NETRESULT))) rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.WIN_RATIO), it.computedStat(Stat.MAXIMUM_NETRESULT)))
rowRepresentables.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.volume)) rowRepresentables.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.volume))
rowRepresentables.add(GraphRow(report, Stat.NETRESULT)) rowRepresentables.add(GraphRow(report, Stat.NET_RESULT))
rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.DURATION), it.computedStat(Stat.AVERAGE_DURATION))) rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.DURATION), it.computedStat(Stat.AVERAGE_DURATION)))
rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.DAYS_PLAYED), it.computedStat(Stat.MAXIMUM_DURATION))) rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.DAYS_PLAYED), it.computedStat(Stat.MAXIMUM_DURATION)))
} }

@ -68,7 +68,7 @@ class CalendarFragment : SessionObserverFragment(), CoroutineScope, StaticRowRep
private var sessionTypeCondition: QueryCondition? = null private var sessionTypeCondition: QueryCondition? = null
private var currentTimeFilter: TimeFilter = TimeFilter.MONTH private var currentTimeFilter: TimeFilter = TimeFilter.MONTH
private var currentStat = Stat.NETRESULT private var currentStat = Stat.NET_RESULT
// Life Cycle // Life Cycle
@ -132,7 +132,7 @@ class CalendarFragment : SessionObserverFragment(), CoroutineScope, StaticRowRep
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) { override fun onTabSelected(tab: TabLayout.Tab) {
when (tab.position) { when (tab.position) {
0 -> currentStat = Stat.NETRESULT 0 -> currentStat = Stat.NET_RESULT
1 -> currentStat = Stat.HOURLY_RATE 1 -> currentStat = Stat.HOURLY_RATE
2 -> currentStat = Stat.NUMBER_OF_GAMES 2 -> currentStat = Stat.NUMBER_OF_GAMES
3 -> currentStat = Stat.WIN_RATIO 3 -> currentStat = Stat.WIN_RATIO
@ -297,12 +297,12 @@ class CalendarFragment : SessionObserverFragment(), CoroutineScope, StaticRowRep
/* /*
Timber.d("========== YEAR x MONTH") Timber.d("========== YEAR x MONTH")
sortedMonthlyReports.keys.forEach { sortedMonthlyReports.keys.forEach {
Timber.d("$it => ${sortedMonthlyReports[it]?.computedStat(Stat.NETRESULT)?.value}") Timber.d("$it => ${sortedMonthlyReports[it]?.computedStat(Stat.NET_RESULT)?.value}")
} }
Timber.d("========== YEARLY") Timber.d("========== YEARLY")
sortedYearlyReports.keys.forEach { sortedYearlyReports.keys.forEach {
Timber.d("$it => ${sortedYearlyReports[it]?.computedStat(Stat.NETRESULT)?.value}") Timber.d("$it => ${sortedYearlyReports[it]?.computedStat(Stat.NET_RESULT)?.value}")
} }
*/ */

@ -10,10 +10,10 @@ import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.adapter.ComparisonChartPagerAdapter import net.pokeranalytics.android.ui.adapter.ComparisonChartPagerAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.extensions.toast
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.MoreTabRow import net.pokeranalytics.android.ui.view.rowrepresentable.MoreTabRow
import net.pokeranalytics.android.ui.extensions.toast
class ComparisonChartFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { class ComparisonChartFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
@ -55,6 +55,7 @@ class ComparisonChartFragment : PokerAnalyticsFragment(), StaticRowRepresentable
} }
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
menu?.clear()
inflater?.inflate(R.menu.toolbar_comparison_chart, menu) inflater?.inflate(R.menu.toolbar_comparison_chart, menu)
this.comparisonChartMenu = menu this.comparisonChartMenu = menu
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)

@ -52,6 +52,7 @@ open class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDele
} }
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
menu?.clear()
inflater?.inflate(R.menu.toolbar_editable_data, menu) inflater?.inflate(R.menu.toolbar_editable_data, menu)
this.editableMenu = menu this.editableMenu = menu
updateMenuUI() updateMenuUI()

@ -71,6 +71,7 @@ open class FiltersFragment : PokerAnalyticsFragment(), StaticRowRepresentableDat
} }
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
menu?.clear()
inflater?.inflate(R.menu.toolbar_editable_data, menu) inflater?.inflate(R.menu.toolbar_editable_data, menu)
this.filterMenu = menu this.filterMenu = menu
updateMenuUI() updateMenuUI()

@ -4,34 +4,25 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible
import com.github.mikephil.charting.charts.BarChart import com.github.mikephil.charting.charts.BarChart
import com.github.mikephil.charting.charts.BarLineChartBase import com.github.mikephil.charting.charts.BarLineChartBase
import com.github.mikephil.charting.charts.LineChart import com.github.mikephil.charting.charts.LineChart
import com.github.mikephil.charting.data.* import com.github.mikephil.charting.data.*
import com.github.mikephil.charting.highlight.Highlight import com.github.mikephil.charting.highlight.Highlight
import com.github.mikephil.charting.listener.OnChartValueSelectedListener import com.github.mikephil.charting.listener.OnChartValueSelectedListener
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import io.realm.Realm
import kotlinx.android.synthetic.main.fragment_evograph.* import kotlinx.android.synthetic.main.fragment_evograph.*
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.* import net.pokeranalytics.android.calculus.*
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.extensions.ChipGroupExtension
import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import net.pokeranalytics.android.ui.graph.PALineDataSet import net.pokeranalytics.android.ui.graph.PALineDataSet
import net.pokeranalytics.android.ui.graph.setStyle import net.pokeranalytics.android.ui.graph.setStyle
import net.pokeranalytics.android.ui.view.LegendView import net.pokeranalytics.android.ui.view.LegendView
import timber.log.Timber import timber.log.Timber
import java.util.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class GraphParameters(var stat: Stat, var computableGroup: ComputableGroup, var report: Report) {
}
class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, CoroutineScope { class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, CoroutineScope {
@ -40,8 +31,13 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co
/** /**
* Create new instance * Create new instance
*/ */
fun newInstance(): GraphFragment { fun newInstance(report: Report? = null): GraphFragment {
val fragment = GraphFragment() val fragment = GraphFragment()
report?.let {
fragment.selectedReport = it
}
val bundle = Bundle() val bundle = Bundle()
fragment.arguments = bundle fragment.arguments = bundle
return fragment return fragment
@ -49,15 +45,12 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co
} }
private lateinit var parentActivity: PokerAnalyticsActivity private lateinit var parentActivity: PokerAnalyticsActivity
private lateinit var computableGroup: ComputableGroup
private lateinit var selectedReport: Report private lateinit var selectedReport: Report
private lateinit var legendView: LegendView private lateinit var legendView: LegendView
private lateinit var chartView: BarLineChartBase<*> private lateinit var chartView: BarLineChartBase<*>
private var stat: Stat = Stat.NETRESULT private var stat: Stat = Stat.NET_RESULT
private var reports: MutableMap<AggregationType, Report> = hashMapOf() private var aggregationType: AggregationType = AggregationType.SESSION
private var aggregationTypes: List<AggregationType> = listOf()
private var displayAggregationChoices: Boolean = true
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
get() = Dispatchers.Main get() = Dispatchers.Main
@ -70,6 +63,7 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
initUI() initUI()
loadGraph()
} }
// OnChartValueSelectedListener // OnChartValueSelectedListener
@ -79,7 +73,6 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co
override fun onValueSelected(e: Entry?, h: Highlight?) { override fun onValueSelected(e: Entry?, h: Highlight?) {
e?.let { entry -> e?.let { entry ->
val statEntry = when (entry.data) { val statEntry = when (entry.data) {
is ObjectIdentifier -> { is ObjectIdentifier -> {
val identifier = entry.data as ObjectIdentifier val identifier = entry.data as ObjectIdentifier
@ -88,12 +81,11 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co
is StatEntry -> entry.data as StatEntry? is StatEntry -> entry.data as StatEntry?
else -> null else -> null
} }
statEntry?.let { statEntry?.let {
val formattedDate = it.entryTitle val formattedDate = it.entryTitle
val entryValue = it.formattedValue(this.stat, requireContext()) val entryValue = it.formattedValue(this.stat, requireContext())
val totalStatValue = this.stat.format(e.y.toDouble(), currency = null, context = requireContext()) val totalStatValue = this.stat.format(entry.y.toDouble(), currency = null, context = requireContext())
this.legendView.setItemData(this.stat, formattedDate, entryValue, totalStatValue) this.legendView.setItemData(this.stat, formattedDate, entryValue, totalStatValue)
} }
@ -105,6 +97,7 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co
* Init UI * Init UI
*/ */
private fun initUI() { private fun initUI() {
Timber.d("initUI")
parentActivity = activity as PokerAnalyticsActivity parentActivity = activity as PokerAnalyticsActivity
parentActivity.title = stat.localizedTitle(requireContext()) parentActivity.title = stat.localizedTitle(requireContext())
@ -119,78 +112,24 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co
this.chartView.setStyle(false, requireContext()) this.chartView.setStyle(false, requireContext())
this.chartContainer.addView(this.chartView) this.chartContainer.addView(this.chartView)
this.loadGraph(this.aggregationTypes.first(), this.selectedReport)
this.aggregationTypes.forEach { type ->
val chip = Chip(requireContext())
chip.id = type.ordinal
chip.text = requireContext().getString(type.resId)
chip.chipStartPadding = 8f.px
chip.chipEndPadding = 8f.px
this.chipGroup.addView(chip)
}
this.chipGroup.isVisible = displayAggregationChoices
this.chipGroup.check(this.stat.aggregationTypes.first().ordinal)
this.chipGroup.setOnCheckedChangeListener(object : ChipGroupExtension.SingleSelectionOnCheckedListener() {
override fun onCheckedChanged(group: ChipGroup, checkedId: Int) {
super.onCheckedChanged(group, checkedId)
val aggregationType = aggregationTypes[checkedId]
reports[aggregationType]?.let {
loadGraph(aggregationType, it)
} ?: run {
launchStatComputation(aggregationType)
}
}
})
} }
private fun launchStatComputation(aggregationType: AggregationType) { /**
* Load graph
GlobalScope.launch(coroutineContext) { */
private fun loadGraph() {
var r: Report? = null Timber.d("loadGraph")
val test = GlobalScope.async {
val s = Date()
Timber.d(">>> start...")
val realm = Realm.getDefaultInstance()
val report = Calculator.computeStatsWithEvolutionByAggregationType(realm, computableGroup, aggregationType)
reports[aggregationType] = report
r = report
realm.close()
val e = Date()
val duration = (e.time - s.time) / 1000.0
Timber.d(">>> ended in $duration seconds")
}
test.await()
if (!isDetached) { val graphEntries = when (aggregationType) {
r?.let { AggregationType.SESSION, AggregationType.DURATION -> selectedReport.results.firstOrNull()?.defaultStatEntries(stat)
loadGraph(aggregationType, it) AggregationType.MONTH, AggregationType.YEAR -> {
when (this.stat) {
Stat.NUMBER_OF_GAMES, Stat.NUMBER_OF_SETS -> selectedReport.barEntries(this.stat)
else -> selectedReport.lineEntries(this.stat)
} }
} }
} }
}
fun loadGraph(aggregationType: AggregationType, report: Report) {
val graphEntries = when (aggregationType) {
AggregationType.SESSION, AggregationType.DURATION -> report.results.firstOrNull()?.defaultStatEntries(stat)
AggregationType.MONTH, AggregationType.YEAR -> report.lineEntries(this.stat)
}
graphEntries?.let { entries -> graphEntries?.let { entries ->
this.legendView.prepareWithStat(this.stat, entries.size) this.legendView.prepareWithStat(this.stat, entries.size)
@ -228,15 +167,16 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co
/** /**
* Set data * Set data
*/ */
fun setData(stat: Stat, group: ComputableGroup, report: Report, displayAggregationChoices: Boolean = true) { fun setData(report: Report, aggregationType: AggregationType) {
this.stat = stat Timber.d("setData")
this.computableGroup = group
this.aggregationTypes = stat.aggregationTypes
this.reports[this.aggregationTypes.first()] = report
this.selectedReport = report this.selectedReport = report
this.displayAggregationChoices = displayAggregationChoices this.aggregationType = aggregationType
this.stat = report.options.displayedStats.first()
if (isAdded && !isDetached) {
loadGraph()
}
} }
} }

@ -0,0 +1,92 @@
package net.pokeranalytics.android.ui.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.tabs.TabLayout
import kotlinx.android.synthetic.main.fragment_report_details.*
import kotlinx.android.synthetic.main.fragment_statistic_details.toolbar
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.AggregationType
import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.adapter.ReportPagerAdapter
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
class ReportDetailsFragment : PokerAnalyticsFragment() {
companion object {
fun newInstance(report: Report?, reportTitle: String): ReportDetailsFragment {
val fragment = ReportDetailsFragment()
fragment.reportTitle = reportTitle
report?.let {
fragment.selectedReport = it
}
val bundle = Bundle()
fragment.arguments = bundle
return fragment
}
}
private lateinit var parentActivity: PokerAnalyticsActivity
private lateinit var selectedReport: Report
private var reports: MutableMap<AggregationType, Report> = hashMapOf()
private var stat: Stat = Stat.NET_RESULT
private var displayAggregationChoices: Boolean = true
private var reportTitle: String = ""
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_report_details, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initUI()
}
/**
* Init UI
*/
private fun initUI() {
parentActivity = activity as PokerAnalyticsActivity
// Avoid a bug during setting the title
toolbar.title = ""
parentActivity.setSupportActionBar(toolbar)
parentActivity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
setHasOptionsMenu(true)
toolbar.title = reportTitle
val reportPagerAdapter = ReportPagerAdapter(requireContext(), parentActivity.supportFragmentManager, selectedReport)
viewPager.adapter = reportPagerAdapter
viewPager.offscreenPageLimit = 3
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
viewPager.setCurrentItem(tab.position, false)
}
override fun onTabUnselected(tab: TabLayout.Tab) {
}
override fun onTabReselected(tab: TabLayout.Tab) {
}
})
}
/**
* Set data
*/
fun setData(report: Report) {
this.selectedReport = report
}
}

@ -5,8 +5,16 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.Realm
import kotlinx.android.synthetic.main.fragment_stats.* import kotlinx.android.synthetic.main.fragment_stats.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.comparison.Comparator
import net.pokeranalytics.android.ui.activity.ReportDetailsActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
@ -14,6 +22,8 @@ import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.ReportRow import net.pokeranalytics.android.ui.view.rowrepresentable.ReportRow
import timber.log.Timber import timber.log.Timber
import java.util.*
import kotlin.collections.ArrayList
class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
@ -59,12 +69,9 @@ class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSour
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
super.onRowSelected(position, row, fromAction) super.onRowSelected(position, row, fromAction)
Timber.d("row: $row") if (row is ReportRow) {
when (row) { val reportName = row.localizedTitle(requireContext())
ReportRow.DAY_OF_WEEKS -> { launchComputation(row.comparators, reportName)
//TODO: Open ComparisonChartActivity with correct data
//TODO: Calcul report before or after
}
} }
} }
@ -93,5 +100,29 @@ class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSour
} }
} }
/**
* Launch computation
*/
private fun launchComputation(comparators: List<Comparator>, reportName: String) {
GlobalScope.launch {
val startDate = Date()
val realm = Realm.getDefaultInstance()
val requiredStats: List<Stat> = listOf(Stat.NET_RESULT)
val options = Calculator.Options(evolutionValues = Calculator.Options.EvolutionValues.STANDARD, stats = requiredStats)
val report = Calculator.computeStatsWithComparators(realm, comparators = comparators, options = options)
Timber.d("launchComputation: ${System.currentTimeMillis() - startDate.time}ms")
launch(Dispatchers.Main) {
if (!isDetached) {
ReportDetailsActivity.newInstance(requireContext(), report, reportName)
}
}
}
}
} }

@ -63,6 +63,7 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate {
} }
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
menu?.clear()
inflater?.inflate(R.menu.toolbar_session, menu) inflater?.inflate(R.menu.toolbar_session, menu)
this.sessionMenu = menu this.sessionMenu = menu
updateMenuUI() updateMenuUI()

@ -6,13 +6,25 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.github.mikephil.charting.data.Entry import androidx.core.view.isVisible
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import io.realm.Realm
import kotlinx.android.synthetic.main.fragment_statistic_details.* import kotlinx.android.synthetic.main.fragment_statistic_details.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.*
import net.pokeranalytics.android.ui.activity.StatisticDetailsActivity
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.extensions.ChipGroupExtension
import net.pokeranalytics.android.ui.extensions.hideWithAnimation
import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.extensions.showWithAnimation
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import timber.log.Timber
import java.util.*
class StatisticDetailsFragment : PokerAnalyticsFragment() { class StatisticDetailsFragment : PokerAnalyticsFragment() {
@ -24,9 +36,13 @@ class StatisticDetailsFragment : PokerAnalyticsFragment() {
} }
private lateinit var parentActivity: PokerAnalyticsActivity private lateinit var parentActivity: PokerAnalyticsActivity
private lateinit var computableGroup: ComputableGroup
private lateinit var graphFragment: GraphFragment
private lateinit var selectedReport: Report
private var stat: Stat = Stat.NETRESULT private var reports: MutableMap<AggregationType, Report> = hashMapOf()
private var entries: List<Entry> = ArrayList() private var stat: Stat = Stat.NET_RESULT
private var displayAggregationChoices: Boolean = true
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_statistic_details, container, false) return inflater.inflate(R.layout.fragment_statistic_details, container, false)
@ -51,29 +67,99 @@ class StatisticDetailsFragment : PokerAnalyticsFragment() {
parentActivity.supportActionBar?.setDisplayHomeAsUpEnabled(true) parentActivity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
setHasOptionsMenu(true) setHasOptionsMenu(true)
toolbar.title = stat.localizedTitle(requireContext())
val fragmentManager = parentActivity.supportFragmentManager val fragmentManager = parentActivity.supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction() val fragmentTransaction = fragmentManager.beginTransaction()
val fragment = GraphFragment() graphFragment = GraphFragment()
fragmentTransaction.add(R.id.container, fragment) fragmentTransaction.add(R.id.graphContainer, graphFragment)
fragmentTransaction.commit() fragmentTransaction.commit()
StatisticDetailsActivity.parameters?.let {
fragment.setData(it.stat, it.computableGroup, it.report, StatisticDetailsActivity.displayAggregationChoices) stat.aggregationTypes.firstOrNull()?.let { aggregationType ->
StatisticDetailsActivity.parameters = null reports[aggregationType]?.let { report ->
} ?: run { graphFragment.setData(report, aggregationType)
throw Exception("Missing graph parameters") }
} }
toolbar.title = stat.localizedTitle(requireContext())
val aggregationTypes = stat.aggregationTypes
aggregationTypes.forEachIndexed { index, type ->
val chip = Chip(requireContext())
chip.id = index
chip.text = requireContext().getString(type.resId)
chip.chipStartPadding = 8f.px
chip.chipEndPadding = 8f.px
this.chipGroup.addView(chip)
}
this.chipGroup.isVisible = displayAggregationChoices
this.chipGroup.check(0)
this.chipGroup.setOnCheckedChangeListener(object : ChipGroupExtension.SingleSelectionOnCheckedListener() {
override fun onCheckedChanged(group: ChipGroup, checkedId: Int) {
super.onCheckedChanged(group, checkedId)
val aggregationType = aggregationTypes[checkedId]
reports[aggregationType]?.let { report ->
graphFragment.setData(report, aggregationType)
} ?: run {
launchStatComputation(aggregationType)
}
}
})
} }
/**
* Launch stat computation
*/
private fun launchStatComputation(aggregationType: AggregationType) {
graphContainer.hideWithAnimation()
progressBar.showWithAnimation()
//TODO: Create loader here
Timber.d("launchStatComputation: $aggregationType")
GlobalScope.launch {
val s = Date()
Timber.d(">>> start...")
val realm = Realm.getDefaultInstance()
val report = Calculator.computeStatsWithEvolutionByAggregationType(realm, stat, computableGroup, aggregationType)
reports[aggregationType] = report
realm.close()
val e = Date()
val duration = (e.time - s.time) / 1000.0
Timber.d(">>> ended in $duration seconds")
launch(Dispatchers.Main) {
graphFragment.setData(report, aggregationType)
progressBar.hideWithAnimation()
graphContainer.showWithAnimation()
}
}
}
/** /**
* Set data * Set data
*/ */
fun setData(stat: Stat, entries: List<Entry>) { fun setData(stat: Stat, computableGroup: ComputableGroup, report: Report, displayAggregationChoices: Boolean) {
this.stat = stat this.stat = stat
this.entries = entries this.computableGroup = computableGroup
this.selectedReport = report
this.displayAggregationChoices = displayAggregationChoices
stat.aggregationTypes.firstOrNull()?.let {
reports[it] = report
}
} }
} }

@ -137,14 +137,12 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
GlobalScope.launch(coroutineContext) { GlobalScope.launch(coroutineContext) {
var r = Report()
val test = GlobalScope.async { val test = GlobalScope.async {
val s = Date() val s = Date()
Timber.d(">>> start...") Timber.d(">>> start...")
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
r = createSessionGroupsAndStartCompute(realm) report = createSessionGroupsAndStartCompute(realm)
report = r
realm.close() realm.close()
val e = Date() val e = Date()
@ -155,7 +153,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
test.await() test.await()
if (!isDetached) { if (!isDetached) {
showResults(r) showResults()
} }
} }
@ -164,7 +162,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
private fun createSessionGroupsAndStartCompute(realm: Realm): Report { private fun createSessionGroupsAndStartCompute(realm: Realm): Report {
val allStats: List<Stat> = listOf( val allStats: List<Stat> = listOf(
Stat.NETRESULT, Stat.NET_RESULT,
Stat.HOURLY_RATE, Stat.HOURLY_RATE,
Stat.AVERAGE, Stat.AVERAGE,
Stat.NUMBER_OF_SETS, Stat.NUMBER_OF_SETS,
@ -173,7 +171,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
) )
val allSessionGroup = ComputableGroup(stringAll, listOf(), allStats) val allSessionGroup = ComputableGroup(stringAll, listOf(), allStats)
val cgStats: List<Stat> = listOf( val cgStats: List<Stat> = listOf(
Stat.NETRESULT, Stat.NET_RESULT,
Stat.HOURLY_RATE, Stat.HOURLY_RATE,
Stat.NET_BB_PER_100_HANDS, Stat.NET_BB_PER_100_HANDS,
Stat.HOURLY_RATE_BB, Stat.HOURLY_RATE_BB,
@ -185,7 +183,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
) )
val cgSessionGroup = ComputableGroup(stringCashGame, listOf(QueryCondition.CASH), cgStats) val cgSessionGroup = ComputableGroup(stringCashGame, listOf(QueryCondition.CASH), cgStats)
val tStats: List<Stat> = val tStats: List<Stat> =
listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.ROI, Stat.WIN_RATIO, Stat.NUMBER_OF_GAMES, Stat.AVERAGE_BUYIN) listOf(Stat.NET_RESULT, Stat.HOURLY_RATE, Stat.ROI, Stat.WIN_RATIO, Stat.NUMBER_OF_GAMES, Stat.AVERAGE_BUYIN)
val tSessionGroup = ComputableGroup(stringTournament, listOf(QueryCondition.TOURNAMENT), tStats) val tSessionGroup = ComputableGroup(stringTournament, listOf(QueryCondition.TOURNAMENT), tStats)
Timber.d(">>>>> Start computations...") Timber.d(">>>>> Start computations...")
@ -198,9 +196,11 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
} }
private fun showResults(report: Report) { private fun showResults() {
this.rowRepresentables = this.convertReportIntoRepresentables(report) report?.let {
statsAdapter.notifyDataSetChanged() this.rowRepresentables = this.convertReportIntoRepresentables(it)
statsAdapter.notifyDataSetChanged()
}
} }
private fun convertReportIntoRepresentables(report: Report): ArrayList<RowRepresentable> { private fun convertReportIntoRepresentables(report: Report): ArrayList<RowRepresentable> {
@ -239,7 +239,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
GlobalScope.launch(coroutineContext) { GlobalScope.launch(coroutineContext) {
var report = Report() var report: Report? = null
val test = GlobalScope.async { val test = GlobalScope.async {
val s = Date() val s = Date()
Timber.d(">>> start...") Timber.d(">>> start...")
@ -247,7 +247,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
val aggregationType = stat.aggregationTypes.first() val aggregationType = stat.aggregationTypes.first()
report = Calculator.computeStatsWithEvolutionByAggregationType(realm, computableGroup, aggregationType) report = Calculator.computeStatsWithEvolutionByAggregationType(realm, stat, computableGroup, aggregationType)
realm.close() realm.close()
@ -259,7 +259,9 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
test.await() test.await()
if (!isDetached) { if (!isDetached) {
StatisticDetailsActivity.newInstance(requireContext(), stat, computableGroup, report) report?.let {
StatisticDetailsActivity.newInstance(requireContext(), stat, computableGroup, it)
}
} }
} }

@ -57,7 +57,9 @@ class LegendView : FrameLayout {
this.stat2Name.isVisible = false this.stat2Name.isVisible = false
} }
this.counter.text = "$counter ${context.getString(R.string.sessions)}" val counterText = "$counter ${context.getString(R.string.sessions)}"
this.counter.text = counterText
this.counter.isVisible = stat.shouldShowNumberOfSessions
} }

@ -6,9 +6,9 @@ import android.view.MotionEvent
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
/** /**
* Poker Analytics ViewPager * ViewPager with paging disabled
*/ */
class HomeViewPager(context: Context, attrs: AttributeSet) : ViewPager(context, attrs) { class NoPagingViewPager(context: Context, attrs: AttributeSet) : ViewPager(context, attrs) {
var enablePaging: Boolean = false var enablePaging: Boolean = false

@ -149,7 +149,7 @@ class SessionRowView : FrameLayout {
rowHistorySession.infoTitle.isVisible = false rowHistorySession.infoTitle.isVisible = false
val result = session.result?.net ?: 0.0 val result = session.result?.net ?: 0.0
val formattedStat = ComputedStat(Stat.NETRESULT, result, currency = session.currency).format(context) val formattedStat = ComputedStat(Stat.NET_RESULT, result, currency = session.currency).format(context)
rowHistorySession.gameResult.setTextFormat(formattedStat, context) rowHistorySession.gameResult.setTextFormat(formattedStat, context)
} }

@ -63,7 +63,7 @@ class TransactionRowView : FrameLayout {
rowTransaction.transactionSubtitle.text = subtitle rowTransaction.transactionSubtitle.text = subtitle
// Amount // Amount
val formattedStat = ComputedStat(Stat.NETRESULT, transaction.amount).format(context) val formattedStat = ComputedStat(Stat.NET_RESULT, transaction.amount).format(context)
rowTransaction.transactionAmount.setTextFormat(formattedStat, context) rowTransaction.transactionAmount.setTextFormat(formattedStat, context)
} }

@ -1,6 +1,7 @@
package net.pokeranalytics.android.ui.view.rowrepresentable package net.pokeranalytics.android.ui.view.rowrepresentable
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.comparison.Comparator
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
@ -15,7 +16,7 @@ enum class ReportRow : RowRepresentable {
LOCATIONS, LOCATIONS,
NUMBER_OF_TABLES, NUMBER_OF_TABLES,
TOURNAMENT_TYPES, TOURNAMENT_TYPES,
VARIANT; GAME;
companion object { companion object {
@ -40,9 +41,24 @@ enum class ReportRow : RowRepresentable {
LOCATIONS -> R.string.locations LOCATIONS -> R.string.locations
NUMBER_OF_TABLES -> R.string.number_of_tables NUMBER_OF_TABLES -> R.string.number_of_tables
TOURNAMENT_TYPES -> R.string.tournament_type_complete TOURNAMENT_TYPES -> R.string.tournament_type_complete
VARIANT -> R.string.variant GAME -> R.string.game
} }
} }
override val viewType: Int = RowViewType.TITLE_ARROW.ordinal override val viewType: Int = RowViewType.TITLE_ARROW.ordinal
val comparators: List<Comparator>
get() {
return when (this) {
BLINDS -> listOf(Comparator.BLIND)
BUY_IN -> listOf(Comparator.TOURNAMENT_ENTRY_FEE)
DAY_OF_WEEKS -> listOf(Comparator.DAY_OF_WEEK)
GENERAL -> listOf(Comparator.CASH, Comparator.LIVE)
LOCATIONS -> listOf(Comparator.LOCATION)
NUMBER_OF_TABLES -> listOf() //TODO
TOURNAMENT_TYPES -> listOf(Comparator.TOURNAMENT_TYPE)
GAME -> listOf(Comparator.GAME)
}
}
} }

@ -15,7 +15,7 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:title="@string/app_name" /> app:title="@string/app_name" />
<net.pokeranalytics.android.ui.view.HomeViewPager <net.pokeranalytics.android.ui.view.NoPagingViewPager
android:id="@+id/viewPager" android:id="@+id/viewPager"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/reportDetailsContainer">
</FrameLayout>

@ -1,15 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/statisticDetailsContainer">
android:orientation="vertical">
<fragment </FrameLayout>
android:id="@+id/statisticDetailsFragment"
android:name="net.pokeranalytics.android.ui.fragment.StatisticDetailsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/fragment_statistic_details" />
</LinearLayout>

@ -64,10 +64,24 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/recyclerView" app:layout_constraintTop_toBottomOf="@+id/appBar"
tools:alpha="1" tools:alpha="1"
tools:visibility="visible" /> tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="@font/roboto"
android:text="@string/no_data_found"
android:textSize="18sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/recyclerView"
app:layout_constraintTop_toBottomOf="@+id/appBar" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="0dp" android:layout_width="0dp"

@ -19,32 +19,9 @@
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@id/chips"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/legendContainer" />
<FrameLayout
android:id="@+id/chips"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
tools:layout_height="60dp"> app:layout_constraintTop_toBottomOf="@+id/legendContainer" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal"
app:chipSpacingHorizontal="8dp"
app:singleSelection="true"
tools:layout_height="60dp" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:theme="@style/PokerAnalyticsTheme.Toolbar.Session"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:title="@string/app_name" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.tabs.TabItem
android:id="@+id/tabBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bar" />
<com.google.android.material.tabs.TabItem
android:id="@+id/tabLine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/line" />
<com.google.android.material.tabs.TabItem
android:id="@+id/tabTable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/table" />
</com.google.android.material.tabs.TabLayout>
</com.google.android.material.appbar.AppBarLayout>
<net.pokeranalytics.android.ui.view.NoPagingViewPager
android:id="@+id/viewPager"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBar" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -14,13 +14,54 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:title="@string/app_name" /> tools:title="@string/app_name" />
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:alpha="0"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/chips"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar"
tools:alpha="1"
tools:visibility="visible" />
<FrameLayout <FrameLayout
android:id="@+id/container" android:id="@+id/graphContainer"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toTopOf="@+id/chips"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar" /> app:layout_constraintTop_toBottomOf="@+id/toolbar" />
<FrameLayout
android:id="@+id/chips"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:layout_height="60dp">
<com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal"
app:chipSpacingHorizontal="8dp"
app:singleSelection="true"
tools:layout_height="60dp" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -1,7 +1,7 @@
<resources> <resources>
<!-- PokerAnalytics application theme --> <!-- PokerAnalytics application theme -->
<style name="PokerAnalyticsTheme" parent="Theme.MaterialComponents.NoActionBar"> <style name="PokerAnalyticsTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>

@ -39,7 +39,7 @@ class ExampleUnitTest : RealmUnitTest() {
// //
// val results: ComputedResults = Calculator.compute(group, Calculator.Options()) // val results: ComputedResults = Calculator.compute(group, Calculator.Options())
// //
// val sum = results.computedStat(Stat.NETRESULT) // val sum = results.computedStat(Stat.NET_RESULT)
// if (sum != null) { // if (sum != null) {
// assert(sum.value == 0.0) { "sum is ${sum.value}" } // assert(sum.value == 0.0) { "sum is ${sum.value}" }
// } else { // } else {

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.21' ext.kotlin_version = '1.3.30'
repositories { repositories {
google() google()
jcenter() jcenter()
@ -10,7 +10,7 @@ buildscript {
} }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.3.2' classpath 'com.android.tools.build:gradle:3.4.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'io.realm:realm-gradle-plugin:5.8.0' classpath 'io.realm:realm-gradle-plugin:5.8.0'
classpath 'com.google.gms:google-services:4.2.0' classpath 'com.google.gms:google-services:4.2.0'

@ -1,6 +1,6 @@
#Mon Feb 11 11:18:44 CET 2019 #Thu Apr 18 07:56:20 CEST 2019
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

Loading…
Cancel
Save