diff --git a/app/src/main/java/ani/dantotsu/Functions.kt b/app/src/main/java/ani/dantotsu/Functions.kt index d499d8e2..683263a7 100644 --- a/app/src/main/java/ani/dantotsu/Functions.kt +++ b/app/src/main/java/ani/dantotsu/Functions.kt @@ -784,7 +784,9 @@ fun copyToClipboard(string: String, toast: Boolean = true) { val clipboard = getSystemService(activity, ClipboardManager::class.java) val clip = ClipData.newPlainText("label", string) clipboard?.setPrimaryClip(clip) - if (toast) snackString(activity.getString(R.string.copied_text, string)) + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { + if (toast) snackString(activity.getString(R.string.copied_text, string)) + } } @SuppressLint("SetTextI18n") @@ -1108,7 +1110,7 @@ fun buildMarkwon(activity: Activity, userInputContent: Boolean = true): Markwon val markwon = Markwon.builder(activity) .usePlugin(object : AbstractMarkwonPlugin() { override fun configureConfiguration(builder: MarkwonConfiguration.Builder) { - builder.linkResolver { view, link -> + builder.linkResolver { _, link -> copyToClipboard(link, true) } } @@ -1119,7 +1121,7 @@ fun buildMarkwon(activity: Activity, userInputContent: Boolean = true): Markwon .usePlugin(TablePlugin.create(activity)) .usePlugin(TaskListPlugin.create(activity)) .usePlugin(HtmlPlugin.create { plugin -> - if (!userInputContent) { + if (userInputContent) { plugin.addHandler( TagHandlerNoOp.create("h1", "h2", "h3", "h4", "h5", "h6", "hr", "pre", "a") ) diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt index efda5768..98c0f55c 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt @@ -61,14 +61,14 @@ class AnilistQueries { suspend fun getUserProfile(id: Int): Query.UserProfileResponse? { return executeQuery( - """{user:User(id:$id){id,name,about(asHtml:true)avatar{medium,large},bannerImage,isFollowing,isFollower,isBlocked,favourites{anime{nodes{coverImage{extraLarge,large,medium,color}}}manga{nodes{id,coverImage{extraLarge,large,medium,color}}}characters{nodes{id,image{large,medium}}}staff{nodes{id,image{large,medium}}}studios{nodes{id,name}}}statistics{anime{count,meanScore,standardDeviation,minutesWatched,episodesWatched,chaptersRead,volumesRead}manga{count,meanScore,standardDeviation,minutesWatched,episodesWatched,chaptersRead,volumesRead}}siteUrl}}""", + """{user:User(id:$id){id,name,about(asHtml:true)avatar{medium,large},bannerImage,isFollowing,isFollower,isBlocked,favourites{anime{nodes{id,coverImage{extraLarge,large,medium,color}}}manga{nodes{id,coverImage{extraLarge,large,medium,color}}}characters{nodes{id,image{large,medium}}}staff{nodes{id,image{large,medium}}}studios{nodes{id,name}}}statistics{anime{count,meanScore,standardDeviation,minutesWatched,episodesWatched,chaptersRead,volumesRead}manga{count,meanScore,standardDeviation,minutesWatched,episodesWatched,chaptersRead,volumesRead}}siteUrl}}""", force = true ) } suspend fun getUserStatistics(id: Int, sort: String = "ID"): Query.StatisticsResponse? { return executeQuery( - """{User(id:$id){id name statistics{anime{...UserStatistics}manga{...UserStatistics}}}}fragment UserStatistics on UserStatistics{count meanScore standardDeviation minutesWatched episodesWatched chaptersRead volumesRead formats(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds format}statuses(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds status}scores(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds score}lengths(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds length}releaseYears(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds releaseYear}startYears(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds startYear}genres(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds genre}tags(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds tag{id name}}countries(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds country}voiceActors(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds voiceActor{id name{first middle last full native alternative userPreferred}}characterIds}staff(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds staff{id name{first middle last full native alternative userPreferred}}}studios(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds studio{id name isAnimationStudio}}}""", + """{User(id:$id){id name mediaListOptions{scoreFormat}statistics{anime{...UserStatistics}manga{...UserStatistics}}}}fragment UserStatistics on UserStatistics{count meanScore standardDeviation minutesWatched episodesWatched chaptersRead volumesRead formats(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds format}statuses(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds status}scores(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds score}lengths(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds length}releaseYears(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds releaseYear}startYears(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds startYear}genres(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds genre}tags(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds tag{id name}}countries(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds country}voiceActors(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds voiceActor{id name{first middle last full native alternative userPreferred}}characterIds}staff(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds staff{id name{first middle last full native alternative userPreferred}}}studios(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds studio{id name isAnimationStudio}}}""", force = true, show = true ) @@ -352,10 +352,10 @@ class AnilistQueries { returnArray.addAll(map.values) return returnArray } - val set = PrefManager.getVal>(PrefName.ContinuedAnimeSet).toMutableSet() - if (set.isNotEmpty()) { - set.forEach { - if (map.containsKey(it.toInt())) returnArray.add(map[it.toInt()]!!) + val list = PrefManager.getNullableCustomVal("continueAnimeList", listOf(), List::class.java) as List + if (list.isNotEmpty()) { + list.reversed().forEach { + if (map.containsKey(it)) returnArray.add(map[it]!!) } for (i in map) { if (i.value !in returnArray) returnArray.add(i.value) @@ -504,10 +504,10 @@ class AnilistQueries { returnMap["current$type"] = returnArray return } - val set = PrefManager.getVal>(PrefName.ContinuedAnimeSet).toMutableSet() - if (set.isNotEmpty()) { - set.forEach { - if (subMap.containsKey(it.toInt())) returnArray.add(subMap[it.toInt()]!!) + val list = PrefManager.getNullableCustomVal("continueAnimeList", listOf(), List::class.java) as List + if (list.isNotEmpty()) { + list.reversed().forEach { + if (subMap.containsKey(it)) returnArray.add(subMap[it]!!) } for (i in subMap) { if (i.value !in returnArray) returnArray.add(i.value) @@ -529,9 +529,9 @@ class AnilistQueries { subMap[m.id] = m } } - val set = PrefManager.getCustomVal>("continue_$type", setOf()).toMutableSet() - if (set.isNotEmpty()) { - set.reversed().forEach { + val list = PrefManager.getNullableCustomVal("continueAnimeList", listOf(), List::class.java) as List + if (list.isNotEmpty()) { + list.reversed().forEach { if (subMap.containsKey(it)) returnArray.add(subMap[it]!!) } for (i in subMap) { diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Character.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Character.kt index 766df516..e0539085 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Character.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Character.kt @@ -46,7 +46,7 @@ data class Character( // Notes for site moderators @SerialName("modNotes") var modNotes: String?, -) +) : java.io.Serializable @Serializable data class CharacterConnection( @@ -56,7 +56,7 @@ data class CharacterConnection( // The pagination information // @SerialName("pageInfo") var pageInfo: PageInfo?, -) +) : java.io.Serializable @Serializable data class CharacterEdge( @@ -82,7 +82,7 @@ data class CharacterEdge( // The order the character should be displayed from the users favourites @SerialName("favouriteOrder") var favouriteOrder: Int?, -) +) : java.io.Serializable @Serializable data class CharacterName( @@ -109,7 +109,7 @@ data class CharacterName( // The currently authenticated users preferred name language. Default romaji for non-authenticated @SerialName("userPreferred") var userPreferred: String?, -) +) : java.io.Serializable @Serializable data class CharacterImage( @@ -118,4 +118,4 @@ data class CharacterImage( // The character's image of media at medium size @SerialName("medium") var medium: String?, -) \ No newline at end of file +) : java.io.Serializable \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt index 7d458933..ab23a5d4 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt @@ -143,7 +143,7 @@ class Query { data class ToggleFollow( @SerialName("data") val data: Data? - ) { + ) : java.io.Serializable { @Serializable data class Data( @SerialName("ToggleFollow") @@ -156,7 +156,7 @@ class Query { data class GenreCollection( @SerialName("data") val data: Data - ) { + ) : java.io.Serializable { @Serializable data class Data( @SerialName("GenreCollection") @@ -168,7 +168,7 @@ class Query { data class MediaTagCollection( @SerialName("data") val data: Data - ) { + ) : java.io.Serializable { @Serializable data class Data( @SerialName("MediaTagCollection") @@ -180,7 +180,7 @@ class Query { data class User( @SerialName("data") val data: Data - ) { + ) : java.io.Serializable { @Serializable data class Data( @SerialName("User") @@ -192,7 +192,7 @@ class Query { data class UserProfileResponse( @SerialName("data") val data: Data - ) { + ) : java.io.Serializable { @Serializable data class Data( @SerialName("user") @@ -218,7 +218,7 @@ class Query { val isFollower: Boolean, @SerialName("isBlocked") val isBlocked: Boolean, - @SerialName("favorites") + @SerialName("favourites") val favorites: UserFavorites?, @SerialName("statistics") val statistics: NNUserStatisticTypes, @@ -326,6 +326,8 @@ class Query { val id: Int, @SerialName("name") val name: String, + @SerialName("mediaListOptions") + val mediaListOptions: MediaListOptions, @SerialName("statistics") val statistics: StatisticsTypes ) diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt index 5122e419..da2a989e 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt @@ -251,7 +251,7 @@ data class MediaCoverImage( // Average #hex color of cover image @SerialName("color") var color: String?, -) +) : java.io.Serializable @Serializable data class MediaList( @@ -490,7 +490,7 @@ data class MediaExternalLink( // isDisabled: Boolean @SerialName("notes") var notes: String?, -) +) : java.io.Serializable @Serializable enum class ExternalLinkType { @@ -512,13 +512,13 @@ data class MediaListCollection( // If there is another chunk @SerialName("hasNextChunk") var hasNextChunk: Boolean?, - ) + ) : java.io.Serializable @Serializable data class FollowData( @SerialName("id") var id: Int, @SerialName("isFollowing") var isFollowing: Boolean, -) +) : java.io.Serializable @Serializable data class MediaListGroup( @@ -532,4 +532,4 @@ data class MediaListGroup( @SerialName("isSplitCompletedList") var isSplitCompletedList: Boolean?, @SerialName("status") var status: MediaListStatus?, -) \ No newline at end of file +) : java.io.Serializable \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/User.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/User.kt index d5c6ed65..c74ef85b 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/User.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/User.kt @@ -164,7 +164,7 @@ data class Favourites( @Serializable data class MediaListOptions( // The score format the user is using for media lists - // @SerialName("scoreFormat") var scoreFormat: ScoreFormat?, + @SerialName("scoreFormat") var scoreFormat: String?, // The default order list rows should be displayed in @SerialName("rowOrder") var rowOrder: String?, diff --git a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt index 4f01d919..ee4623a9 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt @@ -1245,10 +1245,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL media.anime!!.selectedEpisode!! ) - val list = PrefManager.getVal>(PrefName.ContinuedAnimeSet).toMutableList() - if (list.contains(media.id.toString())) list.remove(media.id.toString()) - list.add(media.id.toString()) - PrefManager.setVal(PrefName.ContinuedAnimeSet, list.toSet()) + val list = (PrefManager.getNullableCustomVal("continueAnimeList", listOf(), List::class.java) as List).toMutableList() + if (list.contains(media.id)) list.remove(media.id) + list.add(media.id) + PrefManager.setCustomVal("continueAnimeList", list) lifecycleScope.launch(Dispatchers.IO) { extractor?.onVideoStopped(video) diff --git a/app/src/main/java/ani/dantotsu/profile/ChartBuilder.kt b/app/src/main/java/ani/dantotsu/profile/ChartBuilder.kt index f9a81ee4..1fcce464 100644 --- a/app/src/main/java/ani/dantotsu/profile/ChartBuilder.kt +++ b/app/src/main/java/ani/dantotsu/profile/ChartBuilder.kt @@ -18,7 +18,6 @@ import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAScrollablePlotAre import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAStyle import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAYAxis import com.github.aachartmodel.aainfographics.aatools.AAColor -import com.github.aachartmodel.aainfographics.aatools.AAGradientColor class ChartBuilder { companion object { @@ -37,7 +36,7 @@ class ChartBuilder { data class ChartPacket( val username: String, val names: List, - val statData: List + var statData: List ) fun buildChart( @@ -47,11 +46,12 @@ class ChartBuilder { statType: StatType, mediaType: MediaType, chartPackets: List, - xAxisName: String = "X Axis", + xAxisName: String, xAxisTickInterval: Int? = null, polar: Boolean = false, passedCategories: List? = null, scrollPos: Float? = null, + normalize: Boolean = false ): AAOptions { val typedValue = TypedValue() context.theme.resolveAttribute( @@ -69,6 +69,11 @@ class ChartBuilder { aaChartType = AAChartType.Column categories = chartPackets[0].names.map { it.toString() } } + if (normalize && chartPackets.size > 1) { + chartPackets.forEach { + it.statData = normalizeData(it.statData) + } + } val namesMax = chartPackets.maxOf { it.names.size } val palette = ColorEditor.generateColorPalette(primaryColor, namesMax) @@ -76,20 +81,25 @@ class ChartBuilder { ChartType.OneDimensional -> { val chart = AAChartModel() .chartType(aaChartType) - .subtitle(getTypeName(statType, mediaType)) + .subtitle( + getTypeName( + statType, + mediaType + ) + if (normalize && chartPackets.size > 1) " (Normalized)" else "" + ) .zoomType(AAChartZoomType.None) .dataLabelsEnabled(true) val elements: MutableList = mutableListOf() chartPackets.forEachIndexed { index, chartPacket -> - val element = AASeriesElement() - .name(chartPacket.username) - .data( - get1DElements( - chartPacket.names, - chartPacket.statData, - palette - ) + val element = AASeriesElement() + .name(chartPacket.username) + .data( + get1DElements( + chartPacket.names, + chartPacket.statData, + palette ) + ) if (index == 0) { element.color(primaryColor) } else { @@ -109,31 +119,66 @@ class ChartBuilder { palette.map { String.format("#%06X", 0xFFFFFF and it) }.toTypedArray() val chart = AAChartModel() .chartType(aaChartType) - .subtitle(getTypeName(statType, mediaType)) + .subtitle( + getTypeName( + statType, + mediaType + ) + if (normalize && chartPackets.size > 1) " (Normalized)" else "" + ) .zoomType(AAChartZoomType.None) .dataLabelsEnabled(false) - .yAxisTitle(getTypeName(statType, mediaType)) - if (chartPackets.size == 1) { - chart.colorsTheme(hexColorsArray) - } + .yAxisTitle( + getTypeName( + statType, + mediaType + ) + if (normalize && chartPackets.size > 1) " (Normalized)" else "" + ) + if (chartPackets.size == 1) { + chart.colorsTheme(hexColorsArray) + } val elements: MutableList = mutableListOf() chartPackets.forEachIndexed { index, chartPacket -> val element = get2DElements( - chartPacket.names, - chartPacket.statData, - chartPackets.size == 1 - ) + chartPacket.names, + chartPacket.statData, + chartPackets.size == 1 + ) element.name(chartPacket.username) if (index == 0) { - element.color(AAColor.rgbaColor(Color.red(primaryColor), Color.green(primaryColor), Color.blue(primaryColor), 0.9f)) + element.color( + AAColor.rgbaColor( + Color.red(primaryColor), + Color.green(primaryColor), + Color.blue(primaryColor), + 0.9f + ) + ) } else { - element.color(AAColor.rgbaColor(Color.red(ColorEditor.oppositeColor(primaryColor)), Color.green(ColorEditor.oppositeColor(primaryColor)), Color.blue(ColorEditor.oppositeColor(primaryColor)), 0.9f)) + element.color( + AAColor.rgbaColor( + Color.red( + ColorEditor.oppositeColor( + primaryColor + ) + ), + Color.green(ColorEditor.oppositeColor(primaryColor)), + Color.blue(ColorEditor.oppositeColor(primaryColor)), + 0.9f + ) + ) } if (chartPackets.size == 1) { - element.fillColor(AAColor.rgbaColor(Color.red(primaryColor), Color.green(primaryColor), Color.blue(primaryColor), 0.9f)) + element.fillColor( + AAColor.rgbaColor( + Color.red(primaryColor), + Color.green(primaryColor), + Color.blue(primaryColor), + 0.9f + ) + ) } elements.add(element) } @@ -205,12 +250,12 @@ class ChartBuilder { statValues.add(arrayOf(names[i], statData[i], statData[i])) } return AASeriesElement() - .data(statValues.toTypedArray()) - .dataLabels( - AADataLabels() - .enabled(false) - ) - .colorByPoint(colorByPoint) + .data(statValues.toTypedArray()) + .dataLabels( + AADataLabels() + .enabled(false) + ) + .colorByPoint(colorByPoint) } private fun get1DElements( @@ -255,6 +300,14 @@ class ChartBuilder { } } + private fun normalizeData(data: List): List { + if (data.isEmpty()) { + return data + } + val max = data.maxOf { it.toDouble() } + return data.map { (it.toDouble() / max) * 100 } + } + private fun setColors(aaOptions: AAOptions, context: Context, primaryColor: Int) { val backgroundColor = TypedValue() context.theme.resolveAttribute( @@ -281,7 +334,7 @@ class ChartBuilder { Color.red(colorOnBackground.data), Color.green(colorOnBackground.data), Color.blue(colorOnBackground.data), - 0.9f + 1.0f ) ) @@ -292,7 +345,7 @@ class ChartBuilder { Color.red(backgroundColor.data), Color.green(backgroundColor.data), Color.blue(backgroundColor.data), - 0.9f + 1.0f ) ) aaOptions.title?.style(onBackgroundStyle) diff --git a/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt b/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt index e8965f19..06590372 100644 --- a/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt +++ b/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt @@ -2,6 +2,7 @@ package ani.dantotsu.profile import android.content.Intent import android.os.Bundle +import android.util.TypedValue import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -13,7 +14,6 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import ani.dantotsu.buildMarkwon import ani.dantotsu.connections.anilist.ProfileViewModel import ani.dantotsu.connections.anilist.api.Query import ani.dantotsu.databinding.FragmentProfileBinding @@ -23,10 +23,12 @@ import ani.dantotsu.media.MediaAdaptor import ani.dantotsu.media.user.ListActivity import ani.dantotsu.setSlideIn import ani.dantotsu.setSlideUp +import ani.dantotsu.util.ColorEditor.Companion.toCssColor import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -class ProfileFragment(): Fragment() { + +class ProfileFragment() : Fragment() { lateinit var binding: FragmentProfileBinding private lateinit var activity: ProfileActivity private lateinit var user: Query.UserProfile @@ -44,8 +46,34 @@ class ProfileFragment(): Fragment() { super.onViewCreated(view, savedInstanceState) activity = requireActivity() as ProfileActivity user = arguments?.getSerializable("user") as Query.UserProfile - val markwon = buildMarkwon(activity, false) - markwon.setMarkdown(binding.profileUserBio, user.about?:"") + + val backGroundColorTypedValue = TypedValue() + val textColorTypedValue = TypedValue() + activity.theme.resolveAttribute( + android.R.attr.windowBackground, + backGroundColorTypedValue, + true + ) + activity.theme.resolveAttribute( + com.google.android.material.R.attr.colorOnBackground, + textColorTypedValue, + true + ) + + binding.profileUserBio.settings.loadWithOverviewMode = true + binding.profileUserBio.settings.useWideViewPort = true + binding.profileUserBio.setInitialScale(1) + binding.profileUserBio.loadDataWithBaseURL( + null, + styled( + convertMarkdownToHtml(user.about ?: ""), + backGroundColorTypedValue.data, + textColorTypedValue.data + ), + "text/html; charset=utf-8", + "UTF-8", + null + ) binding.userInfoContainer.visibility = if (user.about != null) View.VISIBLE else View.GONE binding.profileAnimeList.setOnClickListener { @@ -67,7 +95,8 @@ class ProfileFragment(): Fragment() { binding.profileAnimeListImage.loadImage("https://bit.ly/31bsIHq") binding.profileMangaListImage.loadImage("https://bit.ly/2ZGfcuG") binding.statsEpisodesWatched.text = user.statistics.anime.episodesWatched.toString() - binding.statsDaysWatched.text = (user.statistics.anime.minutesWatched / (24 * 60)).toString() + binding.statsDaysWatched.text = + (user.statistics.anime.minutesWatched / (24 * 60)).toString() binding.statsTotalAnime.text = user.statistics.anime.count.toString() binding.statsAnimeMeanScore.text = user.statistics.anime.meanScore.toString() binding.statsChaptersRead.text = user.statistics.manga.chaptersRead.toString() @@ -80,7 +109,7 @@ class ProfileFragment(): Fragment() { model.setAnimeFav(user.id) model.setMangaFav(user.id) } - + initRecyclerView( model.getAnimeFav(), binding.profileFavAnimeContainer, @@ -89,7 +118,7 @@ class ProfileFragment(): Fragment() { binding.profileFavAnimeEmpty, binding.profileFavAnime ) - + initRecyclerView( model.getMangaFav(), binding.profileFavMangaContainer, @@ -107,6 +136,16 @@ class ProfileFragment(): Fragment() { } } + private fun convertMarkdownToHtml(markdown: String): String { + val regex = """\[\!\[(.*?)\]\((.*?)\)\]\((.*?)\)""".toRegex() + return regex.replace(markdown) { matchResult -> + val altText = matchResult.groupValues[1] + val imageUrl = matchResult.groupValues[2] + val linkUrl = matchResult.groupValues[3] + """$altText""" + } + } + private fun initRecyclerView( mode: LiveData>, container: View, @@ -146,6 +185,37 @@ class ProfileFragment(): Fragment() { } } + private fun styled(html: String, backGroundColor: Int, textColor: Int): String { + return """ + + + + + + + $html + + + """.trimIndent() + } + companion object { fun newInstance(query: Query.UserProfile): ProfileFragment { val args = Bundle().apply { diff --git a/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt b/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt index 687a18d5..bb245f47 100644 --- a/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt +++ b/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt @@ -12,10 +12,10 @@ import ani.dantotsu.R import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.api.Query import ani.dantotsu.databinding.FragmentStatisticsBinding -import ani.dantotsu.profile.ChartBuilder.Companion.ChartType -import ani.dantotsu.profile.ChartBuilder.Companion.StatType -import ani.dantotsu.profile.ChartBuilder.Companion.MediaType import ani.dantotsu.profile.ChartBuilder.Companion.ChartPacket +import ani.dantotsu.profile.ChartBuilder.Companion.ChartType +import ani.dantotsu.profile.ChartBuilder.Companion.MediaType +import ani.dantotsu.profile.ChartBuilder.Companion.StatType import com.github.aachartmodel.aainfographics.aachartcreator.AAChartType import com.xwray.groupie.GroupieAdapter import kotlinx.coroutines.Dispatchers @@ -147,7 +147,7 @@ class StatsFragment : private fun loadFormatChart(anime: Boolean) { val chartPackets = mutableListOf() - stats.forEach {stat -> + stats.forEach { stat -> val names: List = if (anime) { stat?.statistics?.anime?.formats?.map { it.format } ?: emptyList() } else { @@ -167,7 +167,7 @@ class StatsFragment : } ?: emptyList() } if (names.isNotEmpty() && values.isNotEmpty()) { - chartPackets.add(ChartPacket(stat?.name?:"Unknown", names, values)) + chartPackets.add(ChartPacket(stat?.name ?: "Unknown", names, values)) } } if (chartPackets.isNotEmpty()) { @@ -178,6 +178,7 @@ class StatsFragment : statType, type, chartPackets, + xAxisName = "Format", ) adapter.add(ChartItem("Format", formatChart, activity)) } @@ -215,7 +216,8 @@ class StatsFragment : AAChartType.Funnel, statType, type, - chartPackets + chartPackets, + xAxisName = "Status", ) adapter.add(ChartItem("Status", statusChart, activity)) } @@ -225,9 +227,19 @@ class StatsFragment : val chartPackets = mutableListOf() stats.forEach { stat -> val names: List = if (anime) { - stat?.statistics?.anime?.scores?.map { it.score } ?: emptyList() + stat?.statistics?.anime?.scores?.map { + convertScore( + it.score, + stat.mediaListOptions.scoreFormat + ) + } ?: emptyList() } else { - stat?.statistics?.manga?.scores?.map { it.score } ?: emptyList() + stat?.statistics?.manga?.scores?.map { + convertScore( + it.score, + stat.mediaListOptions.scoreFormat + ) + } ?: emptyList() } val values: List = if (anime) { when (statType) { @@ -337,6 +349,7 @@ class StatsFragment : type, chartPackets, xAxisName = "Year", + scrollPos = 0.0f ) adapter.add(ChartItem("Release Year", releaseYearChart, activity)) } @@ -429,6 +442,7 @@ class StatsFragment : xAxisName = "Genre", polar = true, passedCategories = chartPackets[0].names as List, + normalize = true ) adapter.add(ChartItem("Genre", genreChart, activity)) } @@ -546,9 +560,11 @@ class StatsFragment : val chartPackets = mutableListOf() stats.forEach { stat -> val names: List = if (anime) { - stat?.statistics?.anime?.voiceActors?.map { it.voiceActor.name.full?:"unknown" } ?: emptyList() + stat?.statistics?.anime?.voiceActors?.map { it.voiceActor.name.full ?: "unknown" } + ?: emptyList() } else { - stat?.statistics?.manga?.voiceActors?.map { it.voiceActor.name.full?:"unknown" } ?: emptyList() + stat?.statistics?.manga?.voiceActors?.map { it.voiceActor.name.full ?: "unknown" } + ?: emptyList() } val values: List = if (anime) { when (statType) { @@ -644,7 +660,8 @@ class StatsFragment : xAxisName = "Studio", polar = true, passedCategories = chartPackets[0].names as List, - scrollPos = null + scrollPos = null, + normalize = true ) adapter.add(ChartItem("Studio", studioChart, activity)) } @@ -654,9 +671,11 @@ class StatsFragment : val chartPackets = mutableListOf() stats.forEach { stat -> val names: List = if (anime) { - stat?.statistics?.anime?.staff?.map { it.staff.name.full?:"unknown" } ?: emptyList() + stat?.statistics?.anime?.staff?.map { it.staff.name.full ?: "unknown" } + ?: emptyList() } else { - stat?.statistics?.manga?.staff?.map { it.staff.name.full?:"unknown" } ?: emptyList() + stat?.statistics?.manga?.staff?.map { it.staff.name.full ?: "unknown" } + ?: emptyList() } val values: List = if (anime) { when (statType) { @@ -704,6 +723,17 @@ class StatsFragment : } } + private fun convertScore(score: Int, type: String?): Int { + return when (type) { + "POINT_100" -> score + "POINT_10_DECIMAL" -> score + "POINT_10" -> score * 10 + "POINT_5" -> score * 20 + "POINT_3" -> score * 33 + else -> score + } + } + companion object { fun newInstance(user: Query.UserProfile): StatsFragment { val args = Bundle().apply { diff --git a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt index af7e005b..7dec7e49 100644 --- a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt +++ b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt @@ -100,7 +100,6 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files UseInternalCast(Pref(Location.Player, Boolean::class, false)), Pip(Pref(Location.Player, Boolean::class, true)), RotationPlayer(Pref(Location.Player, Boolean::class, true)), - ContinuedAnimeSet(Pref(Location.Player, Set::class, setOf())), //Reader ShowSource(Pref(Location.Reader, Boolean::class, true)), diff --git a/app/src/main/java/ani/dantotsu/util/ColorEditor.kt b/app/src/main/java/ani/dantotsu/util/ColorEditor.kt index 4a2c1d45..6503f225 100644 --- a/app/src/main/java/ani/dantotsu/util/ColorEditor.kt +++ b/app/src/main/java/ani/dantotsu/util/ColorEditor.kt @@ -84,5 +84,14 @@ class ColorEditor { } return adjustedColor } + + fun Int.toCssColor(): String { + var base = "rgba(" + base += "${Color.red(this)}, " + base += "${Color.green(this)}, " + base += "${Color.blue(this)}, " + base += "${Color.alpha(this) / 255.0})" + return base + } } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml index 90253f56..81e2207b 100644 --- a/app/src/main/res/layout/fragment_profile.xml +++ b/app/src/main/res/layout/fragment_profile.xml @@ -332,7 +332,7 @@ - diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index d71f6935..ac1a6054 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -226,7 +226,7 @@ #e800ac - #B30084 + #FF92E2 #FFFFFF #FFD8EA #3C002A diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 91fa4e90..e1d6f959 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -42,7 +42,7 @@