diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt index 4876b646..49fa3454 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt @@ -391,7 +391,7 @@ class ComputedResults(group: ComputableGroup, shouldManageMultiGroupProgressValu override fun formattedValue(stat: Stat, context: Context): TextFormat { this.computedStat(stat)?.let { - return it.format(context) + return it.format() } ?: run { throw IllegalStateException("Missing stat in results") } diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt index 4e1aea9b..c9f76c35 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt @@ -4,6 +4,7 @@ import android.content.Context import net.pokeranalytics.android.R import net.pokeranalytics.android.exceptions.FormattingException import net.pokeranalytics.android.model.interfaces.Timed +import net.pokeranalytics.android.ui.graph.AxisFormatting import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.util.NULL_TEXT @@ -40,14 +41,23 @@ enum class AggregationType { DURATION; val resId: Int - get() { - return when (this) { - SESSION -> R.string.session - MONTH -> R.string.month - YEAR -> R.string.year - DURATION -> R.string.duration + get() { + return when (this) { + SESSION -> R.string.session + MONTH -> R.string.month + YEAR -> R.string.year + DURATION -> R.string.duration + } } - } + + val axisFormatting: AxisFormatting + get() { + return when (this) { + DURATION -> AxisFormatting.X_DURATION + else -> AxisFormatting.DEFAULT + } + } + } /** @@ -147,7 +157,7 @@ enum class Stat : RowRepresentable { /** * Formats the value of the stat to be suitable for display */ - fun format(value: Double, secondValue: Double? = null, currency: Currency? = null, context: Context): TextFormat { + fun format(value: Double, secondValue: Double? = null, currency: Currency? = null): TextFormat { if (value.isNaN()) { return TextFormat(NULL_TEXT, R.color.white) @@ -195,10 +205,10 @@ enum class Stat : RowRepresentable { } - fun cumulativeLabelResId(context: Context) : String { + fun cumulativeLabelResId(context: Context): String { val resId = when (this) { 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 NUMBER_OF_SETS -> R.string.number_of_sessions NUMBER_OF_GAMES -> R.string.number_of_records NET_RESULT, DURATION -> R.string.total @@ -225,7 +235,12 @@ enum class Stat : RowRepresentable { val aggregationTypes: List get() { return when (this) { - NET_RESULT -> 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) else -> listOf(AggregationType.SESSION, AggregationType.MONTH, AggregationType.YEAR) } @@ -282,8 +297,8 @@ class ComputedStat(var stat: Stat, var value: Double, var secondValue: Double? = /** * Formats the value of the stat to be suitable for display */ - fun format(context: Context): TextFormat { - return this.stat.format(this.value, this.secondValue, this.currency, context) + fun format(): TextFormat { + return this.stat.format(this.value, this.secondValue, this.currency) } } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt index 9d983688..9e99cfcc 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt @@ -950,7 +950,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat } value?.let { - return stat.format(it, currency = currency, context = context) + return stat.format(it, currency = currency) } ?: run { return TextFormat(NULL_TEXT) } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt index 562724b8..e2777e86 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt @@ -120,15 +120,15 @@ open class SessionSet() : RealmObject(), Timed, Filterable { override fun formattedValue(stat: Stat, context: Context) : TextFormat { return when (stat) { - 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.HOURLY_RATE -> stat.format(this.hourlyRate, currency = null, context = context) - Stat.HANDS_PLAYED -> stat.format(this.estimatedHands, currency = null, context = context) - Stat.HOURLY_RATE_BB -> stat.format(this.bbHourlyRate, currency = null, context = context) + Stat.NET_RESULT, Stat.AVERAGE -> stat.format(this.ratedNet, currency = null) + Stat.DURATION, Stat.AVERAGE_DURATION -> stat.format(this.netDuration.toDouble(), currency = null) + Stat.HOURLY_RATE -> stat.format(this.hourlyRate, currency = null) + Stat.HANDS_PLAYED -> stat.format(this.estimatedHands, currency = null) + Stat.HOURLY_RATE_BB -> stat.format(this.bbHourlyRate, currency = null) Stat.NET_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION_BB_PER_100_HANDS -> { val netBBPer100Hands = Stat.netBBPer100Hands(this.bbNet, this.estimatedHands) if (netBBPer100Hands != null) { - return stat.format(this.estimatedHands, currency = null, context = context) + return stat.format(this.estimatedHands, currency = null) } else { return TextFormat(NULL_TEXT) } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt index 2492ed13..62fabad5 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt @@ -84,7 +84,7 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co val formattedDate = it.entryTitle val entryValue = it.formattedValue(this.stat, requireContext()) - val totalStatValue = this.stat.format(entry.y.toDouble(), currency = null, context = requireContext()) + val totalStatValue = this.stat.format(entry.y.toDouble(), currency = null) this.legendView.setItemData(this.stat, formattedDate, entryValue, totalStatValue) } @@ -108,7 +108,8 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co GraphType.BAR -> BarChart(context) } - this.chartView.setStyle(false, requireContext()) + val axisFormatting = aggregationType.axisFormatting + this.chartView.setStyle(false, axisFormatting, requireContext()) this.chartContainer.addView(this.chartView) } @@ -118,7 +119,10 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co private fun loadGraph() { val graphEntries = when (aggregationType) { - AggregationType.SESSION, AggregationType.DURATION -> selectedReport.results.firstOrNull()?.defaultStatEntries(stat) + AggregationType.SESSION -> selectedReport.results.firstOrNull()?.defaultStatEntries(stat) + AggregationType.DURATION -> { + selectedReport.results.firstOrNull()?.durationEntries(stat) + } AggregationType.MONTH, AggregationType.YEAR -> { when (this.stat) { Stat.NUMBER_OF_GAMES, Stat.NUMBER_OF_SETS -> selectedReport.barEntries(this.stat) @@ -153,7 +157,8 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co } } - this.chartView.setStyle(false, requireContext()) + val axisFormatting = aggregationType.axisFormatting + this.chartView.setStyle(false, axisFormatting, requireContext()) this.chartView.setOnChartValueSelectedListener(this) this.chartView.highlightValue((entries.size - 1).toFloat(), 0) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt index 2d28347e..94993c8a 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt @@ -77,7 +77,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc if (row is StatRow) { context?.let { context -> row.computedStat?.let { - dc.textFormat = it.format(context) + dc.textFormat = it.format() } } } @@ -87,7 +87,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc override fun statFormatForRow(row: RowRepresentable): TextFormat { if (row is StatRow) { context?.let { context -> - row.computedStat?.let { return it.format(context) } + row.computedStat?.let { return it.format() } } } return TextFormat(NULL_TEXT) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/graph/GraphExtensions.kt b/app/src/main/java/net/pokeranalytics/android/ui/graph/GraphExtensions.kt index 68aa648f..d667c35d 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/graph/GraphExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/graph/GraphExtensions.kt @@ -8,9 +8,16 @@ import com.github.mikephil.charting.components.XAxis import net.pokeranalytics.android.R import net.pokeranalytics.android.ui.extensions.px +enum class AxisFormatting { + DEFAULT, + X_DURATION, + Y_DURATION, +} +fun BarLineChartBase<*>.setStyle(small: Boolean, axisFormatting: AxisFormatting = AxisFormatting.DEFAULT, context: Context) { -fun BarLineChartBase<*>.setStyle(small: Boolean, context: Context) { + this.legend.isEnabled = false + this.description.isEnabled = false // X Axis this.xAxis.axisLineColor = ContextCompat.getColor(context, R.color.chart_default) @@ -42,20 +49,22 @@ fun BarLineChartBase<*>.setStyle(small: Boolean, context: Context) { this.axisLeft.typeface = ResourcesCompat.getFont(context, R.font.roboto_medium) this.axisLeft.labelCount = if (small) 1 else 7 // @todo not great if interval is [0..2] for number of records as we get decimals this.axisLeft.textSize = 12f + this.axisLeft.valueFormatter = LargeNumberFormatter() this.axisRight.isEnabled = false - this.legend.isEnabled = false - this.description.isEnabled = false this.data?.isHighlightEnabled = !small -// @todo -// if timeYAxis { -// this.axisLeft.valueFormatter = HourValueFormatter() -// } else { -// this.axisLeft.valueFormatter = LargeNumberFormatter() -// } -// - + when (axisFormatting) { + AxisFormatting.DEFAULT -> { + this.axisLeft.valueFormatter = LargeNumberFormatter() + } + AxisFormatting.X_DURATION -> { + this.xAxis.valueFormatter = HourFormatter() + } + AxisFormatting.Y_DURATION -> { + this.axisLeft.valueFormatter = HourFormatter() + } + } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/graph/LargeNumberFormatter.kt b/app/src/main/java/net/pokeranalytics/android/ui/graph/LargeNumberFormatter.kt new file mode 100644 index 00000000..9c6fa0f3 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/graph/LargeNumberFormatter.kt @@ -0,0 +1,32 @@ +package net.pokeranalytics.android.ui.graph + +import com.github.mikephil.charting.components.AxisBase +import com.github.mikephil.charting.formatter.ValueFormatter +import net.pokeranalytics.android.util.extensions.kmbFormatted + +class LargeNumberFormatter : ValueFormatter() { + + override fun getFormattedValue(value: Float): String { + return value.kmbFormatted + } + + override fun getAxisLabel(value: Float, axis: AxisBase?): String { + val test = value.kmbFormatted + return test + } + +} + +class HourFormatter : ValueFormatter() { + + override fun getFormattedValue(value: Float): String { + return value.kmbFormatted + "H" + } + + override fun getAxisLabel(value: Float, axis: AxisBase?): String { + val test = value.kmbFormatted + "H" + return test + } + +} + diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt index c36d8028..fd5040cc 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt @@ -22,6 +22,7 @@ import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Transaction import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.extensions.setTextFormat +import net.pokeranalytics.android.ui.graph.AxisFormatting import net.pokeranalytics.android.ui.graph.PALineDataSet import net.pokeranalytics.android.ui.graph.setStyle import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable @@ -135,7 +136,7 @@ enum class RowViewType(private var layoutRes: Int) { // Value itemView.findViewById(R.id.value)?.let { if (row.computedStat != null) { - val format = row.computedStat!!.format(itemView.context) + val format = row.computedStat!!.format() it.setTextFormat(format, itemView.context) } else if (row.value != null) { it.text = row.value @@ -278,7 +279,7 @@ enum class RowViewType(private var layoutRes: Int) { itemView.findViewById(R.id.stat1Value)?.let { view -> view.text = "" - row.computedStat1?.format(view.context)?.let { + row.computedStat1?.format()?.let { view.setTextFormat(it, itemView.context) } } @@ -293,7 +294,7 @@ enum class RowViewType(private var layoutRes: Int) { itemView.findViewById(R.id.stat2Value)?.let { view -> view.text = "" - row.computedStat2?.format(view.context)?.let { + row.computedStat2?.format()?.let { view.setTextFormat(it, itemView.context) } } @@ -349,7 +350,7 @@ enum class RowViewType(private var layoutRes: Int) { it.addView(chartView) } - chartView.setStyle(true, context) + chartView.setStyle(true, AxisFormatting.DEFAULT, context) chartView.setTouchEnabled(false) chartView.highlightValue((entries.size - 1).toFloat(), 0) } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/SessionRowView.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/SessionRowView.kt index 3fb91cac..9920cbee 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/SessionRowView.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/SessionRowView.kt @@ -149,7 +149,7 @@ class SessionRowView : FrameLayout { rowHistorySession.infoTitle.isVisible = false val result = session.result?.net ?: 0.0 - val formattedStat = ComputedStat(Stat.NET_RESULT, result, currency = session.currency).format(context) + val formattedStat = ComputedStat(Stat.NET_RESULT, result, currency = session.currency).format() rowHistorySession.gameResult.setTextFormat(formattedStat, context) } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/TransactionRowView.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/TransactionRowView.kt index aa3182b4..f9971876 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/TransactionRowView.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/TransactionRowView.kt @@ -63,7 +63,7 @@ class TransactionRowView : FrameLayout { rowTransaction.transactionSubtitle.text = subtitle // Amount - val formattedStat = ComputedStat(Stat.NET_RESULT, transaction.amount).format(context) + val formattedStat = ComputedStat(Stat.NET_RESULT, transaction.amount).format() rowTransaction.transactionAmount.setTextFormat(formattedStat, context) } diff --git a/app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt b/app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt index 5e2bb841..9566e07a 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt @@ -2,11 +2,31 @@ package net.pokeranalytics.android.util.extensions import android.content.Context import net.pokeranalytics.android.R -import net.pokeranalytics.android.util.UserDefaults +import java.lang.Math.abs import java.text.DecimalFormat import java.text.NumberFormat import java.util.* +val Number.kmbFormatted: String + get() { + var thousandsExponent = 0 + var v = this.toDouble() + while (abs(v) >= 10000 && thousandsExponent < 3) { + v /= 1000 + thousandsExponent++ + } + + val unit = when(thousandsExponent) { + 0 -> "" + 1 -> "K" + 2 -> "M" + 3 -> "B" + else -> "B+" // shouldn't happen + } + + val formatter = NumberFormat.getInstance() + return formatter.format(v) + unit + } // Double diff --git a/app/src/test/java/net/pokeranalytics/android/BasicUnitTest.kt b/app/src/test/java/net/pokeranalytics/android/BasicUnitTest.kt new file mode 100644 index 00000000..0a2424f2 --- /dev/null +++ b/app/src/test/java/net/pokeranalytics/android/BasicUnitTest.kt @@ -0,0 +1,34 @@ +package net.pokeranalytics.android + +import net.pokeranalytics.android.util.extensions.kmbFormatted +import org.junit.Assert +import org.junit.Test + +class BasicUnitTest : RealmUnitTest() { + + @Test + fun testStats() { + Assert.assertEquals(0, 0) + } + + @Test + fun testFormatting() { + + val n1 = 100.0 + val n2 = 1000.0 + val n3 = n2 * n2 // 1M + val n4 = n3 * n2 // 1B + + val s1 = n1.kmbFormatted + val s2 = n2.kmbFormatted + val s3 = n3.kmbFormatted + val s4 = n4.kmbFormatted + + Assert.assertEquals("100", s1) + Assert.assertEquals("1K", s2) + Assert.assertEquals("1M", s3) + Assert.assertEquals("1B", s4) + + } + +} diff --git a/app/src/test/java/net/pokeranalytics/android/ExampleUnitTest.kt b/app/src/test/java/net/pokeranalytics/android/ExampleUnitTest.kt deleted file mode 100644 index c2cb85b7..00000000 --- a/app/src/test/java/net/pokeranalytics/android/ExampleUnitTest.kt +++ /dev/null @@ -1,59 +0,0 @@ -package net.pokeranalytics.android - -import org.junit.Assert -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ - -class ExampleUnitTest : RealmUnitTest() { - - @Test - fun testStats() { - Assert.assertEquals(0, 0) - } - - -// class Grade(someValue: Double) : SessionInterface { -// -// override var bbPer100Hands: Double = 0.0 -// override var ratedNet: Double = 0.0 -// override var value: Double = someValue -// -// override var sessionSet: SessionSet? = SessionSet() -// override var estimatedHands: Double = 0.0 -// override var bbNet: Double = 0.0 -// override var bigBlindSessionCount: Int = 0 // 0 or 1 -// override var ratedBuyin: Double = 0.0 -// -// } -// -// @Test -// fun testStats() { -// -// val grades: List = listOf(Grade(10.0), Grade(20.0)) -// val group = ComputableGroup(name = "test", computables = grades) -// -// val results: ComputedResults = Calculator.compute(group, Calculator.Options()) -// -// val sum = results.computedStat(Stat.NET_RESULT) -// if (sum != null) { -// assert(sum.value == 0.0) { "sum is ${sum.value}" } -// } else { -// fail("No Net result stat") -// } -// -// val average = results.computedStat(Stat.AVERAGE) -// if (average != null) { -// assert(average.value == 0.0) { "average is ${average.value}" } -// } else { -// fail("No AVERAGE stat") -// } -// -// } - - -}