feat: more charts | code cleanup

This commit is contained in:
rebelonion 2024-03-04 00:02:41 -06:00
parent 2673b7d9bc
commit 7a1ed4f83e
9 changed files with 782 additions and 790 deletions

View file

@ -211,7 +211,7 @@ class Query {
val statistics: NNUserStatisticTypes,
@SerialName("siteUrl")
val siteUrl: String,
)
): java.io.Serializable
@Serializable
data class NNUserStatisticTypes(

View file

@ -0,0 +1,321 @@
package ani.dantotsu.profile
import android.content.Context
import android.graphics.Color
import android.util.TypedValue
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartModel
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartStackingType
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartType
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartZoomType
import com.github.aachartmodel.aainfographics.aachartcreator.AADataElement
import com.github.aachartmodel.aainfographics.aachartcreator.AAOptions
import com.github.aachartmodel.aainfographics.aachartcreator.AASeriesElement
import com.github.aachartmodel.aainfographics.aachartcreator.aa_toAAOptions
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AADataLabels
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAItemStyle
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAScrollablePlotArea
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAStyle
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAYAxis
import com.github.aachartmodel.aainfographics.aatools.AAColor
class ChartBuilder {
companion object {
enum class ChartType {
OneDimensional, TwoDimensional
}
enum class StatType {
COUNT, TIME, MEAN_SCORE
}
enum class MediaType {
ANIME, MANGA
}
fun buildChart(
context: Context,
chartType: ChartType,
aaChartType: AAChartType,
statType: StatType,
mediaType: MediaType,
names: List<Any>,
statData: List<Number>,
xAxisName: String = "X Axis",
xAxisTickInterval: Int? = null,
polar: Boolean = false,
categories: List<String>? = null,
scrollPos: Float? = null,
): AAOptions {
val typedValue = TypedValue()
context.theme.resolveAttribute(
com.google.android.material.R.attr.colorPrimary,
typedValue,
true
)
val primaryColor = typedValue.data
val palette = generateColorPalette(primaryColor, names.size)
val aaChartModel = when (chartType) {
ChartType.OneDimensional -> {
val chart = AAChartModel()
.chartType(aaChartType)
.subtitle(getTypeName(statType, mediaType))
.zoomType(AAChartZoomType.None)
.dataLabelsEnabled(true)
.series(
get1DElements(
names,
statData,
palette,
primaryColor
)
)
xAxisTickInterval?.let { chart.xAxisTickInterval(it) }
categories?.let { chart.categories(it.toTypedArray()) }
chart
}
ChartType.TwoDimensional -> {
val hexColorsArray: Array<Any> =
palette.map { String.format("#%06X", 0xFFFFFF and it) }.toTypedArray()
val chart = AAChartModel()
.chartType(aaChartType)
.subtitle(getTypeName(statType, mediaType))
.zoomType(AAChartZoomType.None)
.dataLabelsEnabled(false)
.yAxisTitle(getTypeName(statType, mediaType))
.stacking(AAChartStackingType.Normal)
.series(get2DElements(names, statData, primaryColor))
.colorsTheme(hexColorsArray)
xAxisTickInterval?.let { chart.xAxisTickInterval(it) }
categories?.let { chart.categories(it.toTypedArray()) }
chart
}
}
val aaOptions = aaChartModel.aa_toAAOptions()
aaOptions.chart?.polar = polar
aaOptions.tooltip?.apply {
headerFormat
formatter(
getToolTipFunction(
chartType,
xAxisName,
getTypeName(statType, mediaType)
)
)
}
aaOptions.legend?.apply {
enabled(true)
.labelFormat = "{name}: {y}"
}
aaOptions.plotOptions?.series?.connectNulls(true)
aaOptions.chart?.panning = true
scrollPos?.let {
aaOptions.chart?.scrollablePlotArea(AAScrollablePlotArea().scrollPositionX(scrollPos))
aaOptions.chart?.scrollablePlotArea?.minWidth((context.resources.displayMetrics.widthPixels.toFloat() / context.resources.displayMetrics.density) * (names.size.toFloat() / 18.0f))
}
val min = statData.minOfOrNull { it.toDouble() } ?: 0.0
val max = statData.maxOfOrNull { it.toDouble() } ?: 0.0
val aaYaxis = AAYAxis().min(min).max(max)
val tickInterval = when (max) {
in 0.0..10.0 -> 1.0
in 10.0..30.0 -> 5.0
in 30.0..100.0 -> 10.0
in 100.0..1000.0 -> 100.0
in 1000.0..10000.0 -> 1000.0
else -> 10000.0
}
aaYaxis.tickInterval(tickInterval)
aaOptions.yAxis(aaYaxis)
setColors(aaOptions, context, primaryColor)
return aaOptions
}
private fun get2DElements(
names: List<Any>,
statData: List<Any>,
primaryColor: Int
): Array<Any> {
val statValues = mutableListOf<Array<Any>>()
for (i in statData.indices) {
statValues.add(arrayOf(names[i], statData[i], statData[i]))
}
return arrayOf(
AASeriesElement().name("Score")
.data(statValues.toTypedArray())
.dataLabels(
AADataLabels()
.enabled(false)
)
.colorByPoint(true)
.fillColor(AAColor.rgbaColor(
Color.red(primaryColor),
Color.green(primaryColor),
Color.blue(primaryColor),
0.9f
))
)
}
private fun get1DElements(
names: List<Any>,
statData: List<Number>,
colors: List<Int>,
primaryColor: Int
): Array<Any> {
val statDataElements = mutableListOf<AADataElement>()
for (i in statData.indices) {
val element = AADataElement()
.y(statData[i])
.color(
AAColor.rgbaColor(
Color.red(colors[i]),
Color.green(colors[i]),
Color.blue(colors[i]),
0.9f
)
)
if (names[i] is Number) {
element.x(names[i] as Number)
element.dataLabels(
AADataLabels()
.enabled(false)
.format("{point.y}")
.backgroundColor(AAColor.rgbaColor(255, 255, 255, 0.0f))
)
} else {
element.x(i)
element.name(names[i] as String)
}
statDataElements.add(element)
}
return arrayOf(
AASeriesElement().name("Score").color(primaryColor)
.data(statDataElements.toTypedArray())
)
}
private fun getTypeName(statType: StatType, mediaType: MediaType): String {
return when (statType) {
StatType.COUNT -> "Count"
StatType.TIME -> if (mediaType == MediaType.ANIME) "Hours Watched" else "Chapters Read"
StatType.MEAN_SCORE -> "Mean Score"
}
}
private fun generateColorPalette(
baseColor: Int,
size: Int,
hueDelta: Float = 8f,
saturationDelta: Float = 2.02f,
valueDelta: Float = 2.02f
): List<Int> {
val palette = mutableListOf<Int>()
val hsv = FloatArray(3)
Color.colorToHSV(baseColor, hsv)
for (i in 0 until size) {
val newHue =
(hsv[0] + hueDelta * i) % 360 // Ensure hue stays within the 0-360 range
val newSaturation = (hsv[1] + saturationDelta * i).coerceIn(0f, 1f)
val newValue = (hsv[2] + valueDelta * i).coerceIn(0f, 1f)
val newHsv = floatArrayOf(newHue, newSaturation, newValue)
palette.add(Color.HSVToColor(newHsv))
}
return palette
}
private fun setColors(aaOptions: AAOptions, context: Context, primaryColor: Int) {
val backgroundColor = TypedValue()
context.theme.resolveAttribute(
com.google.android.material.R.attr.colorSurfaceVariant,
backgroundColor,
true
)
val backgroundStyle = AAStyle().color(
AAColor.rgbaColor(
Color.red(backgroundColor.data),
Color.green(backgroundColor.data),
Color.blue(backgroundColor.data),
1f
)
)
val colorOnBackground = TypedValue()
context.theme.resolveAttribute(
com.google.android.material.R.attr.colorOnSurface,
colorOnBackground,
true
)
val onBackgroundStyle = AAStyle().color(
AAColor.rgbaColor(
Color.red(colorOnBackground.data),
Color.green(colorOnBackground.data),
Color.blue(colorOnBackground.data),
0.9f
)
)
aaOptions.chart?.backgroundColor(backgroundStyle.color)
aaOptions.tooltip?.backgroundColor(
AAColor.rgbaColor(
Color.red(primaryColor),
Color.green(primaryColor),
Color.blue(primaryColor),
0.9f
)
)
aaOptions.title?.style(onBackgroundStyle)
aaOptions.subtitle?.style(onBackgroundStyle)
aaOptions.tooltip?.style(backgroundStyle)
aaOptions.credits?.style(onBackgroundStyle)
aaOptions.xAxis?.labels?.style(onBackgroundStyle)
aaOptions.yAxis?.labels?.style(onBackgroundStyle)
aaOptions.plotOptions?.series?.dataLabels?.style(onBackgroundStyle)
aaOptions.plotOptions?.series?.dataLabels?.backgroundColor(backgroundStyle.color)
aaOptions.legend?.itemStyle(AAItemStyle().color(onBackgroundStyle.color))
aaOptions.touchEventEnabled(true)
}
private fun getToolTipFunction(
chartType: ChartType,
type: String,
typeName: String
): String {
return when (chartType) {
ChartType.OneDimensional -> {
"""
function () {
return this.point.name
+ ': <br/> '
+ '<b> '
+ this.y
+ ', '
+ (this.percentage).toFixed(2)
+ '%'
}
""".trimIndent()
}
ChartType.TwoDimensional -> {
"""
function () {
return '$type: ' +
this.x +
'<br/> ' +
' $typeName ' +
this.y
}
""".trimIndent()
}
}
}
}
}

View file

@ -0,0 +1,26 @@
package ani.dantotsu.profile
import android.view.View
import ani.dantotsu.R
import ani.dantotsu.databinding.ItemChartBinding
import com.github.aachartmodel.aainfographics.aachartcreator.AAOptions
import com.xwray.groupie.viewbinding.BindableItem
class ChartItem(
private val title: String,
private val aaOptions: AAOptions): BindableItem<ItemChartBinding>() {
private lateinit var binding: ItemChartBinding
override fun bind(viewBinding: ItemChartBinding, position: Int) {
binding = viewBinding
binding.typeText.text = title
binding.chartView.aa_drawChartWithChartOptions(aaOptions)
}
override fun getLayout(): Int {
return R.layout.item_chart
}
override fun initializeViewBinding(view: View): ItemChartBinding {
return ItemChartBinding.bind(view)
}
}

View file

@ -13,6 +13,7 @@ import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.Query
@ -39,6 +40,7 @@ class ProfileActivity : AppCompatActivity(){
private lateinit var binding: ActivityProfileBinding
private var selected: Int = 0
private lateinit var navBar: AnimatedBottomBar
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -65,7 +67,7 @@ class ProfileActivity : AppCompatActivity(){
}
withContext(Dispatchers.Main) {
binding.profileViewPager.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin = navBarHeight }
binding.profileViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, user, this@ProfileActivity)
binding.profileViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, user)
navBar.visibility = View.VISIBLE
navBar.selectTabAt(selected)
navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener {
@ -129,20 +131,18 @@ class ProfileActivity : AppCompatActivity(){
}
super.onResume()
}
private class ViewPagerAdapter(
fragmentManager: FragmentManager,
lifecycle: Lifecycle,
private val user: Query.UserProfile,
private val activity: ProfileActivity
private val user: Query.UserProfile
) :
FragmentStateAdapter(fragmentManager, lifecycle) {
override fun getItemCount(): Int = 2
override fun createFragment(position: Int): Fragment = when (position) {
0 -> ProfileFragment(user, activity)
1 -> StatsFragment(user, activity)
else -> ProfileFragment(user, activity)
0 -> ProfileFragment.newInstance(user)
1 -> StatsFragment.newInstance(user)
else -> ProfileFragment.newInstance(user)
}
}
}

