diff --git a/app/src/main/java/ani/dantotsu/profile/ChartBuilder.kt b/app/src/main/java/ani/dantotsu/profile/ChartBuilder.kt index f9a81ee4..1fcce464 100644 --- a/app/src/main/java/ani/dantotsu/profile/ChartBuilder.kt +++ b/app/src/main/java/ani/dantotsu/profile/ChartBuilder.kt @@ -18,7 +18,6 @@ import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAScrollablePlotAre import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAStyle import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAYAxis import com.github.aachartmodel.aainfographics.aatools.AAColor -import com.github.aachartmodel.aainfographics.aatools.AAGradientColor class ChartBuilder { companion object { @@ -37,7 +36,7 @@ class ChartBuilder { data class ChartPacket( val username: String, val names: List, - val statData: List + var statData: List ) fun buildChart( @@ -47,11 +46,12 @@ class ChartBuilder { statType: StatType, mediaType: MediaType, chartPackets: List, - xAxisName: String = "X Axis", + xAxisName: String, xAxisTickInterval: Int? = null, polar: Boolean = false, passedCategories: List? = null, scrollPos: Float? = null, + normalize: Boolean = false ): AAOptions { val typedValue = TypedValue() context.theme.resolveAttribute( @@ -69,6 +69,11 @@ class ChartBuilder { aaChartType = AAChartType.Column categories = chartPackets[0].names.map { it.toString() } } + if (normalize && chartPackets.size > 1) { + chartPackets.forEach { + it.statData = normalizeData(it.statData) + } + } val namesMax = chartPackets.maxOf { it.names.size } val palette = ColorEditor.generateColorPalette(primaryColor, namesMax) @@ -76,20 +81,25 @@ class ChartBuilder { ChartType.OneDimensional -> { val chart = AAChartModel() .chartType(aaChartType) - .subtitle(getTypeName(statType, mediaType)) + .subtitle( + getTypeName( + statType, + mediaType + ) + if (normalize && chartPackets.size > 1) " (Normalized)" else "" + ) .zoomType(AAChartZoomType.None) .dataLabelsEnabled(true) val elements: MutableList = mutableListOf() chartPackets.forEachIndexed { index, chartPacket -> - val element = AASeriesElement() - .name(chartPacket.username) - .data( - get1DElements( - chartPacket.names, - chartPacket.statData, - palette - ) + val element = AASeriesElement() + .name(chartPacket.username) + .data( + get1DElements( + chartPacket.names, + chartPacket.statData, + palette ) + ) if (index == 0) { element.color(primaryColor) } else { @@ -109,31 +119,66 @@ class ChartBuilder { palette.map { String.format("#%06X", 0xFFFFFF and it) }.toTypedArray() val chart = AAChartModel() .chartType(aaChartType) - .subtitle(getTypeName(statType, mediaType)) + .subtitle( + getTypeName( + statType, + mediaType + ) + if (normalize && chartPackets.size > 1) " (Normalized)" else "" + ) .zoomType(AAChartZoomType.None) .dataLabelsEnabled(false) - .yAxisTitle(getTypeName(statType, mediaType)) - if (chartPackets.size == 1) { - chart.colorsTheme(hexColorsArray) - } + .yAxisTitle( + getTypeName( + statType, + mediaType + ) + if (normalize && chartPackets.size > 1) " (Normalized)" else "" + ) + if (chartPackets.size == 1) { + chart.colorsTheme(hexColorsArray) + } val elements: MutableList = mutableListOf() chartPackets.forEachIndexed { index, chartPacket -> val element = get2DElements( - chartPacket.names, - chartPacket.statData, - chartPackets.size == 1 - ) + chartPacket.names, + chartPacket.statData, + chartPackets.size == 1 + ) element.name(chartPacket.username) if (index == 0) { - element.color(AAColor.rgbaColor(Color.red(primaryColor), Color.green(primaryColor), Color.blue(primaryColor), 0.9f)) + element.color( + AAColor.rgbaColor( + Color.red(primaryColor), + Color.green(primaryColor), + Color.blue(primaryColor), + 0.9f + ) + ) } else { - element.color(AAColor.rgbaColor(Color.red(ColorEditor.oppositeColor(primaryColor)), Color.green(ColorEditor.oppositeColor(primaryColor)), Color.blue(ColorEditor.oppositeColor(primaryColor)), 0.9f)) + element.color( + AAColor.rgbaColor( + Color.red( + ColorEditor.oppositeColor( + primaryColor + ) + ), + Color.green(ColorEditor.oppositeColor(primaryColor)), + Color.blue(ColorEditor.oppositeColor(primaryColor)), + 0.9f + ) + ) } if (chartPackets.size == 1) { - element.fillColor(AAColor.rgbaColor(Color.red(primaryColor), Color.green(primaryColor), Color.blue(primaryColor), 0.9f)) + element.fillColor( + AAColor.rgbaColor( + Color.red(primaryColor), + Color.green(primaryColor), + Color.blue(primaryColor), + 0.9f + ) + ) } elements.add(element) } @@ -205,12 +250,12 @@ class ChartBuilder { statValues.add(arrayOf(names[i], statData[i], statData[i])) } return AASeriesElement() - .data(statValues.toTypedArray()) - .dataLabels( - AADataLabels() - .enabled(false) - ) - .colorByPoint(colorByPoint) + .data(statValues.toTypedArray()) + .dataLabels( + AADataLabels() + .enabled(false) + ) + .colorByPoint(colorByPoint) } private fun get1DElements( @@ -255,6 +300,14 @@ class ChartBuilder { } } + private fun normalizeData(data: List): List { + if (data.isEmpty()) { + return data + } + val max = data.maxOf { it.toDouble() } + return data.map { (it.toDouble() / max) * 100 } + } + private fun setColors(aaOptions: AAOptions, context: Context, primaryColor: Int) { val backgroundColor = TypedValue() context.theme.resolveAttribute( @@ -281,7 +334,7 @@ class ChartBuilder { Color.red(colorOnBackground.data), Color.green(colorOnBackground.data), Color.blue(colorOnBackground.data), - 0.9f + 1.0f ) ) @@ -292,7 +345,7 @@ class ChartBuilder { Color.red(backgroundColor.data), Color.green(backgroundColor.data), Color.blue(backgroundColor.data), - 0.9f + 1.0f ) ) aaOptions.title?.style(onBackgroundStyle) diff --git a/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt b/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt index cb9943d4..bb245f47 100644 --- a/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt +++ b/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt @@ -12,10 +12,10 @@ import ani.dantotsu.R import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.api.Query import ani.dantotsu.databinding.FragmentStatisticsBinding -import ani.dantotsu.profile.ChartBuilder.Companion.ChartType -import ani.dantotsu.profile.ChartBuilder.Companion.StatType -import ani.dantotsu.profile.ChartBuilder.Companion.MediaType import ani.dantotsu.profile.ChartBuilder.Companion.ChartPacket +import ani.dantotsu.profile.ChartBuilder.Companion.ChartType +import ani.dantotsu.profile.ChartBuilder.Companion.MediaType +import ani.dantotsu.profile.ChartBuilder.Companion.StatType import com.github.aachartmodel.aainfographics.aachartcreator.AAChartType import com.xwray.groupie.GroupieAdapter import kotlinx.coroutines.Dispatchers @@ -147,7 +147,7 @@ class StatsFragment : private fun loadFormatChart(anime: Boolean) { val chartPackets = mutableListOf() - stats.forEach {stat -> + stats.forEach { stat -> val names: List = if (anime) { stat?.statistics?.anime?.formats?.map { it.format } ?: emptyList() } else { @@ -167,7 +167,7 @@ class StatsFragment : } ?: emptyList() } if (names.isNotEmpty() && values.isNotEmpty()) { - chartPackets.add(ChartPacket(stat?.name?:"Unknown", names, values)) + chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values)) } } if (chartPackets.isNotEmpty()) { @@ -178,6 +178,7 @@ class StatsFragment : statType, type, chartPackets, + xAxisName = "Format", ) adapter.add(ChartItem("Format", formatChart, activity)) } @@ -215,7 +216,8 @@ class StatsFragment : AAChartType.Funnel, statType, type, - chartPackets + chartPackets, + xAxisName = "Status", ) adapter.add(ChartItem("Status", statusChart, activity)) } @@ -225,9 +227,19 @@ class StatsFragment : val chartPackets = mutableListOf() stats.forEach { stat -> val names: List = if (anime) { - stat?.statistics?.anime?.scores?.map { convertScore(it.score, stat.mediaListOptions.scoreFormat) } ?: emptyList() + stat?.statistics?.anime?.scores?.map { + convertScore( + it.score, + stat.mediaListOptions.scoreFormat + ) + } ?: emptyList() } else { - stat?.statistics?.manga?.scores?.map { convertScore(it.score, stat.mediaListOptions.scoreFormat) } ?: emptyList() + stat?.statistics?.manga?.scores?.map { + convertScore( + it.score, + stat.mediaListOptions.scoreFormat + ) + } ?: emptyList() } val values: List = if (anime) { when (statType) { @@ -337,6 +349,7 @@ class StatsFragment : type, chartPackets, xAxisName = "Year", + scrollPos = 0.0f ) adapter.add(ChartItem("Release Year", releaseYearChart, activity)) } @@ -429,6 +442,7 @@ class StatsFragment : xAxisName = "Genre", polar = true, passedCategories = chartPackets[0].names as List, + normalize = true ) adapter.add(ChartItem("Genre", genreChart, activity)) } @@ -546,9 +560,11 @@ class StatsFragment : val chartPackets = mutableListOf() stats.forEach { stat -> val names: List = if (anime) { - stat?.statistics?.anime?.voiceActors?.map { it.voiceActor.name.full?:"unknown" } ?: emptyList() + stat?.statistics?.anime?.voiceActors?.map { it.voiceActor.name.full ?: "unknown" } + ?: emptyList() } else { - stat?.statistics?.manga?.voiceActors?.map { it.voiceActor.name.full?:"unknown" } ?: emptyList() + stat?.statistics?.manga?.voiceActors?.map { it.voiceActor.name.full ?: "unknown" } + ?: emptyList() } val values: List = if (anime) { when (statType) { @@ -644,7 +660,8 @@ class StatsFragment : xAxisName = "Studio", polar = true, passedCategories = chartPackets[0].names as List, - scrollPos = null + scrollPos = null, + normalize = true ) adapter.add(ChartItem("Studio", studioChart, activity)) } @@ -654,9 +671,11 @@ class StatsFragment : val chartPackets = mutableListOf() stats.forEach { stat -> val names: List = if (anime) { - stat?.statistics?.anime?.staff?.map { it.staff.name.full?:"unknown" } ?: emptyList() + stat?.statistics?.anime?.staff?.map { it.staff.name.full ?: "unknown" } + ?: emptyList() } else { - stat?.statistics?.manga?.staff?.map { it.staff.name.full?:"unknown" } ?: emptyList() + stat?.statistics?.manga?.staff?.map { it.staff.name.full ?: "unknown" } + ?: emptyList() } val values: List = if (anime) { when (statType) {