feat: more charts | code cleanup
This commit is contained in:
parent
2673b7d9bc
commit
7a1ed4f83e
9 changed files with 782 additions and 790 deletions
|
@ -211,7 +211,7 @@ class Query {
|
||||||
val statistics: NNUserStatisticTypes,
|
val statistics: NNUserStatisticTypes,
|
||||||
@SerialName("siteUrl")
|
@SerialName("siteUrl")
|
||||||
val siteUrl: String,
|
val siteUrl: String,
|
||||||
)
|
): java.io.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class NNUserStatisticTypes(
|
data class NNUserStatisticTypes(
|
||||||
|
|
321
app/src/main/java/ani/dantotsu/profile/ChartBuilder.kt
Normal file
321
app/src/main/java/ani/dantotsu/profile/ChartBuilder.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
app/src/main/java/ani/dantotsu/profile/ChartItem.kt
Normal file
26
app/src/main/java/ani/dantotsu/profile/ChartItem.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import androidx.fragment.app.FragmentManager
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.connections.anilist.api.Query
|
import ani.dantotsu.connections.anilist.api.Query
|
||||||
|
@ -39,6 +40,7 @@ class ProfileActivity : AppCompatActivity(){
|
||||||
private lateinit var binding: ActivityProfileBinding
|
private lateinit var binding: ActivityProfileBinding
|
||||||
private var selected: Int = 0
|
private var selected: Int = 0
|
||||||
private lateinit var navBar: AnimatedBottomBar
|
private lateinit var navBar: AnimatedBottomBar
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -65,7 +67,7 @@ class ProfileActivity : AppCompatActivity(){
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
binding.profileViewPager.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin = navBarHeight }
|
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.visibility = View.VISIBLE
|
||||||
navBar.selectTabAt(selected)
|
navBar.selectTabAt(selected)
|
||||||
navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener {
|
navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener {
|
||||||
|
@ -129,20 +131,18 @@ class ProfileActivity : AppCompatActivity(){
|
||||||
}
|
}
|
||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ViewPagerAdapter(
|
private class ViewPagerAdapter(
|
||||||
fragmentManager: FragmentManager,
|
fragmentManager: FragmentManager,
|
||||||
lifecycle: Lifecycle,
|
lifecycle: Lifecycle,
|
||||||
private val user: Query.UserProfile,
|
private val user: Query.UserProfile
|
||||||
private val activity: ProfileActivity
|
|
||||||
) :
|
) :
|
||||||
FragmentStateAdapter(fragmentManager, lifecycle) {
|
FragmentStateAdapter(fragmentManager, lifecycle) {
|
||||||
|
|
||||||
override fun getItemCount(): Int = 2
|
override fun getItemCount(): Int = 2
|
||||||
override fun createFragment(position: Int): Fragment = when (position) {
|
override fun createFragment(position: Int): Fragment = when (position) {
|
||||||
0 -> ProfileFragment(user, activity)
|
0 -> ProfileFragment.newInstance(user)
|
||||||
1 -> StatsFragment(user, activity)
|
1 -> StatsFragment.newInstance(user)
|
||||||
else -> ProfileFragment(user, activity)
|
else -> ProfileFragment.newInstance(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,7 +13,6 @@ import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import ani.dantotsu.bottomBar
|
|
||||||
import ani.dantotsu.buildMarkwon
|
import ani.dantotsu.buildMarkwon
|
||||||
import ani.dantotsu.connections.anilist.ProfileViewModel
|
import ani.dantotsu.connections.anilist.ProfileViewModel
|
||||||
import ani.dantotsu.connections.anilist.api.Query
|
import ani.dantotsu.connections.anilist.api.Query
|
||||||
|
@ -27,8 +26,10 @@ import ani.dantotsu.setSlideUp
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class ProfileFragment(private val user: Query.UserProfile, private val activity: ProfileActivity): Fragment() {
|
class ProfileFragment(): Fragment() {
|
||||||
lateinit var binding: FragmentProfileBinding
|
lateinit var binding: FragmentProfileBinding
|
||||||
|
private lateinit var activity: ProfileActivity
|
||||||
|
private lateinit var user: Query.UserProfile
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
|
@ -41,6 +42,8 @@ class ProfileFragment(private val user: Query.UserProfile, private val activity:
|
||||||
val model: ProfileViewModel by activityViewModels()
|
val model: ProfileViewModel by activityViewModels()
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
activity = requireActivity() as ProfileActivity
|
||||||
|
user = arguments?.getSerializable("user") as Query.UserProfile
|
||||||
val markwon = buildMarkwon(activity, false)
|
val markwon = buildMarkwon(activity, false)
|
||||||
markwon.setMarkdown(binding.profileUserBio, user.about?:"")
|
markwon.setMarkdown(binding.profileUserBio, user.about?:"")
|
||||||
binding.userInfoContainer.visibility = if (user.about != null) View.VISIBLE else View.GONE
|
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
|
binding.profileFavManga
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
if (this::binding.isInitialized) {
|
||||||
|
binding.root.requestLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun initRecyclerView(
|
private fun initRecyclerView(
|
||||||
mode: LiveData<ArrayList<Media>>,
|
mode: LiveData<ArrayList<Media>>,
|
||||||
container: View,
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,48 +1,37 @@
|
||||||
package ani.dantotsu.profile
|
package ani.dantotsu.profile
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.TypedValue
|
|
||||||
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 android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.connections.anilist.api.Query
|
import ani.dantotsu.connections.anilist.api.Query
|
||||||
import ani.dantotsu.databinding.FragmentStatisticsBinding
|
import ani.dantotsu.databinding.FragmentStatisticsBinding
|
||||||
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartModel
|
import ani.dantotsu.profile.ChartBuilder.Companion.ChartType
|
||||||
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartStackingType
|
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.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.aaoptionsmodel.AAYAxis
|
||||||
import com.github.aachartmodel.aainfographics.aatools.AAColor
|
import com.xwray.groupie.GroupieAdapter
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class StatsFragment(private val user: Query.UserProfile, private val activity: ProfileActivity) :
|
class StatsFragment() :
|
||||||
Fragment() {
|
Fragment() {
|
||||||
private lateinit var binding: FragmentStatisticsBinding
|
private lateinit var binding: FragmentStatisticsBinding
|
||||||
|
private var adapter: GroupieAdapter = GroupieAdapter()
|
||||||
private var stats: Query.StatisticsResponse? = null
|
private var stats: Query.StatisticsResponse? = null
|
||||||
private var type: MediaType = MediaType.ANIME
|
private var type: MediaType = MediaType.ANIME
|
||||||
private var statType: StatType = StatType.COUNT
|
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(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -55,14 +44,14 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
activity = requireActivity() as ProfileActivity
|
||||||
|
user = arguments?.getSerializable("user") as Query.UserProfile
|
||||||
|
|
||||||
val typedValue = TypedValue()
|
binding.statisticList.adapter = adapter
|
||||||
activity.theme.resolveAttribute(
|
binding.statisticList.setHasFixedSize(true)
|
||||||
com.google.android.material.R.attr.colorPrimary,
|
binding.statisticList.isNestedScrollingEnabled = false
|
||||||
typedValue,
|
binding.statisticList.layoutManager = LinearLayoutManager(requireContext())
|
||||||
true
|
binding.statisticProgressBar.visibility = View.VISIBLE
|
||||||
)
|
|
||||||
primaryColor = typedValue.data
|
|
||||||
|
|
||||||
binding.sourceType.setAdapter(
|
binding.sourceType.setAdapter(
|
||||||
ArrayAdapter(
|
ArrayAdapter(
|
||||||
|
@ -94,161 +83,39 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P
|
||||||
}
|
}
|
||||||
loadStats(type == MediaType.ANIME)
|
loadStats(type == MediaType.ANIME)
|
||||||
binding.statisticProgressBar.visibility = View.GONE
|
binding.statisticProgressBar.visibility = View.GONE
|
||||||
binding.chartsContainer.visibility = View.VISIBLE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
if (this::binding.isInitialized) {
|
||||||
|
binding.root.requestLayout()
|
||||||
|
}
|
||||||
loadStats(type == MediaType.ANIME)
|
loadStats(type == MediaType.ANIME)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadStats(anime: Boolean) {
|
private fun loadStats(anime: Boolean) {
|
||||||
val formatChartModel = getFormatChartModel(anime)
|
binding.statisticProgressBar.visibility = View.VISIBLE
|
||||||
if (formatChartModel != null) {
|
binding.statisticList.visibility = View.GONE
|
||||||
binding.formatChartView.visibility = View.VISIBLE
|
adapter.clear()
|
||||||
val aaOptions = buildOptions(formatChartModel)
|
loadFormatChart(anime)
|
||||||
binding.formatChartView.aa_drawChartWithChartOptions(aaOptions)
|
loadScoreChart(anime)
|
||||||
} else {
|
loadStatusChart(anime)
|
||||||
binding.formatChartView.visibility = View.GONE
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
val statusChartModel = getStatusChartModel(anime)
|
private fun loadFormatChart(anime: Boolean) {
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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? {
|
|
||||||
val names: List<String> = if (anime) {
|
val names: List<String> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.anime?.formats?.map { it.format } ?: emptyList()
|
stats?.data?.user?.statistics?.anime?.formats?.map { it.format } ?: emptyList()
|
||||||
} else {
|
} 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 }
|
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.formats?.map { it.meanScore }
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
if (names.isEmpty() || values.isEmpty())
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
return null
|
val formatChart = ChartBuilder.buildChart(
|
||||||
val primaryColor = primaryColor
|
activity,
|
||||||
val palette = generateColorPalette(primaryColor, names.size)
|
ChartType.OneDimensional,
|
||||||
return AAChartModel()
|
AAChartType.Pie,
|
||||||
.chartType(AAChartType.Pie)
|
statType,
|
||||||
.subtitle(getTypeName())
|
type,
|
||||||
.zoomType(AAChartZoomType.XY)
|
names,
|
||||||
.dataLabelsEnabled(true)
|
values
|
||||||
.series(getElements(names, values, palette))
|
)
|
||||||
|
adapter.add(ChartItem("Format", formatChart))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getStatusChartModel(anime: Boolean): AAChartModel? {
|
private fun loadStatusChart(anime: Boolean) {
|
||||||
val names: List<String> = if (anime) {
|
val names: List<String> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.anime?.statuses?.map { it.status } ?: emptyList()
|
stats?.data?.user?.statistics?.anime?.statuses?.map { it.status } ?: emptyList()
|
||||||
} else {
|
} 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 }
|
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.statuses?.map { it.meanScore }
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
if (names.isEmpty() || values.isEmpty())
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
return null
|
val statusChart = ChartBuilder.buildChart(
|
||||||
val palette = generateColorPalette(primaryColor, names.size)
|
activity,
|
||||||
return AAChartModel()
|
ChartType.OneDimensional,
|
||||||
.chartType(AAChartType.Funnel)
|
AAChartType.Funnel,
|
||||||
.subtitle(getTypeName())
|
statType,
|
||||||
.zoomType(AAChartZoomType.XY)
|
type,
|
||||||
.dataLabelsEnabled(true)
|
names,
|
||||||
.series(getElements(names, values, palette))
|
values
|
||||||
|
)
|
||||||
|
adapter.add(ChartItem("Status", statusChart))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getScoreChartModel(anime: Boolean): AAChartModel? {
|
private fun loadScoreChart(anime: Boolean) {
|
||||||
val names: List<Int> = if (anime) {
|
val names: List<Int> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.anime?.scores?.map { it.score } ?: emptyList()
|
stats?.data?.user?.statistics?.anime?.scores?.map { it.score } ?: emptyList()
|
||||||
} else {
|
} 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 }
|
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.scores?.map { it.meanScore }
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
if (names.isEmpty() || values.isEmpty())
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
return null
|
val scoreChart = ChartBuilder.buildChart(
|
||||||
val palette = generateColorPalette(primaryColor, names.size)
|
activity,
|
||||||
return AAChartModel()
|
ChartType.TwoDimensional,
|
||||||
.chartType(AAChartType.Column)
|
AAChartType.Column,
|
||||||
.subtitle(getTypeName())
|
statType,
|
||||||
.zoomType(AAChartZoomType.XY)
|
type,
|
||||||
.dataLabelsEnabled(false)
|
names,
|
||||||
.yAxisTitle(getTypeName())
|
values,
|
||||||
.xAxisTickInterval(10)
|
xAxisName = "Score",
|
||||||
.stacking(AAChartStackingType.Normal)
|
)
|
||||||
.series(getElements(names, values, palette))
|
adapter.add(ChartItem("Score", scoreChart))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getLengthChartModel(anime: Boolean): AAChartModel? {
|
private fun loadLengthChart(anime: Boolean) {
|
||||||
val names: List<String> = if (anime) {
|
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 {
|
} 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) {
|
val values: List<Number> = if (anime) {
|
||||||
when (statType) {
|
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 }
|
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.lengths?.map { it.meanScore }
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
//clear nulls from names
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
if (names.isEmpty() || values.isEmpty())
|
val lengthChart = ChartBuilder.buildChart(
|
||||||
return null
|
activity,
|
||||||
val palette = generateColorPalette(primaryColor, names.size)
|
ChartType.OneDimensional,
|
||||||
return AAChartModel()
|
AAChartType.Pyramid,
|
||||||
.chartType(AAChartType.Pyramid)
|
statType,
|
||||||
.subtitle(getTypeName())
|
type,
|
||||||
.zoomType(AAChartZoomType.XY)
|
names,
|
||||||
.dataLabelsEnabled(true)
|
values,
|
||||||
.series(getElements(names, values, palette))
|
xAxisName = "Length",
|
||||||
|
)
|
||||||
|
adapter.add(ChartItem("Length", lengthChart))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getReleaseYearChartModel(anime: Boolean): AAChartModel? {
|
private fun loadReleaseYearChart(anime: Boolean) {
|
||||||
val names: List<Number> = if (anime) {
|
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 {
|
} 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) {
|
val values: List<Number> = if (anime) {
|
||||||
when (statType) {
|
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 }
|
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.releaseYears?.map { it.meanScore }
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
if (names.isEmpty() || values.isEmpty())
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
return null
|
val releaseYearChart = ChartBuilder.buildChart(
|
||||||
val palette = generateColorPalette(primaryColor, names.size)
|
activity,
|
||||||
val hexColorsArray: Array<Any> = palette.map { String.format("#%06X", 0xFFFFFF and it) }.toTypedArray()
|
ChartType.TwoDimensional,
|
||||||
return AAChartModel()
|
AAChartType.Bubble,
|
||||||
.chartType(AAChartType.Bubble)
|
statType,
|
||||||
.subtitle(getTypeName())
|
type,
|
||||||
.zoomType(AAChartZoomType.XY)
|
names,
|
||||||
.dataLabelsEnabled(false)
|
values,
|
||||||
.yAxisTitle(getTypeName())
|
xAxisName = "Year",
|
||||||
.stacking(AAChartStackingType.Normal)
|
)
|
||||||
.series(getElementsSimple(names, values))
|
adapter.add(ChartItem("Release Year", releaseYearChart))
|
||||||
.colorsTheme(hexColorsArray)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getStartYearChartModel(anime: Boolean): AAChartModel? {
|
private fun loadStartYearChart(anime: Boolean) {
|
||||||
val names: List<Number> = if (anime) {
|
val names: List<Number> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.anime?.startYears?.map { it.startYear } ?: emptyList()
|
stats?.data?.user?.statistics?.anime?.startYears?.map { it.startYear } ?: emptyList()
|
||||||
} else {
|
} 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 }
|
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.startYears?.map { it.meanScore }
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
if (names.isEmpty() || values.isEmpty())
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
return null
|
val startYearChart = ChartBuilder.buildChart(
|
||||||
val palette = generateColorPalette(primaryColor, names.size)
|
activity,
|
||||||
val hexColorsArray: Array<Any> = palette.map { String.format("#%06X", 0xFFFFFF and it) }.toTypedArray()
|
ChartType.TwoDimensional,
|
||||||
return AAChartModel()
|
AAChartType.Bar,
|
||||||
.chartType(AAChartType.Bar)
|
statType,
|
||||||
.subtitle(getTypeName())
|
type,
|
||||||
.zoomType(AAChartZoomType.XY)
|
names,
|
||||||
.dataLabelsEnabled(false)
|
values,
|
||||||
.yAxisTitle(getTypeName())
|
xAxisName = "Year",
|
||||||
.stacking(AAChartStackingType.Normal)
|
)
|
||||||
.series(getElementsSimple(names, values))
|
adapter.add(ChartItem("Start Year", startYearChart))
|
||||||
.colorsTheme(hexColorsArray)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getGenreChartModel(anime: Boolean): Pair<AAChartModel?, Pair<Int, Int>> {
|
private fun loadGenreChart(anime: Boolean) {
|
||||||
val names: List<String> = if (anime) {
|
val names: List<String> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.anime?.genres?.map { it.genre } ?: emptyList()
|
stats?.data?.user?.statistics?.anime?.genres?.map { it.genre } ?: emptyList()
|
||||||
} else {
|
} 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 }
|
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.genres?.map { it.meanScore }
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
if (names.isEmpty() || values.isEmpty())
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
return Pair(null, Pair(0, 0))
|
val genreChart = ChartBuilder.buildChart(
|
||||||
val palette = generateColorPalette(primaryColor, names.size)
|
activity,
|
||||||
val hexColorsArray: Array<Any> = palette.map { String.format("#%06X", 0xFFFFFF and it) }.toTypedArray()
|
ChartType.TwoDimensional,
|
||||||
return Pair(AAChartModel()
|
AAChartType.Areaspline,
|
||||||
.chartType(AAChartType.Area)
|
statType,
|
||||||
.subtitle(getTypeName())
|
type,
|
||||||
.zoomType(AAChartZoomType.XY)
|
names.take(15),
|
||||||
.dataLabelsEnabled(false)
|
values.take(15),
|
||||||
.legendEnabled(false)
|
xAxisName = "Genre",
|
||||||
.yAxisTitle(getTypeName())
|
polar = true,
|
||||||
.stacking(AAChartStackingType.Normal)
|
categories = names
|
||||||
.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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
)
|
)
|
||||||
|
adapter.add(ChartItem("Genre", genreChart))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadTagChart(anime: Boolean) {
|
||||||
|
val names: List<String> = if (anime) {
|
||||||
|
stats?.data?.user?.statistics?.anime?.tags?.map { it.tag.name } ?: emptyList()
|
||||||
} else {
|
} else {
|
||||||
element.x(i)
|
stats?.data?.user?.statistics?.manga?.tags?.map { it.tag.name } ?: emptyList()
|
||||||
element.name(names[i] as String)
|
|
||||||
}
|
}
|
||||||
statDataElements.add(element)
|
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()
|
||||||
}
|
}
|
||||||
return arrayOf(
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
AASeriesElement().name("Score").color(primaryColor)
|
val min = values.minOf { it.toInt() }
|
||||||
.data(statDataElements.toTypedArray())
|
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 getElementsSimple(
|
private fun loadCountryChart(anime: Boolean) {
|
||||||
names: List<Any>,
|
val names: List<String> = if (anime) {
|
||||||
statData: List<Any>
|
stats?.data?.user?.statistics?.anime?.countries?.map { it.country } ?: emptyList()
|
||||||
): Array<Any> {
|
} else {
|
||||||
val statValues = mutableListOf<Array<Any>>()
|
stats?.data?.user?.statistics?.manga?.countries?.map { it.country } ?: emptyList()
|
||||||
for (i in statData.indices) {
|
|
||||||
statValues.add(arrayOf(names[i], statData[i], statData[i]))
|
|
||||||
}
|
}
|
||||||
return arrayOf(
|
val values: List<Number> = if (anime) {
|
||||||
AASeriesElement().name("Score")
|
when (statType) {
|
||||||
.data(statValues.toTypedArray())
|
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.countries?.map { it.count }
|
||||||
.dataLabels(AADataLabels()
|
StatType.TIME -> stats?.data?.user?.statistics?.anime?.countries?.map { it.minutesWatched / 60 }
|
||||||
.enabled(false)
|
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.countries?.map { it.meanScore }
|
||||||
)
|
} ?: emptyList()
|
||||||
.colorByPoint(true)
|
} 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 setColors(aaOptions: AAOptions) {
|
private fun loadVoiceActorsChart(anime: Boolean) {
|
||||||
val backgroundColor = TypedValue()
|
val names: List<String> = if (anime) {
|
||||||
activity.theme.resolveAttribute(com.google.android.material.R.attr.colorSurfaceVariant, backgroundColor, true)
|
stats?.data?.user?.statistics?.anime?.voiceActors?.map { it.voiceActor.name.full?:"unknown" } ?: emptyList()
|
||||||
val backgroundStyle = AAStyle().color(
|
} else {
|
||||||
AAColor.rgbaColor(
|
stats?.data?.user?.statistics?.manga?.voiceActors?.map { it.voiceActor.name.full?:"unknown" } ?: emptyList()
|
||||||
Color.red(backgroundColor.data),
|
}
|
||||||
Color.green(backgroundColor.data),
|
val values: List<Number> = if (anime) {
|
||||||
Color.blue(backgroundColor.data),
|
when (statType) {
|
||||||
1f
|
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))
|
||||||
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(
|
private fun loadStaffChart(anime: Boolean) {
|
||||||
baseColor: Int,
|
val names: List<String> = if (anime) {
|
||||||
size: Int,
|
stats?.data?.user?.statistics?.anime?.staff?.map { it.staff.name.full?:"unknown" } ?: emptyList()
|
||||||
hueDelta: Float = 8f,
|
} else {
|
||||||
saturationDelta: Float = 2.02f,
|
stats?.data?.user?.statistics?.manga?.staff?.map { it.staff.name.full?:"unknown" } ?: emptyList()
|
||||||
valueDelta: Float = 2.02f
|
}
|
||||||
): List<Int> {
|
val values: List<Number> = if (anime) {
|
||||||
val palette = mutableListOf<Int>()
|
when (statType) {
|
||||||
val hsv = FloatArray(3)
|
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.staff?.map { it.count }
|
||||||
Color.colorToHSV(baseColor, hsv)
|
StatType.TIME -> stats?.data?.user?.statistics?.anime?.staff?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.staff?.map { it.meanScore }
|
||||||
for (i in 0 until size) {
|
} ?: emptyList()
|
||||||
val newHue = (hsv[0] + hueDelta * i) % 360 // Ensure hue stays within the 0-360 range
|
} else {
|
||||||
val newSaturation = (hsv[1] + saturationDelta * i).coerceIn(0f, 1f)
|
when (statType) {
|
||||||
val newValue = (hsv[2] + valueDelta * i).coerceIn(0f, 1f)
|
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.staff?.map { it.count }
|
||||||
|
StatType.TIME -> stats?.data?.user?.statistics?.manga?.staff?.map { it.chaptersRead }
|
||||||
val newHsv = floatArrayOf(newHue, newSaturation, newValue)
|
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.staff?.map { it.meanScore }
|
||||||
palette.add(Color.HSVToColor(newHsv))
|
} ?: 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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return palette
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
9
app/src/main/res/drawable/ic_open_24.xml
Normal file
9
app/src/main/res/drawable/ic_open_24.xml
Normal 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>
|
|
@ -97,385 +97,9 @@
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/chartsContainer"
|
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_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
tools:listitem="@layout/item_chart" />
|
||||||
|
|
||||||
<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>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
54
app/src/main/res/layout/item_chart.xml
Normal file
54
app/src/main/res/layout/item_chart.xml
Normal 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>
|
Loading…
Add table
Add a link
Reference in a new issue