View file

@ -13,7 +13,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.bottomBar
import ani.dantotsu.buildMarkwon
import ani.dantotsu.connections.anilist.ProfileViewModel
import ani.dantotsu.connections.anilist.api.Query
@ -27,8 +26,10 @@ import ani.dantotsu.setSlideUp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class ProfileFragment(private val user: Query.UserProfile, private val activity: ProfileActivity): Fragment() {
class ProfileFragment(): Fragment() {
lateinit var binding: FragmentProfileBinding
private lateinit var activity: ProfileActivity
private lateinit var user: Query.UserProfile
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -41,6 +42,8 @@ class ProfileFragment(private val user: Query.UserProfile, private val activity:
val model: ProfileViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity = requireActivity() as ProfileActivity
user = arguments?.getSerializable("user") as Query.UserProfile
val markwon = buildMarkwon(activity, false)
markwon.setMarkdown(binding.profileUserBio, user.about?:"")
binding.userInfoContainer.visibility = if (user.about != null) View.VISIBLE else View.GONE
@ -96,6 +99,14 @@ class ProfileFragment(private val user: Query.UserProfile, private val activity:
binding.profileFavManga
)
}
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
binding.root.requestLayout()
}
}
private fun initRecyclerView(
mode: LiveData<ArrayList<Media>>,
container: View,
@ -135,4 +146,15 @@ class ProfileFragment(private val user: Query.UserProfile, private val activity:
}
}
companion object {
fun newInstance(query: Query.UserProfile): ProfileFragment {
val args = Bundle().apply {
putSerializable("user", query)
}
return ProfileFragment().apply {
arguments = args
}
}
}
}

