feat: compare user stats
This commit is contained in:
parent
d181dcf837
commit
5279b0cd65
13 changed files with 745 additions and 472 deletions
|
@ -44,7 +44,7 @@ android {
|
||||||
applicationIdSuffix ".beta" // keep as beta by popular request
|
applicationIdSuffix ".beta" // keep as beta by popular request
|
||||||
versionNameSuffix "-alpha02"
|
versionNameSuffix "-alpha02"
|
||||||
manifestPlaceholders = [icon_placeholder: "@mipmap/ic_launcher_alpha", icon_placeholder_round: "@mipmap/ic_launcher_alpha_round"]
|
manifestPlaceholders = [icon_placeholder: "@mipmap/ic_launcher_alpha", icon_placeholder_round: "@mipmap/ic_launcher_alpha_round"]
|
||||||
debuggable System.getenv("CI") == null
|
debuggable true
|
||||||
isDefault true
|
isDefault true
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
|
|
|
@ -608,12 +608,11 @@ class AnilistQueries {
|
||||||
|
|
||||||
|
|
||||||
private suspend fun bannerImage(type: String): String? {
|
private suspend fun bannerImage(type: String): String? {
|
||||||
//var image = loadData<BannerImage>("banner_$type")
|
val image = BannerImage(
|
||||||
val image: BannerImage? = BannerImage(
|
PrefManager.getCustomVal("banner_${type}_url", ""),
|
||||||
PrefManager.getCustomVal("banner_${type}_url", null),
|
|
||||||
PrefManager.getCustomVal("banner_${type}_time", 0L)
|
PrefManager.getCustomVal("banner_${type}_time", 0L)
|
||||||
)
|
)
|
||||||
if (image == null || image.checkTime()) {
|
if (image.url.isNullOrEmpty() || image.checkTime()) {
|
||||||
val response =
|
val response =
|
||||||
executeQuery<Query.MediaListCollection>("""{ MediaListCollection(userId: ${Anilist.userid}, type: $type, chunk:1,perChunk:25, sort: [SCORE_DESC,UPDATED_TIME_DESC]) { lists { entries{ media { id bannerImage } } } } } """)
|
executeQuery<Query.MediaListCollection>("""{ MediaListCollection(userId: ${Anilist.userid}, type: $type, chunk:1,perChunk:25, sort: [SCORE_DESC,UPDATED_TIME_DESC]) { lists { entries{ media { id bannerImage } } } } } """)
|
||||||
val random = response?.data?.mediaListCollection?.lists?.mapNotNull {
|
val random = response?.data?.mediaListCollection?.lists?.mapNotNull {
|
||||||
|
|
|
@ -299,12 +299,12 @@ class Query {
|
||||||
data class StatisticsResponse(
|
data class StatisticsResponse(
|
||||||
@SerialName("data")
|
@SerialName("data")
|
||||||
val data: Data
|
val data: Data
|
||||||
) {
|
): java.io.Serializable {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Data(
|
data class Data(
|
||||||
@SerialName("User")
|
@SerialName("User")
|
||||||
val user: StatisticsUser?
|
val user: StatisticsUser?
|
||||||
)
|
): java.io.Serializable
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
|
@ -1260,7 +1260,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
|
|
||||||
subtitle = intent.getSerialized("subtitle")
|
subtitle = intent.getSerialized("subtitle")
|
||||||
?: when (val subLang: String? =
|
?: when (val subLang: String? =
|
||||||
PrefManager.getCustomVal("subLang_${media.id}", null as String?)) {
|
PrefManager.getNullableCustomVal("subLang_${media.id}", null, String::class.java)) {
|
||||||
null -> {
|
null -> {
|
||||||
when (episode.selectedSubtitle) {
|
when (episode.selectedSubtitle) {
|
||||||
null -> null
|
null -> null
|
||||||
|
|
|
@ -68,7 +68,7 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
|
||||||
binding.subtitleTitle.setText(R.string.none)
|
binding.subtitleTitle.setText(R.string.none)
|
||||||
model.getMedia().observe(viewLifecycleOwner) { media ->
|
model.getMedia().observe(viewLifecycleOwner) { media ->
|
||||||
val mediaID: Int = media.id
|
val mediaID: Int = media.id
|
||||||
val selSubs = PrefManager.getCustomVal<String?>("subLang_${mediaID}", null)
|
val selSubs = PrefManager.getNullableCustomVal("subLang_${mediaID}", null, String::class.java)
|
||||||
if (episode.selectedSubtitle != null && selSubs != "None") {
|
if (episode.selectedSubtitle != null && selSubs != "None") {
|
||||||
binding.root.setCardBackgroundColor(TRANSPARENT)
|
binding.root.setCardBackgroundColor(TRANSPARENT)
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
|
||||||
model.getMedia().observe(viewLifecycleOwner) { media ->
|
model.getMedia().observe(viewLifecycleOwner) { media ->
|
||||||
val mediaID: Int = media.id
|
val mediaID: Int = media.id
|
||||||
val selSubs: String? =
|
val selSubs: String? =
|
||||||
PrefManager.getCustomVal<String?>("subLang_${mediaID}", null)
|
PrefManager.getNullableCustomVal("subLang_${mediaID}", null, String::class.java)
|
||||||
if (episode.selectedSubtitle != position - 1 && selSubs != subtitles[position - 1].language) {
|
if (episode.selectedSubtitle != position - 1 && selSubs != subtitles[position - 1].language) {
|
||||||
binding.root.setCardBackgroundColor(TRANSPARENT)
|
binding.root.setCardBackgroundColor(TRANSPARENT)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ import ani.dantotsu.loadImage
|
||||||
import ani.dantotsu.others.ImageViewDialog
|
import ani.dantotsu.others.ImageViewDialog
|
||||||
import ani.dantotsu.profile.ProfileActivity
|
import ani.dantotsu.profile.ProfileActivity
|
||||||
import ani.dantotsu.snackString
|
import ani.dantotsu.snackString
|
||||||
|
import ani.dantotsu.util.ColorEditor.Companion.adjustColorForContrast
|
||||||
|
import ani.dantotsu.util.ColorEditor.Companion.getContrastRatio
|
||||||
import com.xwray.groupie.GroupieAdapter
|
import com.xwray.groupie.GroupieAdapter
|
||||||
import com.xwray.groupie.Section
|
import com.xwray.groupie.Section
|
||||||
import com.xwray.groupie.viewbinding.BindableItem
|
import com.xwray.groupie.viewbinding.BindableItem
|
||||||
|
@ -300,25 +302,6 @@ class CommentItem(val comment: Comment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getLuminance(color: Int): Double {
|
|
||||||
val r = Color.red(color) / 255.0
|
|
||||||
val g = Color.green(color) / 255.0
|
|
||||||
val b = Color.blue(color) / 255.0
|
|
||||||
|
|
||||||
val rL = if (r <= 0.03928) r / 12.92 else ((r + 0.055) / 1.055).pow(2.4)
|
|
||||||
val gL = if (g <= 0.03928) g / 12.92 else ((g + 0.055) / 1.055).pow(2.4)
|
|
||||||
val bL = if (b <= 0.03928) b / 12.92 else ((b + 0.055) / 1.055).pow(2.4)
|
|
||||||
|
|
||||||
return 0.2126 * rL + 0.7152 * gL + 0.0722 * bL
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getContrastRatio(color1: Int, color2: Int): Double {
|
|
||||||
val l1 = getLuminance(color1)
|
|
||||||
val l2 = getLuminance(color2)
|
|
||||||
|
|
||||||
return if (l1 > l2) (l1 + 0.05) / (l2 + 0.05) else (l2 + 0.05) / (l1 + 0.05)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getAvatarColor(voteCount: Int, backgroundColor: Int): Pair<Int, Int> {
|
private fun getAvatarColor(voteCount: Int, backgroundColor: Int): Pair<Int, Int> {
|
||||||
val level = if (voteCount < 0) 0 else sqrt(abs(voteCount.toDouble()) / 0.8).toInt()
|
val level = if (voteCount < 0) 0 else sqrt(abs(voteCount.toDouble()) / 0.8).toInt()
|
||||||
val colorString = if (level > usernameColors.size - 1) usernameColors[usernameColors.size - 1] else usernameColors[level]
|
val colorString = if (level > usernameColors.size - 1) usernameColors[usernameColors.size - 1] else usernameColors[level]
|
||||||
|
@ -331,37 +314,6 @@ class CommentItem(val comment: Comment,
|
||||||
return Pair(color, level)
|
return Pair(color, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun adjustColorForContrast(originalColor: Int, backgroundColor: Int): Int {
|
|
||||||
var adjustedColor = originalColor
|
|
||||||
var contrastRatio = getContrastRatio(adjustedColor, backgroundColor)
|
|
||||||
val isBackgroundDark = getLuminance(backgroundColor) < 0.5
|
|
||||||
|
|
||||||
while (contrastRatio < 4.5) {
|
|
||||||
// Adjust brightness by modifying the RGB values
|
|
||||||
val r = Color.red(adjustedColor)
|
|
||||||
val g = Color.green(adjustedColor)
|
|
||||||
val b = Color.blue(adjustedColor)
|
|
||||||
|
|
||||||
// Calculate the amount to adjust
|
|
||||||
val adjustment = if (isBackgroundDark) 10 else -10
|
|
||||||
|
|
||||||
// Adjust the color
|
|
||||||
val newR = (r + adjustment).coerceIn(0, 255)
|
|
||||||
val newG = (g + adjustment).coerceIn(0, 255)
|
|
||||||
val newB = (b + adjustment).coerceIn(0, 255)
|
|
||||||
|
|
||||||
adjustedColor = Color.rgb(newR, newG, newB)
|
|
||||||
contrastRatio = getContrastRatio(adjustedColor, backgroundColor)
|
|
||||||
|
|
||||||
// Break the loop if the color adjustment does not change (to avoid infinite loop)
|
|
||||||
if (newR == r && newG == g && newB == b) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return adjustedColor
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the dialog for yes/no confirmation
|
* Builds the dialog for yes/no confirmation
|
||||||
* no doesn't do anything, yes calls the callback
|
* no doesn't do anything, yes calls the callback
|
||||||
|
|
|
@ -440,7 +440,7 @@ class MangaReadAdapter(
|
||||||
if (media.manga?.chapters != null) {
|
if (media.manga?.chapters != null) {
|
||||||
val chapters = media.manga.chapters!!.keys.toTypedArray()
|
val chapters = media.manga.chapters!!.keys.toTypedArray()
|
||||||
val anilistEp = (media.userProgress ?: 0).plus(1)
|
val anilistEp = (media.userProgress ?: 0).plus(1)
|
||||||
val appEp = PrefManager.getCustomVal<String?>("${media.id}_current_chp", null)
|
val appEp = PrefManager.getNullableCustomVal("${media.id}_current_chp", null, String::class.java)
|
||||||
?.toIntOrNull() ?: 1
|
?.toIntOrNull() ?: 1
|
||||||
var continueEp = (if (anilistEp > appEp) anilistEp else appEp).toString()
|
var continueEp = (if (anilistEp > appEp) anilistEp else appEp).toString()
|
||||||
val filteredChapters = chapters.filter { chapterKey ->
|
val filteredChapters = chapters.filter { chapterKey ->
|
||||||
|
|
|
@ -291,7 +291,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
|
||||||
applySettings()
|
applySettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
val cfi = PrefManager.getCustomVal("${sanitizedBookId}_progress", null as String?)
|
val cfi = PrefManager.getNullableCustomVal("${sanitizedBookId}_progress", null, String::class.java)
|
||||||
|
|
||||||
cfi?.let { binding.bookReader.goto(it) }
|
cfi?.let { binding.bookReader.goto(it) }
|
||||||
binding.progress.visibility = View.GONE
|
binding.progress.visibility = View.GONE
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ani.dantotsu.profile
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
|
import ani.dantotsu.util.ColorEditor
|
||||||
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartModel
|
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartModel
|
||||||
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartStackingType
|
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartStackingType
|
||||||
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartType
|
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartType
|
||||||
|
@ -17,6 +18,7 @@ import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAScrollablePlotAre
|
||||||
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAStyle
|
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.github.aachartmodel.aainfographics.aatools.AAColor
|
||||||
|
import com.github.aachartmodel.aainfographics.aatools.AAGradientColor
|
||||||
|
|
||||||
class ChartBuilder {
|
class ChartBuilder {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -25,25 +27,30 @@ class ChartBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class StatType {
|
enum class StatType {
|
||||||
COUNT, TIME, MEAN_SCORE
|
COUNT, TIME, AVG_SCORE
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class MediaType {
|
enum class MediaType {
|
||||||
ANIME, MANGA
|
ANIME, MANGA
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class ChartPacket(
|
||||||
|
val username: String,
|
||||||
|
val names: List<Any>,
|
||||||
|
val statData: List<Number>
|
||||||
|
)
|
||||||
|
|
||||||
fun buildChart(
|
fun buildChart(
|
||||||
context: Context,
|
context: Context,
|
||||||
chartType: ChartType,
|
passedChartType: ChartType,
|
||||||
aaChartType: AAChartType,
|
passedAaChartType: AAChartType,
|
||||||
statType: StatType,
|
statType: StatType,
|
||||||
mediaType: MediaType,
|
mediaType: MediaType,
|
||||||
names: List<Any>,
|
chartPackets: List<ChartPacket>,
|
||||||
statData: List<Number>,
|
|
||||||
xAxisName: String = "X Axis",
|
xAxisName: String = "X Axis",
|
||||||
xAxisTickInterval: Int? = null,
|
xAxisTickInterval: Int? = null,
|
||||||
polar: Boolean = false,
|
polar: Boolean = false,
|
||||||
categories: List<String>? = null,
|
passedCategories: List<String>? = null,
|
||||||
scrollPos: Float? = null,
|
scrollPos: Float? = null,
|
||||||
): AAOptions {
|
): AAOptions {
|
||||||
val typedValue = TypedValue()
|
val typedValue = TypedValue()
|
||||||
|
@ -53,7 +60,18 @@ class ChartBuilder {
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
val primaryColor = typedValue.data
|
val primaryColor = typedValue.data
|
||||||
val palette = generateColorPalette(primaryColor, names.size)
|
var chartType = passedChartType
|
||||||
|
var aaChartType = passedAaChartType
|
||||||
|
var categories = passedCategories
|
||||||
|
if (chartType == ChartType.OneDimensional && chartPackets.size != 1) {
|
||||||
|
//need to convert to 2D
|
||||||
|
chartType = ChartType.TwoDimensional
|
||||||
|
aaChartType = AAChartType.Column
|
||||||
|
categories = chartPackets[0].names.map { it.toString() }
|
||||||
|
}
|
||||||
|
|
||||||
|
val namesMax = chartPackets.maxOf { it.names.size }
|
||||||
|
val palette = ColorEditor.generateColorPalette(primaryColor, namesMax)
|
||||||
val aaChartModel = when (chartType) {
|
val aaChartModel = when (chartType) {
|
||||||
ChartType.OneDimensional -> {
|
ChartType.OneDimensional -> {
|
||||||
val chart = AAChartModel()
|
val chart = AAChartModel()
|
||||||
|
@ -61,14 +79,26 @@ class ChartBuilder {
|
||||||
.subtitle(getTypeName(statType, mediaType))
|
.subtitle(getTypeName(statType, mediaType))
|
||||||
.zoomType(AAChartZoomType.None)
|
.zoomType(AAChartZoomType.None)
|
||||||
.dataLabelsEnabled(true)
|
.dataLabelsEnabled(true)
|
||||||
.series(
|
val elements: MutableList<Any> = mutableListOf()
|
||||||
get1DElements(
|
chartPackets.forEachIndexed { index, chartPacket ->
|
||||||
names,
|
val element = AASeriesElement()
|
||||||
statData,
|
.name(chartPacket.username)
|
||||||
palette,
|
.data(
|
||||||
primaryColor
|
get1DElements(
|
||||||
)
|
chartPacket.names,
|
||||||
)
|
chartPacket.statData,
|
||||||
|
palette
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (index == 0) {
|
||||||
|
element.color(primaryColor)
|
||||||
|
} else {
|
||||||
|
element.color(ColorEditor.oppositeColor(primaryColor))
|
||||||
|
}
|
||||||
|
elements.add(element)
|
||||||
|
|
||||||
|
}
|
||||||
|
chart.series(elements.toTypedArray())
|
||||||
xAxisTickInterval?.let { chart.xAxisTickInterval(it) }
|
xAxisTickInterval?.let { chart.xAxisTickInterval(it) }
|
||||||
categories?.let { chart.categories(it.toTypedArray()) }
|
categories?.let { chart.categories(it.toTypedArray()) }
|
||||||
chart
|
chart
|
||||||
|
@ -83,9 +113,31 @@ class ChartBuilder {
|
||||||
.zoomType(AAChartZoomType.None)
|
.zoomType(AAChartZoomType.None)
|
||||||
.dataLabelsEnabled(false)
|
.dataLabelsEnabled(false)
|
||||||
.yAxisTitle(getTypeName(statType, mediaType))
|
.yAxisTitle(getTypeName(statType, mediaType))
|
||||||
.stacking(AAChartStackingType.Normal)
|
if (chartPackets.size == 1) {
|
||||||
.series(get2DElements(names, statData, primaryColor))
|
chart.colorsTheme(hexColorsArray)
|
||||||
.colorsTheme(hexColorsArray)
|
}
|
||||||
|
|
||||||
|
val elements: MutableList<AASeriesElement> = mutableListOf()
|
||||||
|
chartPackets.forEachIndexed { index, chartPacket ->
|
||||||
|
val element = get2DElements(
|
||||||
|
chartPacket.names,
|
||||||
|
chartPacket.statData,
|
||||||
|
chartPackets.size == 1
|
||||||
|
)
|
||||||
|
element.name(chartPacket.username)
|
||||||
|
|
||||||
|
if (index == 0) {
|
||||||
|
element.color(AAColor.rgbaColor(Color.red(primaryColor), Color.green(primaryColor), Color.blue(primaryColor), 0.9f))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
element.color(AAColor.rgbaColor(Color.red(ColorEditor.oppositeColor(primaryColor)), Color.green(ColorEditor.oppositeColor(primaryColor)), Color.blue(ColorEditor.oppositeColor(primaryColor)), 0.9f))
|
||||||
|
}
|
||||||
|
if (chartPackets.size == 1) {
|
||||||
|
element.fillColor(AAColor.rgbaColor(Color.red(primaryColor), Color.green(primaryColor), Color.blue(primaryColor), 0.9f))
|
||||||
|
}
|
||||||
|
elements.add(element)
|
||||||
|
}
|
||||||
|
chart.series(elements.toTypedArray())
|
||||||
|
|
||||||
xAxisTickInterval?.let { chart.xAxisTickInterval(it) }
|
xAxisTickInterval?.let { chart.xAxisTickInterval(it) }
|
||||||
categories?.let { chart.categories(it.toTypedArray()) }
|
categories?.let { chart.categories(it.toTypedArray()) }
|
||||||
|
@ -101,24 +153,32 @@ class ChartBuilder {
|
||||||
getToolTipFunction(
|
getToolTipFunction(
|
||||||
chartType,
|
chartType,
|
||||||
xAxisName,
|
xAxisName,
|
||||||
getTypeName(statType, mediaType)
|
getTypeName(statType, mediaType),
|
||||||
|
chartPackets.size
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
if (chartPackets.size > 1) {
|
||||||
|
useHTML(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
aaOptions.legend?.apply {
|
aaOptions.legend?.apply {
|
||||||
enabled(true)
|
enabled(true)
|
||||||
.labelFormat = "{name}: {y}"
|
.labelFormat = "{name}"
|
||||||
}
|
}
|
||||||
aaOptions.plotOptions?.series?.connectNulls(true)
|
aaOptions.plotOptions?.series?.connectNulls(false)
|
||||||
|
aaOptions.plotOptions?.series?.stacking(AAChartStackingType.False)
|
||||||
aaOptions.chart?.panning = true
|
aaOptions.chart?.panning = true
|
||||||
|
|
||||||
scrollPos?.let {
|
scrollPos?.let {
|
||||||
aaOptions.chart?.scrollablePlotArea(AAScrollablePlotArea().scrollPositionX(scrollPos))
|
aaOptions.chart?.scrollablePlotArea(AAScrollablePlotArea().scrollPositionX(scrollPos))
|
||||||
aaOptions.chart?.scrollablePlotArea?.minWidth((context.resources.displayMetrics.widthPixels.toFloat() / context.resources.displayMetrics.density) * (names.size.toFloat() / 18.0f))
|
aaOptions.chart?.scrollablePlotArea?.minWidth((context.resources.displayMetrics.widthPixels.toFloat() / context.resources.displayMetrics.density) * (namesMax.toFloat() / 18.0f))
|
||||||
}
|
}
|
||||||
val min = ((statData.minOfOrNull { it.toDouble() } ?: 0.0) - 1.0).coerceAtLeast(0.0)
|
val allStatData = chartPackets.flatMap { it.statData }
|
||||||
val max = statData.maxOfOrNull { it.toDouble() } ?: 0.0
|
val min = (allStatData.minOfOrNull { it.toDouble() } ?: 0.0) - 1.0
|
||||||
val aaYaxis = AAYAxis().min(min).max(max)
|
val coercedMin = min.coerceAtLeast(0.0)
|
||||||
|
val max = allStatData.maxOfOrNull { it.toDouble() } ?: 0.0
|
||||||
|
|
||||||
|
val aaYaxis = AAYAxis().min(coercedMin).max(max)
|
||||||
val tickInterval = when (max) {
|
val tickInterval = when (max) {
|
||||||
in 0.0..10.0 -> 1.0
|
in 0.0..10.0 -> 1.0
|
||||||
in 10.0..30.0 -> 5.0
|
in 10.0..30.0 -> 5.0
|
||||||
|
@ -138,34 +198,25 @@ class ChartBuilder {
|
||||||
private fun get2DElements(
|
private fun get2DElements(
|
||||||
names: List<Any>,
|
names: List<Any>,
|
||||||
statData: List<Any>,
|
statData: List<Any>,
|
||||||
primaryColor: Int
|
colorByPoint: Boolean
|
||||||
): Array<Any> {
|
): AASeriesElement {
|
||||||
val statValues = mutableListOf<Array<Any>>()
|
val statValues = mutableListOf<Array<Any>>()
|
||||||
for (i in statData.indices) {
|
for (i in statData.indices) {
|
||||||
statValues.add(arrayOf(names[i], statData[i], statData[i]))
|
statValues.add(arrayOf(names[i], statData[i], statData[i]))
|
||||||
}
|
}
|
||||||
return arrayOf(
|
return AASeriesElement()
|
||||||
AASeriesElement().name("Score")
|
|
||||||
.data(statValues.toTypedArray())
|
.data(statValues.toTypedArray())
|
||||||
.dataLabels(
|
.dataLabels(
|
||||||
AADataLabels()
|
AADataLabels()
|
||||||
.enabled(false)
|
.enabled(false)
|
||||||
)
|
)
|
||||||
.colorByPoint(true)
|
.colorByPoint(colorByPoint)
|
||||||
.fillColor(AAColor.rgbaColor(
|
|
||||||
Color.red(primaryColor),
|
|
||||||
Color.green(primaryColor),
|
|
||||||
Color.blue(primaryColor),
|
|
||||||
0.9f
|
|
||||||
))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun get1DElements(
|
private fun get1DElements(
|
||||||
names: List<Any>,
|
names: List<Any>,
|
||||||
statData: List<Number>,
|
statData: List<Number>,
|
||||||
colors: List<Int>,
|
colors: List<Int>
|
||||||
primaryColor: Int
|
|
||||||
): Array<Any> {
|
): Array<Any> {
|
||||||
val statDataElements = mutableListOf<AADataElement>()
|
val statDataElements = mutableListOf<AADataElement>()
|
||||||
for (i in statData.indices) {
|
for (i in statData.indices) {
|
||||||
|
@ -193,44 +244,17 @@ class ChartBuilder {
|
||||||
}
|
}
|
||||||
statDataElements.add(element)
|
statDataElements.add(element)
|
||||||
}
|
}
|
||||||
return arrayOf(
|
return statDataElements.toTypedArray()
|
||||||
AASeriesElement().name("Score").color(primaryColor)
|
|
||||||
.data(statDataElements.toTypedArray())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getTypeName(statType: StatType, mediaType: MediaType): String {
|
private fun getTypeName(statType: StatType, mediaType: MediaType): String {
|
||||||
return when (statType) {
|
return when (statType) {
|
||||||
StatType.COUNT -> "Count"
|
StatType.COUNT -> "Count"
|
||||||
StatType.TIME -> if (mediaType == MediaType.ANIME) "Hours Watched" else "Chapters Read"
|
StatType.TIME -> if (mediaType == MediaType.ANIME) "Hours Watched" else "Chapters Read"
|
||||||
StatType.MEAN_SCORE -> "Mean Score"
|
StatType.AVG_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) {
|
private fun setColors(aaOptions: AAOptions, context: Context, primaryColor: Int) {
|
||||||
val backgroundColor = TypedValue()
|
val backgroundColor = TypedValue()
|
||||||
context.theme.resolveAttribute(
|
context.theme.resolveAttribute(
|
||||||
|
@ -265,15 +289,15 @@ class ChartBuilder {
|
||||||
aaOptions.chart?.backgroundColor(backgroundStyle.color)
|
aaOptions.chart?.backgroundColor(backgroundStyle.color)
|
||||||
aaOptions.tooltip?.backgroundColor(
|
aaOptions.tooltip?.backgroundColor(
|
||||||
AAColor.rgbaColor(
|
AAColor.rgbaColor(
|
||||||
Color.red(primaryColor),
|
Color.red(backgroundColor.data),
|
||||||
Color.green(primaryColor),
|
Color.green(backgroundColor.data),
|
||||||
Color.blue(primaryColor),
|
Color.blue(backgroundColor.data),
|
||||||
0.9f
|
0.9f
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
aaOptions.title?.style(onBackgroundStyle)
|
aaOptions.title?.style(onBackgroundStyle)
|
||||||
aaOptions.subtitle?.style(onBackgroundStyle)
|
aaOptions.subtitle?.style(onBackgroundStyle)
|
||||||
aaOptions.tooltip?.style(backgroundStyle)
|
aaOptions.tooltip?.style(onBackgroundStyle)
|
||||||
aaOptions.credits?.style(onBackgroundStyle)
|
aaOptions.credits?.style(onBackgroundStyle)
|
||||||
aaOptions.xAxis?.labels?.style(onBackgroundStyle)
|
aaOptions.xAxis?.labels?.style(onBackgroundStyle)
|
||||||
aaOptions.yAxis?.labels?.style(onBackgroundStyle)
|
aaOptions.yAxis?.labels?.style(onBackgroundStyle)
|
||||||
|
@ -287,7 +311,8 @@ class ChartBuilder {
|
||||||
private fun getToolTipFunction(
|
private fun getToolTipFunction(
|
||||||
chartType: ChartType,
|
chartType: ChartType,
|
||||||
type: String,
|
type: String,
|
||||||
typeName: String
|
typeName: String,
|
||||||
|
chartSize: Int
|
||||||
): String {
|
): String {
|
||||||
return when (chartType) {
|
return when (chartType) {
|
||||||
ChartType.OneDimensional -> {
|
ChartType.OneDimensional -> {
|
||||||
|
@ -305,7 +330,8 @@ class ChartBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
ChartType.TwoDimensional -> {
|
ChartType.TwoDimensional -> {
|
||||||
"""
|
if (chartSize == 1) {
|
||||||
|
"""
|
||||||
function () {
|
function () {
|
||||||
return '$type: ' +
|
return '$type: ' +
|
||||||
this.x +
|
this.x +
|
||||||
|
@ -314,8 +340,34 @@ class ChartBuilder {
|
||||||
this.y
|
this.y
|
||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
} else {
|
||||||
|
"""
|
||||||
|
function() {
|
||||||
|
let wholeContentStr = '<span style=\"' + 'color:gray; font-size:13px\"' + '>◉${type}: ' + this.x + '</span><br/>';
|
||||||
|
if (this.points) {
|
||||||
|
let length = this.points.length;
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
let thisPoint = this.points[i];
|
||||||
|
let yValue = thisPoint.y;
|
||||||
|
if (yValue != 0) {
|
||||||
|
let spanStyleStartStr = '<span style=\"' + 'color: ' + thisPoint.color + '; font-size:13px\"' + '>◉ ';
|
||||||
|
let spanStyleEndStr = '</span> <br/>';
|
||||||
|
wholeContentStr += spanStyleStartStr + thisPoint.series.name + ': ' + yValue + spanStyleEndStr;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let spanStyleStartStr = '<span style=\"' + 'color: ' + this.point.color + '; font-size:13px\"' + '>◉ ';
|
||||||
|
let spanStyleEndStr = '</span> <br/>';
|
||||||
|
wholeContentStr += spanStyleStartStr + this.point.series.name + ': ' + this.point.y + spanStyleEndStr;
|
||||||
|
}
|
||||||
|
return wholeContentStr;
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,19 +15,19 @@ import ani.dantotsu.databinding.FragmentStatisticsBinding
|
||||||
import ani.dantotsu.profile.ChartBuilder.Companion.ChartType
|
import ani.dantotsu.profile.ChartBuilder.Companion.ChartType
|
||||||
import ani.dantotsu.profile.ChartBuilder.Companion.StatType
|
import ani.dantotsu.profile.ChartBuilder.Companion.StatType
|
||||||
import ani.dantotsu.profile.ChartBuilder.Companion.MediaType
|
import ani.dantotsu.profile.ChartBuilder.Companion.MediaType
|
||||||
|
import ani.dantotsu.profile.ChartBuilder.Companion.ChartPacket
|
||||||
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartType
|
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartType
|
||||||
import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAYAxis
|
|
||||||
import com.xwray.groupie.GroupieAdapter
|
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() :
|
class StatsFragment :
|
||||||
Fragment() {
|
Fragment() {
|
||||||
private lateinit var binding: FragmentStatisticsBinding
|
private lateinit var binding: FragmentStatisticsBinding
|
||||||
private var adapter: GroupieAdapter = GroupieAdapter()
|
private var adapter: GroupieAdapter = GroupieAdapter()
|
||||||
private var stats: Query.StatisticsResponse? = null
|
private var stats: MutableList<Query.StatisticsUser?> = mutableListOf()
|
||||||
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 lateinit var user: Query.UserProfile
|
private lateinit var user: Query.UserProfile
|
||||||
|
@ -52,25 +52,55 @@ class StatsFragment() :
|
||||||
binding.statisticList.isNestedScrollingEnabled = false
|
binding.statisticList.isNestedScrollingEnabled = false
|
||||||
binding.statisticList.layoutManager = LinearLayoutManager(requireContext())
|
binding.statisticList.layoutManager = LinearLayoutManager(requireContext())
|
||||||
binding.statisticProgressBar.visibility = View.VISIBLE
|
binding.statisticProgressBar.visibility = View.VISIBLE
|
||||||
|
binding.compare.visibility = if (user.id == Anilist.userid) View.GONE else View.VISIBLE
|
||||||
|
|
||||||
binding.sourceType.setAdapter(
|
binding.sourceType.setAdapter(
|
||||||
ArrayAdapter(
|
ArrayAdapter(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
R.layout.item_dropdown,
|
R.layout.item_dropdown,
|
||||||
MediaType.entries.map { it.name.uppercase(Locale.ROOT) }
|
MediaType.entries.map { it.name.uppercase(Locale.ROOT).replace("_", " ") }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
binding.sourceFilter.setAdapter(
|
binding.sourceFilter.setAdapter(
|
||||||
ArrayAdapter(
|
ArrayAdapter(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
R.layout.item_dropdown,
|
R.layout.item_dropdown,
|
||||||
StatType.entries.map { it.name.uppercase(Locale.ROOT) }
|
StatType.entries.map { it.name.uppercase(Locale.ROOT).replace("_", " ") }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
binding.compare.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
if (isChecked) {
|
||||||
|
activity.lifecycleScope.launch {
|
||||||
|
if (Anilist.userid != null) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
binding.statisticProgressBar.visibility = View.VISIBLE
|
||||||
|
binding.statisticList.visibility = View.GONE
|
||||||
|
}
|
||||||
|
val userStats =
|
||||||
|
Anilist.query.getUserStatistics(Anilist.userid!!)?.data?.user
|
||||||
|
if (userStats != null) {
|
||||||
|
stats.add(userStats)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
loadStats(type == MediaType.ANIME)
|
||||||
|
binding.statisticProgressBar.visibility = View.GONE
|
||||||
|
binding.statisticList.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stats.removeAll(
|
||||||
|
stats.filter { it?.id == Anilist.userid }
|
||||||
|
)
|
||||||
|
loadStats(type == MediaType.ANIME)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
binding.filterContainer.visibility = View.GONE
|
binding.filterContainer.visibility = View.GONE
|
||||||
activity.lifecycleScope.launch {
|
activity.lifecycleScope.launch {
|
||||||
stats = Anilist.query.getUserStatistics(user.id)
|
stats.clear()
|
||||||
|
stats.add(Anilist.query.getUserStatistics(user.id)?.data?.user)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
binding.filterContainer.visibility = View.VISIBLE
|
binding.filterContainer.visibility = View.VISIBLE
|
||||||
binding.sourceType.setOnItemClickListener { _, _, i, _ ->
|
binding.sourceType.setOnItemClickListener { _, _, i, _ ->
|
||||||
|
@ -116,99 +146,114 @@ class StatsFragment() :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadFormatChart(anime: Boolean) {
|
private fun loadFormatChart(anime: Boolean) {
|
||||||
val names: List<String> = if (anime) {
|
val chartPackets = mutableListOf<ChartPacket>()
|
||||||
stats?.data?.user?.statistics?.anime?.formats?.map { it.format } ?: emptyList()
|
stats.forEach {stat ->
|
||||||
} else {
|
val names: List<String> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.manga?.formats?.map { it.format } ?: emptyList()
|
stat?.statistics?.anime?.formats?.map { it.format } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
stat?.statistics?.manga?.formats?.map { it.format } ?: emptyList()
|
||||||
|
}
|
||||||
|
val values: List<Number> = if (anime) {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.anime?.formats?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.anime?.formats?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.anime?.formats?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
} else {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.manga?.formats?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.manga?.formats?.map { it.chaptersRead }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.manga?.formats?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
if (names.isNotEmpty() && values.isNotEmpty()) {
|
||||||
|
chartPackets.add(ChartPacket(stat?.name?:"Unknown", names, values))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val values: List<Number> = if (anime) {
|
if (chartPackets.isNotEmpty()) {
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.formats?.map { it.count }
|
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.anime?.formats?.map { it.minutesWatched / 60 }
|
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.formats?.map { it.meanScore }
|
|
||||||
} ?: emptyList()
|
|
||||||
} else {
|
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.formats?.map { it.count }
|
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.manga?.formats?.map { it.chaptersRead }
|
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.formats?.map { it.meanScore }
|
|
||||||
} ?: emptyList()
|
|
||||||
}
|
|
||||||
if (names.isNotEmpty() || values.isNotEmpty()) {
|
|
||||||
val formatChart = ChartBuilder.buildChart(
|
val formatChart = ChartBuilder.buildChart(
|
||||||
activity,
|
activity,
|
||||||
ChartType.OneDimensional,
|
ChartType.OneDimensional,
|
||||||
AAChartType.Pie,
|
AAChartType.Pie,
|
||||||
statType,
|
statType,
|
||||||
type,
|
type,
|
||||||
names,
|
chartPackets,
|
||||||
values
|
|
||||||
)
|
)
|
||||||
adapter.add(ChartItem("Format", formatChart, activity))
|
adapter.add(ChartItem("Format", formatChart, activity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadStatusChart(anime: Boolean) {
|
private fun loadStatusChart(anime: Boolean) {
|
||||||
val names: List<String> = if (anime) {
|
val chartPackets = mutableListOf<ChartPacket>()
|
||||||
stats?.data?.user?.statistics?.anime?.statuses?.map { it.status } ?: emptyList()
|
stats.forEach { stat ->
|
||||||
} else {
|
val names: List<String> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.manga?.statuses?.map { it.status } ?: emptyList()
|
stat?.statistics?.anime?.statuses?.map { it.status } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
stat?.statistics?.manga?.statuses?.map { it.status } ?: emptyList()
|
||||||
|
}
|
||||||
|
val values: List<Number> = if (anime) {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.anime?.statuses?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.anime?.statuses?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.anime?.statuses?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
} else {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.manga?.statuses?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.manga?.statuses?.map { it.chaptersRead }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.manga?.statuses?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
if (names.isNotEmpty() && values.isNotEmpty()) {
|
||||||
|
chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val values: List<Number> = if (anime) {
|
if (chartPackets.isNotEmpty()) {
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.statuses?.map { it.count }
|
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.anime?.statuses?.map { it.minutesWatched / 60 }
|
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.statuses?.map { it.meanScore }
|
|
||||||
} ?: emptyList()
|
|
||||||
} else {
|
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.statuses?.map { it.count }
|
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.manga?.statuses?.map { it.chaptersRead }
|
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.statuses?.map { it.meanScore }
|
|
||||||
} ?: emptyList()
|
|
||||||
}
|
|
||||||
if (names.isNotEmpty() || values.isNotEmpty()) {
|
|
||||||
val statusChart = ChartBuilder.buildChart(
|
val statusChart = ChartBuilder.buildChart(
|
||||||
activity,
|
activity,
|
||||||
ChartType.OneDimensional,
|
ChartType.OneDimensional,
|
||||||
AAChartType.Funnel,
|
AAChartType.Funnel,
|
||||||
statType,
|
statType,
|
||||||
type,
|
type,
|
||||||
names,
|
chartPackets
|
||||||
values
|
|
||||||
)
|
)
|
||||||
adapter.add(ChartItem("Status", statusChart, activity))
|
adapter.add(ChartItem("Status", statusChart, activity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadScoreChart(anime: Boolean) {
|
private fun loadScoreChart(anime: Boolean) {
|
||||||
val names: List<Int> = if (anime) {
|
val chartPackets = mutableListOf<ChartPacket>()
|
||||||
stats?.data?.user?.statistics?.anime?.scores?.map { it.score } ?: emptyList()
|
stats.forEach { stat ->
|
||||||
} else {
|
val names: List<Int> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.manga?.scores?.map { it.score } ?: emptyList()
|
stat?.statistics?.anime?.scores?.map { it.score } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
stat?.statistics?.manga?.scores?.map { it.score } ?: emptyList()
|
||||||
|
}
|
||||||
|
val values: List<Number> = if (anime) {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.anime?.scores?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.anime?.scores?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.anime?.scores?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
} else {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.manga?.scores?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.manga?.scores?.map { it.chaptersRead }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.manga?.scores?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
|
chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val values: List<Number> = if (anime) {
|
if (chartPackets.isNotEmpty()) {
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.scores?.map { it.count }
|
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.anime?.scores?.map { it.minutesWatched / 60 }
|
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.scores?.map { it.meanScore }
|
|
||||||
} ?: emptyList()
|
|
||||||
} else {
|
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.scores?.map { it.count }
|
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.manga?.scores?.map { it.chaptersRead }
|
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.scores?.map { it.meanScore }
|
|
||||||
} ?: emptyList()
|
|
||||||
}
|
|
||||||
if (names.isNotEmpty() || values.isNotEmpty()) {
|
|
||||||
val scoreChart = ChartBuilder.buildChart(
|
val scoreChart = ChartBuilder.buildChart(
|
||||||
activity,
|
activity,
|
||||||
ChartType.TwoDimensional,
|
ChartType.TwoDimensional,
|
||||||
AAChartType.Column,
|
AAChartType.Column,
|
||||||
statType,
|
statType,
|
||||||
type,
|
type,
|
||||||
names,
|
chartPackets,
|
||||||
values,
|
|
||||||
xAxisName = "Score",
|
xAxisName = "Score",
|
||||||
)
|
)
|
||||||
adapter.add(ChartItem("Score", scoreChart, activity))
|
adapter.add(ChartItem("Score", scoreChart, activity))
|
||||||
|
@ -216,35 +261,40 @@ class StatsFragment() :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadLengthChart(anime: Boolean) {
|
private fun loadLengthChart(anime: Boolean) {
|
||||||
val names: List<String> = if (anime) {
|
val chartPackets = mutableListOf<ChartPacket>()
|
||||||
stats?.data?.user?.statistics?.anime?.lengths?.map { it.length ?: "unknown" }
|
stats.forEach { stat ->
|
||||||
?: emptyList()
|
val names: List<String> = if (anime) {
|
||||||
} else {
|
stat?.statistics?.anime?.lengths?.map { it.length ?: "unknown" }
|
||||||
stats?.data?.user?.statistics?.manga?.lengths?.map { it.length ?: "unknown" }
|
?: emptyList()
|
||||||
?: emptyList()
|
} else {
|
||||||
|
stat?.statistics?.manga?.lengths?.map { it.length ?: "unknown" }
|
||||||
|
?: emptyList()
|
||||||
|
}
|
||||||
|
val values: List<Number> = if (anime) {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.anime?.lengths?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.anime?.lengths?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.anime?.lengths?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
} else {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.manga?.lengths?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.manga?.lengths?.map { it.chaptersRead }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.manga?.lengths?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
|
chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val values: List<Number> = if (anime) {
|
if (chartPackets.isNotEmpty()) {
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.lengths?.map { it.count }
|
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.anime?.lengths?.map { it.minutesWatched / 60 }
|
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.lengths?.map { it.meanScore }
|
|
||||||
} ?: emptyList()
|
|
||||||
} else {
|
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.lengths?.map { it.count }
|
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.manga?.lengths?.map { it.chaptersRead }
|
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.lengths?.map { it.meanScore }
|
|
||||||
} ?: emptyList()
|
|
||||||
}
|
|
||||||
if (names.isNotEmpty() || values.isNotEmpty()) {
|
|
||||||
val lengthChart = ChartBuilder.buildChart(
|
val lengthChart = ChartBuilder.buildChart(
|
||||||
activity,
|
activity,
|
||||||
ChartType.OneDimensional,
|
ChartType.OneDimensional,
|
||||||
AAChartType.Pyramid,
|
AAChartType.Pyramid,
|
||||||
statType,
|
statType,
|
||||||
type,
|
type,
|
||||||
names,
|
chartPackets,
|
||||||
values,
|
|
||||||
xAxisName = "Length",
|
xAxisName = "Length",
|
||||||
)
|
)
|
||||||
adapter.add(ChartItem("Length", lengthChart, activity))
|
adapter.add(ChartItem("Length", lengthChart, activity))
|
||||||
|
@ -252,35 +302,40 @@ class StatsFragment() :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadReleaseYearChart(anime: Boolean) {
|
private fun loadReleaseYearChart(anime: Boolean) {
|
||||||
val names: List<Number> = if (anime) {
|
val chartPackets = mutableListOf<ChartPacket>()
|
||||||
stats?.data?.user?.statistics?.anime?.releaseYears?.map { it.releaseYear }
|
stats.forEach { stat ->
|
||||||
?: emptyList()
|
val names: List<Number> = if (anime) {
|
||||||
} else {
|
stat?.statistics?.anime?.releaseYears?.map { it.releaseYear }
|
||||||
stats?.data?.user?.statistics?.manga?.releaseYears?.map { it.releaseYear }
|
?: emptyList()
|
||||||
?: emptyList()
|
} else {
|
||||||
|
stat?.statistics?.manga?.releaseYears?.map { it.releaseYear }
|
||||||
|
?: emptyList()
|
||||||
|
}
|
||||||
|
val values: List<Number> = if (anime) {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.anime?.releaseYears?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.anime?.releaseYears?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.anime?.releaseYears?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
} else {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.manga?.releaseYears?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.manga?.releaseYears?.map { it.chaptersRead }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.manga?.releaseYears?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
|
chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val values: List<Number> = if (anime) {
|
if (chartPackets.isNotEmpty()) {
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.releaseYears?.map { it.count }
|
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.anime?.releaseYears?.map { it.minutesWatched / 60 }
|
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.releaseYears?.map { it.meanScore }
|
|
||||||
} ?: emptyList()
|
|
||||||
} else {
|
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.releaseYears?.map { it.count }
|
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.manga?.releaseYears?.map { it.chaptersRead }
|
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.releaseYears?.map { it.meanScore }
|
|
||||||
} ?: emptyList()
|
|
||||||
}
|
|
||||||
if (names.isNotEmpty() || values.isNotEmpty()) {
|
|
||||||
val releaseYearChart = ChartBuilder.buildChart(
|
val releaseYearChart = ChartBuilder.buildChart(
|
||||||
activity,
|
activity,
|
||||||
ChartType.TwoDimensional,
|
ChartType.TwoDimensional,
|
||||||
AAChartType.Bubble,
|
AAChartType.Bubble,
|
||||||
statType,
|
statType,
|
||||||
type,
|
type,
|
||||||
names,
|
chartPackets,
|
||||||
values,
|
|
||||||
xAxisName = "Year",
|
xAxisName = "Year",
|
||||||
)
|
)
|
||||||
adapter.add(ChartItem("Release Year", releaseYearChart, activity))
|
adapter.add(ChartItem("Release Year", releaseYearChart, activity))
|
||||||
|
@ -288,33 +343,38 @@ class StatsFragment() :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadStartYearChart(anime: Boolean) {
|
private fun loadStartYearChart(anime: Boolean) {
|
||||||
val names: List<Number> = if (anime) {
|
val chartPackets = mutableListOf<ChartPacket>()
|
||||||
stats?.data?.user?.statistics?.anime?.startYears?.map { it.startYear } ?: emptyList()
|
stats.forEach { stat ->
|
||||||
} else {
|
val names: List<Number> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.manga?.startYears?.map { it.startYear } ?: emptyList()
|
stat?.statistics?.anime?.startYears?.map { it.startYear } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
stat?.statistics?.manga?.startYears?.map { it.startYear } ?: emptyList()
|
||||||
|
}
|
||||||
|
val values: List<Number> = if (anime) {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.anime?.startYears?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.anime?.startYears?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.anime?.startYears?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
} else {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.manga?.startYears?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.manga?.startYears?.map { it.chaptersRead }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.manga?.startYears?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
|
chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val values: List<Number> = if (anime) {
|
if (chartPackets.isNotEmpty()) {
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.startYears?.map { it.count }
|
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.anime?.startYears?.map { it.minutesWatched / 60 }
|
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.startYears?.map { it.meanScore }
|
|
||||||
} ?: emptyList()
|
|
||||||
} else {
|
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.startYears?.map { it.count }
|
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.manga?.startYears?.map { it.chaptersRead }
|
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.startYears?.map { it.meanScore }
|
|
||||||
} ?: emptyList()
|
|
||||||
}
|
|
||||||
if (names.isNotEmpty() || values.isNotEmpty()) {
|
|
||||||
val startYearChart = ChartBuilder.buildChart(
|
val startYearChart = ChartBuilder.buildChart(
|
||||||
activity,
|
activity,
|
||||||
ChartType.TwoDimensional,
|
ChartType.TwoDimensional,
|
||||||
AAChartType.Bar,
|
AAChartType.Bar,
|
||||||
statType,
|
statType,
|
||||||
type,
|
type,
|
||||||
names,
|
chartPackets,
|
||||||
values,
|
|
||||||
xAxisName = "Year",
|
xAxisName = "Year",
|
||||||
)
|
)
|
||||||
adapter.add(ChartItem("Start Year", startYearChart, activity))
|
adapter.add(ChartItem("Start Year", startYearChart, activity))
|
||||||
|
@ -322,112 +382,160 @@ class StatsFragment() :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadGenreChart(anime: Boolean) {
|
private fun loadGenreChart(anime: Boolean) {
|
||||||
val names: List<String> = if (anime) {
|
val chartPackets = mutableListOf<ChartPacket>()
|
||||||
stats?.data?.user?.statistics?.anime?.genres?.map { it.genre } ?: emptyList()
|
stats.forEach { stat ->
|
||||||
} else {
|
val names: List<String> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.manga?.genres?.map { it.genre } ?: emptyList()
|
stat?.statistics?.anime?.genres?.map { it.genre } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
stat?.statistics?.manga?.genres?.map { it.genre } ?: emptyList()
|
||||||
|
}
|
||||||
|
val values: List<Number> = if (anime) {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.anime?.genres?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.anime?.genres?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.anime?.genres?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
} else {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.manga?.genres?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.manga?.genres?.map { it.chaptersRead }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.manga?.genres?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
|
chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val values: List<Number> = if (anime) {
|
if (chartPackets.isNotEmpty()) {
|
||||||
when (statType) {
|
val referenceNames = chartPackets.first().names.map { it.toString() }
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.genres?.map { it.count }
|
val standardizedPackets = chartPackets.map { packet ->
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.anime?.genres?.map { it.minutesWatched / 60 }
|
val valuesMap = packet.names.map { it.toString() }.zip(packet.statData).toMap()
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.genres?.map { it.meanScore }
|
val standardizedValues = referenceNames.map { name ->
|
||||||
} ?: emptyList()
|
valuesMap[name] ?: 0
|
||||||
} else {
|
}
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.genres?.map { it.count }
|
// Create a new ChartPacket with standardized names and values.
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.manga?.genres?.map { it.chaptersRead }
|
ChartPacket(packet.username, referenceNames, standardizedValues)
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.genres?.map { it.meanScore }
|
}.toMutableList()
|
||||||
} ?: emptyList()
|
chartPackets.clear()
|
||||||
}
|
chartPackets.addAll(standardizedPackets)
|
||||||
if (names.isNotEmpty() || values.isNotEmpty()) {
|
|
||||||
val genreChart = ChartBuilder.buildChart(
|
val genreChart = ChartBuilder.buildChart(
|
||||||
activity,
|
activity,
|
||||||
ChartType.TwoDimensional,
|
ChartType.TwoDimensional,
|
||||||
AAChartType.Areaspline,
|
AAChartType.Areaspline,
|
||||||
statType,
|
statType,
|
||||||
type,
|
type,
|
||||||
names,
|
chartPackets,
|
||||||
values,
|
|
||||||
xAxisName = "Genre",
|
xAxisName = "Genre",
|
||||||
polar = true,
|
polar = true,
|
||||||
categories = names
|
passedCategories = chartPackets[0].names as List<String>,
|
||||||
)
|
)
|
||||||
adapter.add(ChartItem("Genre", genreChart, activity))
|
adapter.add(ChartItem("Genre", genreChart, activity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadTagChart(anime: Boolean) {
|
private fun loadTagChart(anime: Boolean) {
|
||||||
val names: List<String> = if (anime) {
|
val chartPackets = mutableListOf<ChartPacket>()
|
||||||
stats?.data?.user?.statistics?.anime?.tags?.map { it.tag.name } ?: emptyList()
|
stats.forEach { stat ->
|
||||||
} else {
|
val names: List<String> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.manga?.tags?.map { it.tag.name } ?: emptyList()
|
stat?.statistics?.anime?.tags?.map { it.tag.name } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
stat?.statistics?.manga?.tags?.map { it.tag.name } ?: emptyList()
|
||||||
|
}
|
||||||
|
val values: List<Number> = if (anime) {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.anime?.tags?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.anime?.tags?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.anime?.tags?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
} else {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.manga?.tags?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.manga?.tags?.map { it.chaptersRead }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.manga?.tags?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
|
chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val values: List<Number> = if (anime) {
|
if (chartPackets.isNotEmpty()) {
|
||||||
when (statType) {
|
val referenceNames = chartPackets.first().names.map { it.toString() }
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.tags?.map { it.count }
|
val standardizedPackets = chartPackets.map { packet ->
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.anime?.tags?.map { it.minutesWatched / 60 }
|
val valuesMap = packet.names.map { it.toString() }.zip(packet.statData).toMap()
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.tags?.map { it.meanScore }
|
val standardizedValues = referenceNames.map { name ->
|
||||||
} ?: emptyList()
|
valuesMap[name] ?: 0
|
||||||
} else {
|
}
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.tags?.map { it.count }
|
// Create a new ChartPacket with standardized names and values.
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.manga?.tags?.map { it.chaptersRead }
|
ChartPacket(packet.username, referenceNames, standardizedValues)
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.tags?.map { it.meanScore }
|
}.toMutableList()
|
||||||
} ?: emptyList()
|
chartPackets.clear()
|
||||||
}
|
chartPackets.addAll(standardizedPackets)
|
||||||
if (names.isNotEmpty() || values.isNotEmpty()) {
|
|
||||||
val min = values.minOf { it.toInt() }
|
|
||||||
val max = values.maxOf { it.toInt() }
|
|
||||||
val tagChart = ChartBuilder.buildChart(
|
val tagChart = ChartBuilder.buildChart(
|
||||||
activity,
|
activity,
|
||||||
ChartType.TwoDimensional,
|
ChartType.TwoDimensional,
|
||||||
AAChartType.Areaspline,
|
AAChartType.Areaspline,
|
||||||
statType,
|
statType,
|
||||||
type,
|
type,
|
||||||
names,
|
chartPackets,
|
||||||
values,
|
|
||||||
xAxisName = "Tag",
|
xAxisName = "Tag",
|
||||||
polar = false,
|
polar = false,
|
||||||
categories = names,
|
passedCategories = chartPackets[0].names as List<String>,
|
||||||
scrollPos = 0.0f
|
scrollPos = 0.0f
|
||||||
)
|
)
|
||||||
tagChart.yAxis = AAYAxis().min(min).max(max).tickInterval(if (max > 100) 20 else 10)
|
|
||||||
adapter.add(ChartItem("Tag", tagChart, activity))
|
adapter.add(ChartItem("Tag", tagChart, activity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadCountryChart(anime: Boolean) {
|
private fun loadCountryChart(anime: Boolean) {
|
||||||
val names: List<String> = if (anime) {
|
val chartPackets = mutableListOf<ChartPacket>()
|
||||||
stats?.data?.user?.statistics?.anime?.countries?.map { it.country } ?: emptyList()
|
stats.forEach { stat ->
|
||||||
} else {
|
val names: List<String> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.manga?.countries?.map { it.country } ?: emptyList()
|
stat?.statistics?.anime?.countries?.map { it.country } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
stat?.statistics?.manga?.countries?.map { it.country } ?: emptyList()
|
||||||
|
}
|
||||||
|
val values: List<Number> = if (anime) {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.anime?.countries?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.anime?.countries?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.anime?.countries?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
} else {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.manga?.countries?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.manga?.countries?.map { it.chaptersRead }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.manga?.countries?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
|
chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val values: List<Number> = if (anime) {
|
if (chartPackets.isNotEmpty()) {
|
||||||
when (statType) {
|
val referenceNames = chartPackets.first().names.map { it.toString() }
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.countries?.map { it.count }
|
val standardizedPackets = chartPackets.map { packet ->
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.anime?.countries?.map { it.minutesWatched / 60 }
|
val valuesMap = packet.names.map { it.toString() }.zip(packet.statData).toMap()
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.countries?.map { it.meanScore }
|
val standardizedValues = referenceNames.map { name ->
|
||||||
} ?: emptyList()
|
valuesMap[name] ?: 0
|
||||||
} else {
|
}
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.countries?.map { it.count }
|
// Create a new ChartPacket with standardized names and values.
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.manga?.countries?.map { it.chaptersRead }
|
ChartPacket(packet.username, referenceNames, standardizedValues)
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.countries?.map { it.meanScore }
|
}.toMutableList()
|
||||||
} ?: emptyList()
|
chartPackets.clear()
|
||||||
}
|
chartPackets.addAll(standardizedPackets)
|
||||||
if (names.isNotEmpty() || values.isNotEmpty()) {
|
|
||||||
val countryChart = ChartBuilder.buildChart(
|
val countryChart = ChartBuilder.buildChart(
|
||||||
activity,
|
activity,
|
||||||
ChartType.OneDimensional,
|
ChartType.OneDimensional,
|
||||||
AAChartType.Pie,
|
AAChartType.Pie,
|
||||||
statType,
|
statType,
|
||||||
type,
|
type,
|
||||||
names,
|
chartPackets,
|
||||||
values,
|
|
||||||
xAxisName = "Country",
|
xAxisName = "Country",
|
||||||
polar = false,
|
polar = false,
|
||||||
categories = names,
|
passedCategories = chartPackets[0].names as List<String>,
|
||||||
scrollPos = null
|
scrollPos = null
|
||||||
)
|
)
|
||||||
adapter.add(ChartItem("Country", countryChart, activity))
|
adapter.add(ChartItem("Country", countryChart, activity))
|
||||||
|
@ -435,116 +543,167 @@ class StatsFragment() :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadVoiceActorsChart(anime: Boolean) {
|
private fun loadVoiceActorsChart(anime: Boolean) {
|
||||||
val names: List<String> = if (anime) {
|
val chartPackets = mutableListOf<ChartPacket>()
|
||||||
stats?.data?.user?.statistics?.anime?.voiceActors?.map { it.voiceActor.name.full?:"unknown" } ?: emptyList()
|
stats.forEach { stat ->
|
||||||
} else {
|
val names: List<String> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.manga?.voiceActors?.map { it.voiceActor.name.full?:"unknown" } ?: emptyList()
|
stat?.statistics?.anime?.voiceActors?.map { it.voiceActor.name.full?:"unknown" } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
stat?.statistics?.manga?.voiceActors?.map { it.voiceActor.name.full?:"unknown" } ?: emptyList()
|
||||||
|
}
|
||||||
|
val values: List<Number> = if (anime) {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.anime?.voiceActors?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.anime?.voiceActors?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.anime?.voiceActors?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
} else {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.manga?.voiceActors?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.manga?.voiceActors?.map { it.chaptersRead }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.manga?.voiceActors?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
|
chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val values: List<Number> = if (anime) {
|
if (chartPackets.isNotEmpty()) {
|
||||||
when (statType) {
|
val referenceNames = chartPackets.first().names.map { it.toString() }
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.voiceActors?.map { it.count }
|
val standardizedPackets = chartPackets.map { packet ->
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.anime?.voiceActors?.map { it.minutesWatched / 60 }
|
val valuesMap = packet.names.map { it.toString() }.zip(packet.statData).toMap()
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.voiceActors?.map { it.meanScore }
|
val standardizedValues = referenceNames.map { name ->
|
||||||
} ?: emptyList()
|
valuesMap[name] ?: 0
|
||||||
} else {
|
}
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.voiceActors?.map { it.count }
|
// Create a new ChartPacket with standardized names and values.
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.manga?.voiceActors?.map { it.chaptersRead }
|
ChartPacket(packet.username, referenceNames, standardizedValues)
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.voiceActors?.map { it.meanScore }
|
}.toMutableList()
|
||||||
} ?: emptyList()
|
chartPackets.clear()
|
||||||
}
|
chartPackets.addAll(standardizedPackets)
|
||||||
if (names.isNotEmpty() || values.isNotEmpty()) {
|
|
||||||
val voiceActorsChart = ChartBuilder.buildChart(
|
val voiceActorsChart = ChartBuilder.buildChart(
|
||||||
activity,
|
activity,
|
||||||
ChartType.TwoDimensional,
|
ChartType.TwoDimensional,
|
||||||
AAChartType.Column,
|
AAChartType.Column,
|
||||||
statType,
|
statType,
|
||||||
type,
|
type,
|
||||||
names,
|
chartPackets,
|
||||||
values,
|
|
||||||
xAxisName = "Voice Actor",
|
xAxisName = "Voice Actor",
|
||||||
polar = false,
|
polar = false,
|
||||||
categories = names,
|
passedCategories = chartPackets[0].names as List<String>,
|
||||||
scrollPos = 0.0f
|
scrollPos = 0.0f
|
||||||
)
|
)
|
||||||
adapter.add(ChartItem("Voice Actor", voiceActorsChart, activity))
|
adapter.add(ChartItem("Voice Actor", voiceActorsChart, activity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, activity))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadStudioChart(anime: Boolean) {
|
private fun loadStudioChart(anime: Boolean) {
|
||||||
val names: List<String> = if (anime) {
|
val chartPackets = mutableListOf<ChartPacket>()
|
||||||
stats?.data?.user?.statistics?.anime?.studios?.map { it.studio.name } ?: emptyList()
|
stats.forEach { stat ->
|
||||||
} else {
|
val names: List<String> = if (anime) {
|
||||||
stats?.data?.user?.statistics?.manga?.studios?.map { it.studio.name } ?: emptyList()
|
stat?.statistics?.anime?.studios?.map { it.studio.name } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
stat?.statistics?.manga?.studios?.map { it.studio.name } ?: emptyList()
|
||||||
|
}
|
||||||
|
val values: List<Number> = if (anime) {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.anime?.studios?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.anime?.studios?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.anime?.studios?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
} else {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.manga?.studios?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.manga?.studios?.map { it.chaptersRead }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.manga?.studios?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
|
chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val values: List<Number> = if (anime) {
|
if (chartPackets.isNotEmpty()) {
|
||||||
when (statType) {
|
val referenceNames = chartPackets.first().names.map { it.toString() }
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.anime?.studios?.map { it.count }
|
val standardizedPackets = chartPackets.map { packet ->
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.anime?.studios?.map { it.minutesWatched / 60 }
|
val valuesMap = packet.names.map { it.toString() }.zip(packet.statData).toMap()
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.studios?.map { it.meanScore }
|
val standardizedValues = referenceNames.map { name ->
|
||||||
} ?: emptyList()
|
valuesMap[name] ?: 0
|
||||||
} else {
|
}
|
||||||
when (statType) {
|
|
||||||
StatType.COUNT -> stats?.data?.user?.statistics?.manga?.studios?.map { it.count }
|
// Create a new ChartPacket with standardized names and values.
|
||||||
StatType.TIME -> stats?.data?.user?.statistics?.manga?.studios?.map { it.chaptersRead }
|
ChartPacket(packet.username, referenceNames, standardizedValues)
|
||||||
StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.studios?.map { it.meanScore }
|
}.toMutableList()
|
||||||
} ?: emptyList()
|
chartPackets.clear()
|
||||||
}
|
chartPackets.addAll(standardizedPackets)
|
||||||
if (names.isNotEmpty() || values.isNotEmpty()) {
|
|
||||||
val studioChart = ChartBuilder.buildChart(
|
val studioChart = ChartBuilder.buildChart(
|
||||||
activity,
|
activity,
|
||||||
ChartType.TwoDimensional,
|
ChartType.TwoDimensional,
|
||||||
AAChartType.Spline,
|
AAChartType.Spline,
|
||||||
statType,
|
statType,
|
||||||
type,
|
type,
|
||||||
names.take(15),
|
chartPackets,
|
||||||
values.take(15),
|
|
||||||
xAxisName = "Studio",
|
xAxisName = "Studio",
|
||||||
polar = true,
|
polar = true,
|
||||||
categories = names,
|
passedCategories = chartPackets[0].names as List<String>,
|
||||||
scrollPos = null
|
scrollPos = null
|
||||||
)
|
)
|
||||||
adapter.add(ChartItem("Studio", studioChart, activity))
|
adapter.add(ChartItem("Studio", studioChart, activity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadStaffChart(anime: Boolean) {
|
||||||
|
val chartPackets = mutableListOf<ChartPacket>()
|
||||||
|
stats.forEach { stat ->
|
||||||
|
val names: List<String> = if (anime) {
|
||||||
|
stat?.statistics?.anime?.staff?.map { it.staff.name.full?:"unknown" } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
stat?.statistics?.manga?.staff?.map { it.staff.name.full?:"unknown" } ?: emptyList()
|
||||||
|
}
|
||||||
|
val values: List<Number> = if (anime) {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.anime?.staff?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.anime?.staff?.map { it.minutesWatched / 60 }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.anime?.staff?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
} else {
|
||||||
|
when (statType) {
|
||||||
|
StatType.COUNT -> stat?.statistics?.manga?.staff?.map { it.count }
|
||||||
|
StatType.TIME -> stat?.statistics?.manga?.staff?.map { it.chaptersRead }
|
||||||
|
StatType.AVG_SCORE -> stat?.statistics?.manga?.staff?.map { it.meanScore }
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
if (names.isNotEmpty() || values.isNotEmpty()) {
|
||||||
|
chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chartPackets.isNotEmpty()) {
|
||||||
|
val referenceNames = chartPackets.first().names.map { it.toString() }
|
||||||
|
val standardizedPackets = chartPackets.map { packet ->
|
||||||
|
val valuesMap = packet.names.map { it.toString() }.zip(packet.statData).toMap()
|
||||||
|
val standardizedValues = referenceNames.map { name ->
|
||||||
|
valuesMap[name] ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new ChartPacket with standardized names and values.
|
||||||
|
ChartPacket(packet.username, referenceNames, standardizedValues)
|
||||||
|
}.toMutableList()
|
||||||
|
chartPackets.clear()
|
||||||
|
chartPackets.addAll(standardizedPackets)
|
||||||
|
val staffChart = ChartBuilder.buildChart(
|
||||||
|
activity,
|
||||||
|
ChartType.TwoDimensional,
|
||||||
|
AAChartType.Line,
|
||||||
|
statType,
|
||||||
|
type,
|
||||||
|
chartPackets,
|
||||||
|
xAxisName = "Staff",
|
||||||
|
polar = false,
|
||||||
|
passedCategories = chartPackets[0].names as List<String>,
|
||||||
|
scrollPos = 0.0f
|
||||||
|
)
|
||||||
|
adapter.add(ChartItem("Staff", staffChart, activity))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun newInstance(user: Query.UserProfile): StatsFragment {
|
fun newInstance(user: Query.UserProfile): StatsFragment {
|
||||||
val args = Bundle().apply {
|
val args = Bundle().apply {
|
||||||
|
|
88
app/src/main/java/ani/dantotsu/util/ColorEditor.kt
Normal file
88
app/src/main/java/ani/dantotsu/util/ColorEditor.kt
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package ani.dantotsu.util
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
class ColorEditor {
|
||||||
|
companion object {
|
||||||
|
fun oppositeColor(color: Int): Int {
|
||||||
|
val hsv = FloatArray(3)
|
||||||
|
Color.colorToHSV(color, hsv)
|
||||||
|
hsv[0] = (hsv[0] + 180) % 360
|
||||||
|
return adjustColorForContrast(Color.HSVToColor(hsv), color)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLuminance(color: Int): Double {
|
||||||
|
val r = Color.red(color) / 255.0
|
||||||
|
val g = Color.green(color) / 255.0
|
||||||
|
val b = Color.blue(color) / 255.0
|
||||||
|
|
||||||
|
val rL = if (r <= 0.03928) r / 12.92 else ((r + 0.055) / 1.055).pow(2.4)
|
||||||
|
val gL = if (g <= 0.03928) g / 12.92 else ((g + 0.055) / 1.055).pow(2.4)
|
||||||
|
val bL = if (b <= 0.03928) b / 12.92 else ((b + 0.055) / 1.055).pow(2.4)
|
||||||
|
|
||||||
|
return 0.2126 * rL + 0.7152 * gL + 0.0722 * bL
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getContrastRatio(color1: Int, color2: Int): Double {
|
||||||
|
val l1 = getLuminance(color1)
|
||||||
|
val l2 = getLuminance(color2)
|
||||||
|
|
||||||
|
return if (l1 > l2) (l1 + 0.05) / (l2 + 0.05) else (l2 + 0.05) / (l1 + 0.05)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun adjustColorForContrast(originalColor: Int, backgroundColor: Int): Int {
|
||||||
|
var adjustedColor = originalColor
|
||||||
|
var contrastRatio = getContrastRatio(adjustedColor, backgroundColor)
|
||||||
|
val isBackgroundDark = getLuminance(backgroundColor) < 0.5
|
||||||
|
|
||||||
|
while (contrastRatio < 4.5) {
|
||||||
|
// Adjust brightness by modifying the RGB values
|
||||||
|
val r = Color.red(adjustedColor)
|
||||||
|
val g = Color.green(adjustedColor)
|
||||||
|
val b = Color.blue(adjustedColor)
|
||||||
|
|
||||||
|
// Calculate the amount to adjust
|
||||||
|
val adjustment = if (isBackgroundDark) 10 else -10
|
||||||
|
|
||||||
|
// Adjust the color
|
||||||
|
val newR = (r + adjustment).coerceIn(0, 255)
|
||||||
|
val newG = (g + adjustment).coerceIn(0, 255)
|
||||||
|
val newB = (b + adjustment).coerceIn(0, 255)
|
||||||
|
|
||||||
|
adjustedColor = Color.rgb(newR, newG, newB)
|
||||||
|
contrastRatio = getContrastRatio(adjustedColor, backgroundColor)
|
||||||
|
|
||||||
|
// Break the loop if the color adjustment does not change (to avoid infinite loop)
|
||||||
|
if (newR == r && newG == g && newB == b) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return adjustedColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,74 +13,96 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:baselineAligned="false"
|
android:baselineAligned="false"
|
||||||
android:orientation="horizontal"
|
android:orientation="vertical"
|
||||||
android:padding="16dp">
|
android:padding="16dp">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<LinearLayout
|
||||||
android:id="@+id/sourceTypeNameContainer"
|
android:layout_width="match_parent"
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="0dp"
|
android:baselineAligned="false"
|
||||||
android:layout_height="56dp"
|
android:orientation="horizontal">
|
||||||
android:layout_weight="1"
|
|
||||||
android:hint="Type"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
app:boxCornerRadiusBottomEnd="8dp"
|
|
||||||
app:boxCornerRadiusBottomStart="8dp"
|
|
||||||
app:boxCornerRadiusTopEnd="8dp"
|
|
||||||
app:boxCornerRadiusTopStart="8dp"
|
|
||||||
app:boxStrokeColor="@color/text_input_layout_stroke_color"
|
|
||||||
app:hintAnimationEnabled="true">
|
|
||||||
|
|
||||||
<AutoCompleteTextView
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/sourceType"
|
android:id="@+id/sourceTypeNameContainer"
|
||||||
android:layout_width="match_parent"
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:ellipsize="end"
|
android:hint="Type"
|
||||||
android:fontFamily="@font/poppins_bold"
|
android:paddingEnd="8dp"
|
||||||
android:freezesText="false"
|
app:boxCornerRadiusBottomEnd="8dp"
|
||||||
android:inputType="none"
|
app:boxCornerRadiusBottomStart="8dp"
|
||||||
android:maxLines="1"
|
app:boxCornerRadiusTopEnd="8dp"
|
||||||
android:padding="8dp"
|
app:boxCornerRadiusTopStart="8dp"
|
||||||
android:text="@string/anime"
|
app:boxStrokeColor="@color/text_input_layout_stroke_color"
|
||||||
android:textAllCaps="true"
|
app:hintAnimationEnabled="true">
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
android:textSize="14sp"
|
|
||||||
tools:ignore="LabelFor,TextContrastCheck,DuplicateSpeakableTextCheck" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<AutoCompleteTextView
|
||||||
android:id="@+id/sourceFilterNameContainer"
|
android:id="@+id/sourceType"
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="56dp"
|
android:layout_weight="1"
|
||||||
android:layout_weight="1"
|
android:ellipsize="end"
|
||||||
android:hint="View"
|
android:fontFamily="@font/poppins_bold"
|
||||||
android:paddingEnd="8dp"
|
android:freezesText="false"
|
||||||
app:boxCornerRadiusBottomEnd="8dp"
|
android:inputType="none"
|
||||||
app:boxCornerRadiusBottomStart="8dp"
|
android:maxLines="1"
|
||||||
app:boxCornerRadiusTopEnd="8dp"
|
android:padding="8dp"
|
||||||
app:boxCornerRadiusTopStart="8dp"
|
android:text="@string/anime"
|
||||||
app:boxStrokeColor="@color/text_input_layout_stroke_color"
|
android:textAllCaps="true"
|
||||||
app:hintAnimationEnabled="true">
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:ignore="LabelFor,TextContrastCheck,DuplicateSpeakableTextCheck" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<AutoCompleteTextView
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/sourceFilter"
|
android:id="@+id/sourceFilterNameContainer"
|
||||||
android:layout_width="match_parent"
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:ellipsize="end"
|
android:hint="View"
|
||||||
android:fontFamily="@font/poppins_bold"
|
android:paddingEnd="8dp"
|
||||||
android:freezesText="false"
|
app:boxCornerRadiusBottomEnd="8dp"
|
||||||
android:inputType="none"
|
app:boxCornerRadiusBottomStart="8dp"
|
||||||
android:maxLines="1"
|
app:boxCornerRadiusTopEnd="8dp"
|
||||||
android:padding="8dp"
|
app:boxCornerRadiusTopStart="8dp"
|
||||||
android:text="COUNT"
|
app:boxStrokeColor="@color/text_input_layout_stroke_color"
|
||||||
android:textAllCaps="true"
|
app:hintAnimationEnabled="true">
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
android:textSize="14sp"
|
<AutoCompleteTextView
|
||||||
tools:ignore="LabelFor,TextContrastCheck,DuplicateSpeakableTextCheck" />
|
android:id="@+id/sourceFilter"
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:freezesText="false"
|
||||||
|
android:inputType="none"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:text="COUNT"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:ignore="LabelFor,TextContrastCheck,DuplicateSpeakableTextCheck" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/compare"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:enabled="true"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:textColor="?attr/colorPrimary"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:text="@string/compare"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:focusable="true" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
@ -665,6 +665,7 @@
|
||||||
<string name="oldest">Oldest</string>
|
<string name="oldest">Oldest</string>
|
||||||
<string name="highest_rated">Highest rated</string>
|
<string name="highest_rated">Highest rated</string>
|
||||||
<string name="lowest_rated">Lowest rated</string>
|
<string name="lowest_rated">Lowest rated</string>
|
||||||
|
<string name="compare"><u>Compare</u></string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue