diff --git a/app/build.gradle b/app/build.gradle index 76e690cf..db475477 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -127,6 +127,7 @@ dependencies { implementation 'com.github.VipulOG:ebook-reader:0.1.6' implementation 'androidx.paging:paging-runtime-ktx:3.2.1' implementation 'com.github.eltos:simpledialogfragments:v3.7' + implementation 'com.github.AAChartModel:AAChartCore-Kotlin:-SNAPSHOT' // Markwon ext.markwon_version = '4.6.2' diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt b/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt index 85979714..76f30db2 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt @@ -11,6 +11,7 @@ import ani.dantotsu.currContext import ani.dantotsu.openLinkInBrowser import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName +import ani.dantotsu.snackString import ani.dantotsu.toast import ani.dantotsu.tryWithSuspend import java.util.Calendar @@ -124,7 +125,7 @@ object Anilist { show: Boolean = false, cache: Int? = null ): T? { - return tryWithSuspend { + return try { if (rateLimitReset > System.currentTimeMillis() / 1000) { toast("Rate limited. Try after ${rateLimitReset - (System.currentTimeMillis() / 1000)} seconds") throw Exception("Rate limited after ${rateLimitReset - (System.currentTimeMillis() / 1000)} seconds") @@ -163,6 +164,10 @@ object Anilist { if (show) println("Response : ${json.text}") json.parsed() } else null + } catch (e: Exception) { + if (show) snackString("Error fetching Anilist data: ${e.message}") + Log.e("AnilistQuery", "Error: ${e.message}") + null } } } 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 b9fb3ca6..10b75b71 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt @@ -59,6 +59,14 @@ class AnilistQueries { ) } + 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}}}""", + force = true, + show = true + ) + } + suspend fun getMedia(id: Int, mal: Boolean = false): Media? { val response = executeQuery( """{Media(${if (!mal) "id:" else "idMal:"}$id){id idMal status chapters episodes nextAiringEpisode{episode}type meanScore isAdult isFavourite format bannerImage coverImage{large}title{english romaji userPreferred}mediaListEntry{progress private score(format:POINT_100)status}}}""", 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 361c68c7..c5e5c7e4 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 @@ -292,6 +292,320 @@ class Query { @SerialName("name") val name: String, ) + + //---------------------------------------- + // Statistics + + @Serializable + data class StatisticsResponse( + @SerialName("data") + val data: Data + ) { + @Serializable + data class Data( + @SerialName("User") + val user: StatisticsUser? + ) + } + + @Serializable + data class StatisticsUser( + @SerialName("id") + val id: Int, + @SerialName("name") + val name: String, + @SerialName("statistics") + val statistics: StatisticsTypes + ) + + @Serializable + data class StatisticsTypes( + @SerialName("anime") + val anime: Statistics, + @SerialName("manga") + val manga: Statistics + ) + + @Serializable + data class Statistics( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("standardDeviation") + val standardDeviation: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("episodesWatched") + val episodesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("volumesRead") + val volumesRead: Int, + @SerialName("formats") + val formats: List, + @SerialName("statuses") + val statuses: List, + @SerialName("scores") + val scores: List, + @SerialName("lengths") + val lengths: List, + @SerialName("releaseYears") + val releaseYears: List, + @SerialName("startYears") + val startYears: List, + @SerialName("genres") + val genres: List, + @SerialName("tags") + val tags: List, + @SerialName("countries") + val countries: List, + @SerialName("voiceActors") + val voiceActors: List, + @SerialName("staff") + val staff: List, + @SerialName("studios") + val studios: List + ) + + @Serializable + data class StatisticsFormat( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("mediaIds") + val mediaIds: List, + @SerialName("format") + val format: String + ) + + @Serializable + data class StatisticsStatus( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("mediaIds") + val mediaIds: List, + @SerialName("status") + val status: String + ) + + @Serializable + data class StatisticsScore( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("mediaIds") + val mediaIds: List, + @SerialName("score") + val score: Int + ) + + @Serializable + data class StatisticsLength( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("mediaIds") + val mediaIds: List, + @SerialName("length") + val length: String? //can be null for manga + ) + + @Serializable + data class StatisticsReleaseYear( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("mediaIds") + val mediaIds: List, + @SerialName("releaseYear") + val releaseYear: Int + ) + + @Serializable + data class StatisticsStartYear( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("mediaIds") + val mediaIds: List, + @SerialName("startYear") + val startYear: Int + ) + + @Serializable + data class StatisticsGenre( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("mediaIds") + val mediaIds: List, + @SerialName("genre") + val genre: String + ) + + @Serializable + data class StatisticsTag( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("mediaIds") + val mediaIds: List, + @SerialName("tag") + val tag: Tag + ) + + @Serializable + data class Tag( + @SerialName("id") + val id: Int, + @SerialName("name") + val name: String + ) + + @Serializable + data class StatisticsCountry( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("mediaIds") + val mediaIds: List, + @SerialName("country") + val country: String + ) + + @Serializable + data class StatisticsVoiceActor( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("mediaIds") + val mediaIds: List, + @SerialName("voiceActor") + val voiceActor: VoiceActor, + @SerialName("characterIds") + val characterIds: List + ) + + @Serializable + data class VoiceActor( + @SerialName("id") + val id: Int, + @SerialName("name") + val name: StaffName + ) + + @Serializable + data class StaffName( + @SerialName("first") + val first: String?, + @SerialName("middle") + val middle: String?, + @SerialName("last") + val last: String?, + @SerialName("full") + val full: String?, + @SerialName("native") + val native: String?, + @SerialName("alternative") + val alternative: List?, + @SerialName("userPreferred") + val userPreferred: String? + ) + + @Serializable + data class StatisticsStaff( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("mediaIds") + val mediaIds: List, + @SerialName("staff") + val staff: VoiceActor + ) + + @Serializable + data class StatisticsStudio( + @SerialName("count") + val count: Int, + @SerialName("meanScore") + val meanScore: Float, + @SerialName("minutesWatched") + val minutesWatched: Int, + @SerialName("chaptersRead") + val chaptersRead: Int, + @SerialName("mediaIds") + val mediaIds: List, + @SerialName("studio") + val studio: StatStudio + ) + + @Serializable + data class StatStudio( + @SerialName("id") + val id: Int, + @SerialName("name") + val name: String, + @SerialName("isAnimationStudio") + val isAnimationStudio: Boolean + ) + } //data class WhaData( diff --git a/app/src/main/java/ani/dantotsu/profile/StatisticsActivity.kt b/app/src/main/java/ani/dantotsu/profile/StatisticsActivity.kt new file mode 100644 index 00000000..cbe625b4 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/profile/StatisticsActivity.kt @@ -0,0 +1,4 @@ +package ani.dantotsu.profile + +class StatisticsActivity { +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_statistics.xml b/app/src/main/res/layout/fragment_statistics.xml new file mode 100644 index 00000000..788e5494 --- /dev/null +++ b/app/src/main/res/layout/fragment_statistics.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file