View file

@ -1,48 +1,37 @@
package ani.dantotsu.profile
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.Query
import ani.dantotsu.databinding.FragmentStatisticsBinding
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartModel
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartStackingType
import ani.dantotsu.profile.ChartBuilder.Companion.ChartType
import ani.dantotsu.profile.ChartBuilder.Companion.StatType
import ani.dantotsu.profile.ChartBuilder.Companion.MediaType
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartType
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartZoomType
import com.github.aachartmodel.aainfographics.aachartcreator.AADataElement
import com.github.aachartmodel.aainfographics.aachartcreator.AAOptions
import com.github.aachartmodel.aainfographics.aachartcreator.AASeriesElement
import com.github.aachartmodel.aainfographics.aachartcreator.aa_toAAOptions
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAArea
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAChart
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AADataLabels
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAItemStyle
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAMarker
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAPlotOptions
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAStyle
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAYAxis
import com.github.aachartmodel.aainfographics.aatools.AAColor
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.Locale
class StatsFragment(private val user: Query.UserProfile, private val activity: ProfileActivity) :
class StatsFragment() :
Fragment() {
private lateinit var binding: FragmentStatisticsBinding
private var adapter: GroupieAdapter = GroupieAdapter()
private var stats: Query.StatisticsResponse? = null
private var type: MediaType = MediaType.ANIME
private var statType: StatType = StatType.COUNT
private var primaryColor: Int = 0
private lateinit var user: Query.UserProfile
private lateinit var activity: ProfileActivity
override fun onCreateView(
inflater: LayoutInflater,
@ -55,14 +44,14 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity = requireActivity() as ProfileActivity
user = arguments?.getSerializable("user") as Query.UserProfile
val typedValue = TypedValue()
activity.theme.resolveAttribute(
com.google.android.material.R.attr.colorPrimary,
typedValue,
true
)
primaryColor = typedValue.data
binding.statisticList.adapter = adapter
binding.statisticList.setHasFixedSize(true)
binding.statisticList.isNestedScrollingEnabled = false
binding.statisticList.layoutManager = LinearLayoutManager(requireContext())
binding.statisticProgressBar.visibility = View.VISIBLE
binding.sourceType.setAdapter(
ArrayAdapter(
@ -94,161 +83,39 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P
}
loadStats(type == MediaType.ANIME)
binding.statisticProgressBar.visibility = View.GONE
binding.chartsContainer.visibility = View.VISIBLE
}
}
}
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
binding.root.requestLayout()
}
loadStats(type == MediaType.ANIME)
}
private fun loadStats(anime: Boolean) {
val formatChartModel = getFormatChartModel(anime)
if (formatChartModel != null) {
binding.formatChartView.visibility = View.VISIBLE
val aaOptions = buildOptions(formatChartModel)
binding.formatChartView.aa_drawChartWithChartOptions(aaOptions)
} else {
binding.formatChartView.visibility = View.GONE
}
val statusChartModel = getStatusChartModel(anime)
if (statusChartModel != null) {
binding.statusChartView.visibility = View.VISIBLE
val aaOptions = buildOptions(statusChartModel)
binding.statusChartView.aa_drawChartWithChartOptions(aaOptions)
} else {
binding.statusChartView.visibility = View.GONE
}
val scoreChartModel = getScoreChartModel(anime)
if (scoreChartModel != null) {
binding.scoreChartView.visibility = View.VISIBLE
val aaOptions = buildOptions(scoreChartModel, false, """
function () {
return 'score: ' +
this.x +
'<br/> ' +
' ${getTypeName()} ' +
this.y
}
""".trimIndent()
)
binding.scoreChartView.aa_drawChartWithChartOptions(aaOptions)
} else {
binding.scoreChartView.visibility = View.GONE
}
val lengthChartModel = getLengthChartModel(anime)
if (lengthChartModel != null) {
binding.lengthChartView.visibility = View.VISIBLE
val aaOptions = buildOptions(lengthChartModel)
binding.lengthChartView.aa_drawChartWithChartOptions(aaOptions)
} else {
binding.lengthChartView.visibility = View.GONE
}
val releaseYearChartModel = getReleaseYearChartModel(anime)
if (releaseYearChartModel != null) {
binding.releaseYearChartView.visibility = View.VISIBLE
val aaOptions = buildOptions(releaseYearChartModel, false, """
function () {
return 'Year: ' +
this.x +
'<br/> ' +
' ${getTypeName()} ' +
this.y
}
""".trimIndent()
)
binding.releaseYearChartView.aa_drawChartWithChartOptions(aaOptions)
} else {
binding.releaseYearChartView.visibility = View.GONE
}
val startYearChartModel = getStartYearChartModel(anime)
if (startYearChartModel != null) {
binding.startYearChartView.visibility = View.VISIBLE
val aaOptions = buildOptions(startYearChartModel, false, """
function () {
return 'Year: ' +
this.x +
'<br/> ' +
' ${getTypeName()} ' +
this.y
}
""".trimIndent()
)
binding.startYearChartView.aa_drawChartWithChartOptions(aaOptions)
} else {
binding.startYearChartView.visibility = View.GONE
}
val genreChartModel = getGenreChartModel(anime)
if (genreChartModel.first != null) {
binding.genreChartView.visibility = View.VISIBLE
val aaOptions = buildOptions(genreChartModel.first!!, true, """
function () {
return 'Genre: ' +
this.x +
'<br/> ' +
' ${getTypeName()} ' +
this.y
}
""".trimIndent()
)
val min = genreChartModel.second.first
val max = genreChartModel.second.second
aaOptions.yAxis = AAYAxis().min(min).max(max).tickInterval(if (max > 100) 20 else 10)
binding.genreChartView.aa_drawChartWithChartOptions(aaOptions)
} else {
binding.genreChartView.visibility = View.GONE
}
binding.statisticProgressBar.visibility = View.VISIBLE
binding.statisticList.visibility = View.GONE
adapter.clear()
loadFormatChart(anime)
loadScoreChart(anime)
loadStatusChart(anime)
loadReleaseYearChart(anime)
loadStartYearChart(anime)
loadLengthChart(anime)
loadGenreChart(anime)
loadTagChart(anime)
loadCountryChart(anime)
loadVoiceActorsChart(anime)
loadStudioChart(anime)
loadStaffChart(anime)
binding.statisticProgressBar.visibility = View.GONE
binding.statisticList.visibility = View.VISIBLE
}
private fun buildOptions(
aaChartModel: AAChartModel,
polar: Boolean = true,
formatting: String? = null
): AAOptions {
val aaOptions = aaChartModel.aa_toAAOptions()
aaOptions.chart?.zoomType = "xy"
aaOptions.chart?.pinchType = "xy"
aaOptions.chart?.polar = polar
aaOptions.tooltip?.apply {
headerFormat
if (formatting != null) {
formatter(formatting)
} else {
formatter(
"""
function () {
return this.point.name
+ ': <br/> '
+ '<b> '
+ this.y
+ ', '
+ (this.percentage).toFixed(2)
+ '%'
}
""".trimIndent()
)
}
}
aaOptions.legend?.apply {
enabled(true)
.labelFormat = "{name}: {y}"
}
aaOptions.plotOptions?.series?.connectNulls(true)
setColors(aaOptions)
return aaOptions
}
private fun getFormatChartModel(anime: Boolean): AAChartModel? {
private fun loadFormatChart(anime: Boolean) {
val names: List<String> = if (anime) {
stats?.data?.user?.statistics?.anime?.formats?.map { it.format } ?: emptyList()
} else {
@ -267,19 +134,21 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.formats?.map { it.meanScore }
} ?: emptyList()
}
if (names.isEmpty() || values.isEmpty())
return null
val primaryColor = primaryColor
val palette = generateColorPalette(primaryColor, names.size)
return AAChartModel()
.chartType(AAChartType.Pie)
.subtitle(getTypeName())
.zoomType(AAChartZoomType.XY)
.dataLabelsEnabled(true)
.series(getElements(names, values, palette))
if (names.isNotEmpty() || values.isNotEmpty()) {
val formatChart = ChartBuilder.buildChart(
activity,
ChartType.OneDimensional,
AAChartType.Pie,
statType,
type,
names,
values
)
adapter.add(ChartItem("Format", formatChart))
}
}
private fun getStatusChartModel(anime: Boolean): AAChartModel? {
private fun loadStatusChart(anime: Boolean) {
val names: List<String> = if (anime) {
stats?.data?.user?.statistics?.anime?.statuses?.map { it.status } ?: emptyList()
} else {
@ -298,18 +167,21 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.statuses?.map { it.meanScore }
} ?: emptyList()
}
if (names.isEmpty() || values.isEmpty())
return null
val palette = generateColorPalette(primaryColor, names.size)
return AAChartModel()
.chartType(AAChartType.Funnel)
.subtitle(getTypeName())
.zoomType(AAChartZoomType.XY)
.dataLabelsEnabled(true)
.series(getElements(names, values, palette))
if (names.isNotEmpty() || values.isNotEmpty()) {
val statusChart = ChartBuilder.buildChart(
activity,
ChartType.OneDimensional,
AAChartType.Funnel,
statType,
type,
names,
values
)
adapter.add(ChartItem("Status", statusChart))
}
}
private fun getScoreChartModel(anime: Boolean): AAChartModel? {
private fun loadScoreChart(anime: Boolean) {
val names: List<Int> = if (anime) {
stats?.data?.user?.statistics?.anime?.scores?.map { it.score } ?: emptyList()
} else {
@ -328,25 +200,28 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.scores?.map { it.meanScore }
} ?: emptyList()
}
if (names.isEmpty() || values.isEmpty())
return null
val palette = generateColorPalette(primaryColor, names.size)
return AAChartModel()
.chartType(AAChartType.Column)
.subtitle(getTypeName())
.zoomType(AAChartZoomType.XY)
.dataLabelsEnabled(false)
.yAxisTitle(getTypeName())
.xAxisTickInterval(10)
.stacking(AAChartStackingType.Normal)
.series(getElements(names, values, palette))
if (names.isNotEmpty() || values.isNotEmpty()) {
val scoreChart = ChartBuilder.buildChart(
activity,
ChartType.TwoDimensional,
AAChartType.Column,
statType,
type,
names,
values,
xAxisName = "Score",
)
adapter.add(ChartItem("Score", scoreChart))
}
}
private fun getLengthChartModel(anime: Boolean): AAChartModel? {
private fun loadLengthChart(anime: Boolean) {
val names: List<String> = if (anime) {
stats?.data?.user?.statistics?.anime?.lengths?.map { it.length?: "unknown" } ?: emptyList()
stats?.data?.user?.statistics?.anime?.lengths?.map { it.length ?: "unknown" }
?: emptyList()
} else {
stats?.data?.user?.statistics?.manga?.lengths?.map { it.length?: "unknown" } ?: emptyList()
stats?.data?.user?.statistics?.manga?.lengths?.map { it.length ?: "unknown" }
?: emptyList()
}
val values: List<Number> = if (anime) {
when (statType) {
@ -361,23 +236,28 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.lengths?.map { it.meanScore }
} ?: emptyList()
}
//clear nulls from names
if (names.isEmpty() || values.isEmpty())
return null
val palette = generateColorPalette(primaryColor, names.size)
return AAChartModel()
.chartType(AAChartType.Pyramid)
.subtitle(getTypeName())
.zoomType(AAChartZoomType.XY)
.dataLabelsEnabled(true)
.series(getElements(names, values, palette))
if (names.isNotEmpty() || values.isNotEmpty()) {
val lengthChart = ChartBuilder.buildChart(
activity,
ChartType.OneDimensional,
AAChartType.Pyramid,
statType,
type,
names,
values,
xAxisName = "Length",
)
adapter.add(ChartItem("Length", lengthChart))
}
}
private fun getReleaseYearChartModel(anime: Boolean): AAChartModel? {
private fun loadReleaseYearChart(anime: Boolean) {
val names: List<Number> = if (anime) {
stats?.data?.user?.statistics?.anime?.releaseYears?.map { it.releaseYear } ?: emptyList()
stats?.data?.user?.statistics?.anime?.releaseYears?.map { it.releaseYear }
?: emptyList()
} else {
stats?.data?.user?.statistics?.manga?.releaseYears?.map { it.releaseYear } ?: emptyList()
stats?.data?.user?.statistics?.manga?.releaseYears?.map { it.releaseYear }
?: emptyList()
}
val values: List<Number> = if (anime) {
when (statType) {
@ -392,22 +272,22 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.releaseYears?.map { it.meanScore }
} ?: emptyList()
}
if (names.isEmpty() || values.isEmpty())
return null
val palette = generateColorPalette(primaryColor, names.size)
val hexColorsArray: Array<Any> = palette.map { String.format("#%06X", 0xFFFFFF and it) }.toTypedArray()
return AAChartModel()
.chartType(AAChartType.Bubble)
.subtitle(getTypeName())
.zoomType(AAChartZoomType.XY)
.dataLabelsEnabled(false)
.yAxisTitle(getTypeName())
.stacking(AAChartStackingType.Normal)
.series(getElementsSimple(names, values))
.colorsTheme(hexColorsArray)
if (names.isNotEmpty() || values.isNotEmpty()) {
val releaseYearChart = ChartBuilder.buildChart(
activity,
ChartType.TwoDimensional,
AAChartType.Bubble,
statType,
type,
names,
values,
xAxisName = "Year",
)
adapter.add(ChartItem("Release Year", releaseYearChart))
}
}
private fun getStartYearChartModel(anime: Boolean): AAChartModel? {
private fun loadStartYearChart(anime: Boolean) {
val names: List<Number> = if (anime) {
stats?.data?.user?.statistics?.anime?.startYears?.map { it.startYear } ?: emptyList()
} else {
@ -426,22 +306,22 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.startYears?.map { it.meanScore }
} ?: emptyList()
}
if (names.isEmpty() || values.isEmpty())
return null
val palette = generateColorPalette(primaryColor, names.size)
val hexColorsArray: Array<Any> = palette.map { String.format("#%06X", 0xFFFFFF and it) }.toTypedArray()
return AAChartModel()
.chartType(AAChartType.Bar)
.subtitle(getTypeName())
.zoomType(AAChartZoomType.XY)
.dataLabelsEnabled(false)
.yAxisTitle(getTypeName())
.stacking(AAChartStackingType.Normal)
.series(getElementsSimple(names, values))
.colorsTheme(hexColorsArray)
if (names.isNotEmpty() || values.isNotEmpty()) {
val startYearChart = ChartBuilder.buildChart(
activity,
ChartType.TwoDimensional,
AAChartType.Bar,
statType,
type,
names,
values,
xAxisName = "Year",
)
adapter.add(ChartItem("Start Year", startYearChart))
}
}
private fun getGenreChartModel(anime: Boolean): Pair<AAChartModel?, Pair<Int, Int>> {
private fun loadGenreChart(anime: Boolean) {
val names: List<String> = if (anime) {
stats?.data?.user?.statistics?.anime?.genres?.map { it.genre } ?: emptyList()
} else {
@ -460,163 +340,219 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.genres?.map { it.meanScore }
} ?: emptyList()
}
if (names.isEmpty() || values.isEmpty())
return Pair(null, Pair(0, 0))
val palette = generateColorPalette(primaryColor, names.size)
val hexColorsArray: Array<Any> = palette.map { String.format("#%06X", 0xFFFFFF and it) }.toTypedArray()
return Pair(AAChartModel()
.chartType(AAChartType.Area)
.subtitle(getTypeName())
.zoomType(AAChartZoomType.XY)
.dataLabelsEnabled(false)
.legendEnabled(false)
.yAxisTitle(getTypeName())
.stacking(AAChartStackingType.Normal)
.series(getElementsSimple(names, values))
.colorsTheme(hexColorsArray)
.categories(names.toTypedArray()),
Pair(values.minOf { it.toInt() }, values.maxOf { it.toInt() }))
}
enum class StatType {
COUNT, TIME, MEAN_SCORE
}
enum class MediaType {
ANIME, MANGA
}
private fun getTypeName(): String {
return when (statType) {
StatType.COUNT -> "Count"
StatType.TIME -> if (type == MediaType.ANIME) "Hours Watched" else "Chapters Read"
StatType.MEAN_SCORE -> "Mean Score"
if (names.isNotEmpty() || values.isNotEmpty()) {
val genreChart = ChartBuilder.buildChart(
activity,
ChartType.TwoDimensional,
AAChartType.Areaspline,
statType,
type,
names.take(15),
values.take(15),
xAxisName = "Genre",
polar = true,
categories = names
)
adapter.add(ChartItem("Genre", genreChart))
}
}
private fun getElements(
names: List<Any>,
statData: List<Number>,
colors: List<Int>
): Array<Any> {
val statDataElements = mutableListOf<AADataElement>()
for (i in statData.indices) {
val element = AADataElement()
.y(statData[i])
.color(
AAColor.rgbaColor(
Color.red(colors[i]),
Color.green(colors[i]),
Color.blue(colors[i]),
0.9f
)
)
if (names[i] is Number) {
element.x(names[i] as Number)
element.dataLabels(AADataLabels()
.enabled(false)
.format("{point.y}")
.backgroundColor(AAColor.rgbaColor(255, 255, 255, 0.0f))
)
} else {
element.x(i)
element.name(names[i] as String)
private fun loadTagChart(anime: Boolean) {
val names: List<String> = if (anime) {
stats?.data?.user?.statistics?.anime?.tags?.map { it.tag.name } ?: emptyList()
} else {
stats?.data?.user?.statistics?.manga?.tags?.map { it.tag.name } ?: emptyList()
}
val values: List<Number> = if (anime) {
when (statType) {
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.tags?.map { it.count }
StatType.TIME -> stats?.data?.user?.statistics?.anime?.tags?.map { it.minutesWatched / 60 }
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.tags?.map { it.meanScore }
} ?: emptyList()
} else {
when (statType) {
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.tags?.map { it.count }
StatType.TIME -> stats?.data?.user?.statistics?.manga?.tags?.map { it.chaptersRead }
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.tags?.map { it.meanScore }
} ?: emptyList()
}
if (names.isNotEmpty() || values.isNotEmpty()) {
val min = values.minOf { it.toInt() }
val max = values.maxOf { it.toInt() }
val tagChart = ChartBuilder.buildChart(
activity,
ChartType.TwoDimensional,
AAChartType.Areaspline,
statType,
type,
names,
values,
xAxisName = "Tag",
polar = false,
categories = names,
scrollPos = 0.0f
)
tagChart.yAxis = AAYAxis().min(min).max(max).tickInterval(if (max > 100) 20 else 10)
adapter.add(ChartItem("Tag", tagChart))
}
}
private fun loadCountryChart(anime: Boolean) {
val names: List<String> = if (anime) {
stats?.data?.user?.statistics?.anime?.countries?.map { it.country } ?: emptyList()
} else {
stats?.data?.user?.statistics?.manga?.countries?.map { it.country } ?: emptyList()
}
val values: List<Number> = if (anime) {
when (statType) {
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.countries?.map { it.count }
StatType.TIME -> stats?.data?.user?.statistics?.anime?.countries?.map { it.minutesWatched / 60 }
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.countries?.map { it.meanScore }
} ?: emptyList()
} else {
when (statType) {
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.countries?.map { it.count }
StatType.TIME -> stats?.data?.user?.statistics?.manga?.countries?.map { it.chaptersRead }
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.countries?.map { it.meanScore }
} ?: emptyList()
}
if (names.isNotEmpty() || values.isNotEmpty()) {
val countryChart = ChartBuilder.buildChart(
activity,
ChartType.OneDimensional,
AAChartType.Pie,
statType,
type,
names,
values,
xAxisName = "Country",
polar = false,
categories = names,
scrollPos = null
)
adapter.add(ChartItem("Country", countryChart))
}
}
private fun loadVoiceActorsChart(anime: Boolean) {
val names: List<String> = if (anime) {
stats?.data?.user?.statistics?.anime?.voiceActors?.map { it.voiceActor.name.full?:"unknown" } ?: emptyList()
} else {
stats?.data?.user?.statistics?.manga?.voiceActors?.map { it.voiceActor.name.full?:"unknown" } ?: emptyList()
}
val values: List<Number> = if (anime) {
when (statType) {
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.voiceActors?.map { it.count }
StatType.TIME -> stats?.data?.user?.statistics?.anime?.voiceActors?.map { it.minutesWatched / 60 }
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.voiceActors?.map { it.meanScore }
} ?: emptyList()
} else {
when (statType) {
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.voiceActors?.map { it.count }
StatType.TIME -> stats?.data?.user?.statistics?.manga?.voiceActors?.map { it.chaptersRead }
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.voiceActors?.map { it.meanScore }
} ?: emptyList()
}
if (names.isNotEmpty() || values.isNotEmpty()) {
val voiceActorsChart = ChartBuilder.buildChart(
activity,
ChartType.TwoDimensional,
AAChartType.Column,
statType,
type,
names,
values,
xAxisName = "Voice Actor",
polar = false,
categories = names,
scrollPos = 0.0f
)
adapter.add(ChartItem("Voice Actor", voiceActorsChart))
}
}
private fun loadStaffChart(anime: Boolean) {
val names: List<String> = if (anime) {
stats?.data?.user?.statistics?.anime?.staff?.map { it.staff.name.full?:"unknown" } ?: emptyList()
} else {
stats?.data?.user?.statistics?.manga?.staff?.map { it.staff.name.full?:"unknown" } ?: emptyList()
}
val values: List<Number> = if (anime) {
when (statType) {
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.staff?.map { it.count }
StatType.TIME -> stats?.data?.user?.statistics?.anime?.staff?.map { it.minutesWatched / 60 }
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.staff?.map { it.meanScore }
} ?: emptyList()
} else {
when (statType) {
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.staff?.map { it.count }
StatType.TIME -> stats?.data?.user?.statistics?.manga?.staff?.map { it.chaptersRead }
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.staff?.map { it.meanScore }
} ?: emptyList()
}
if (names.isNotEmpty() || values.isNotEmpty()) {
val staffChart = ChartBuilder.buildChart(
activity,
ChartType.TwoDimensional,
AAChartType.Line,
statType,
type,
names,
values,
xAxisName = "Staff",
polar = false,
categories = names,
scrollPos = 0.0f
)
adapter.add(ChartItem("Staff", staffChart))
}
}
private fun loadStudioChart(anime: Boolean) {
val names: List<String> = if (anime) {
stats?.data?.user?.statistics?.anime?.studios?.map { it.studio.name } ?: emptyList()
} else {
stats?.data?.user?.statistics?.manga?.studios?.map { it.studio.name } ?: emptyList()
}
val values: List<Number> = if (anime) {
when (statType) {
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.studios?.map { it.count }
StatType.TIME -> stats?.data?.user?.statistics?.anime?.studios?.map { it.minutesWatched / 60 }
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.studios?.map { it.meanScore }
} ?: emptyList()
} else {
when (statType) {
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.studios?.map { it.count }
StatType.TIME -> stats?.data?.user?.statistics?.manga?.studios?.map { it.chaptersRead }
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.studios?.map { it.meanScore }
} ?: emptyList()
}
if (names.isNotEmpty() || values.isNotEmpty()) {
val studioChart = ChartBuilder.buildChart(
activity,
ChartType.TwoDimensional,
AAChartType.Spline,
statType,
type,
names.take(15),
values.take(15),
xAxisName = "Studio",
polar = true,
categories = names,
scrollPos = null
)
adapter.add(ChartItem("Studio", studioChart))
}
}
companion object {
fun newInstance(user: Query.UserProfile): StatsFragment {
val args = Bundle().apply {
putSerializable("user", user)
}
return StatsFragment().apply {
arguments = args
}
statDataElements.add(element)
}
return arrayOf(
AASeriesElement().name("Score").color(primaryColor)
.data(statDataElements.toTypedArray())
)
}
private fun getElementsSimple(
names: List<Any>,
statData: List<Any>
): Array<Any> {
val statValues = mutableListOf<Array<Any>>()
for (i in statData.indices) {
statValues.add(arrayOf(names[i], statData[i], statData[i]))
}
return arrayOf(
AASeriesElement().name("Score")
.data(statValues.toTypedArray())
.dataLabels(AADataLabels()
.enabled(false)
)
.colorByPoint(true)
)
}
private fun setColors(aaOptions: AAOptions) {
val backgroundColor = TypedValue()
activity.theme.resolveAttribute(com.google.android.material.R.attr.colorSurfaceVariant, backgroundColor, true)
val backgroundStyle = AAStyle().color(
AAColor.rgbaColor(
Color.red(backgroundColor.data),
Color.green(backgroundColor.data),
Color.blue(backgroundColor.data),
1f
)
)
val colorOnBackground = TypedValue()
activity.theme.resolveAttribute(
com.google.android.material.R.attr.colorOnSurface,
colorOnBackground,
true
)
val onBackgroundStyle = AAStyle().color(
AAColor.rgbaColor(
Color.red(colorOnBackground.data),
Color.green(colorOnBackground.data),
Color.blue(colorOnBackground.data),
0.9f
)
)
aaOptions.chart?.backgroundColor(backgroundStyle.color)
aaOptions.tooltip?.backgroundColor(
AAColor.rgbaColor(
Color.red(primaryColor),
Color.green(primaryColor),
Color.blue(primaryColor),
0.9f
)
)
aaOptions.title?.style(onBackgroundStyle)
aaOptions.subtitle?.style(onBackgroundStyle)
aaOptions.tooltip?.style(backgroundStyle)
aaOptions.credits?.style(onBackgroundStyle)
aaOptions.xAxis?.labels?.style(onBackgroundStyle)
aaOptions.yAxis?.labels?.style(onBackgroundStyle)
aaOptions.plotOptions?.series?.dataLabels?.style(onBackgroundStyle)
aaOptions.plotOptions?.series?.dataLabels?.backgroundColor(backgroundStyle.color)
aaOptions.legend?.itemStyle(AAItemStyle().color(onBackgroundStyle.color))
aaOptions.touchEventEnabled(true)
}
private fun generateColorPalette(
baseColor: Int,
size: Int,
hueDelta: Float = 8f,
saturationDelta: Float = 2.02f,
valueDelta: Float = 2.02f
): List<Int> {
val palette = mutableListOf<Int>()
val hsv = FloatArray(3)
Color.colorToHSV(baseColor, hsv)
for (i in 0 until size) {
val newHue = (hsv[0] + hueDelta * i) % 360 // Ensure hue stays within the 0-360 range
val newSaturation = (hsv[1] + saturationDelta * i).coerceIn(0f, 1f)
val newValue = (hsv[2] + valueDelta * i).coerceIn(0f, 1f)
val newHsv = floatArrayOf(newHue, newSaturation, newValue)
palette.add(Color.HSVToColor(newHsv))
}
return palette
}
}

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="M200,840q-33,0 -56.5,-23.5T120,760v-560q0,-33 23.5,-56.5T200,120h560q33,0 56.5,23.5T840,200v560q0,33 -23.5,56.5T760,840L600,840v-80h160v-480L200,280v480h160v80L200,840ZM440,840v-246l-64,64 -56,-58 160,-160 160,160 -56,58 -64,-64v246h-80Z"/>
</vector>

View file

@ -97,385 +97,9 @@
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:id="@+id/chartsContainer"
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/statisticList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Format"
android:textColor="?attr/colorPrimary"
android:textSize="16sp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/formatChartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Score"
android:textColor="?attr/colorPrimary"
android:textSize="16sp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/scoreChartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Status"
android:textColor="?attr/colorPrimary"
android:textSize="16sp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/statusChartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Length"
android:textColor="?attr/colorPrimary"
android:textSize="16sp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/lengthChartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Release Year"
android:textColor="?attr/colorPrimary"
android:textSize="16sp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/releaseYearChartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Start Year"
android:textColor="?attr/colorPrimary"
android:textSize="16sp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/startYearChartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Genre"
android:textColor="?attr/colorPrimary"
android:textSize="16sp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/genreChartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Tag"
android:textColor="?attr/colorPrimary"
android:textSize="16sp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/tagChartChartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Country of Origin"
android:textColor="?attr/colorPrimary"
android:textSize="16sp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/countryChartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Voice Actor"
android:textColor="?attr/colorPrimary"
android:textSize="16sp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/voiceActorChartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Staff"
android:textColor="?attr/colorPrimary"
android:textSize="16sp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/staffChartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Studio"
android:textColor="?attr/colorPrimary"
android:textSize="16sp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/studioChartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</LinearLayout>
android:layout_height="wrap_content"
tools:listitem="@layout/item_chart" />
</LinearLayout>

View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/chartLayout"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/typeText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:text="Format"
android:textColor="?attr/colorPrimary"
android:textSize="16sp"
tools:ignore="HardcodedText" />
<ImageButton
android:id="@+id/openButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="end"
android:background="?android:colorBackground"
android:contentDescription="Open"
android:src="@drawable/ic_open_24"
app:tint="?attr/colorPrimary"
tools:ignore="HardcodedText" />
</FrameLayout>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp">
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/chartView"
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="?android:colorBackground" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>