diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4c267042..1a6ab597 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -202,7 +202,7 @@
android:parentActivityName=".MainActivity" />
+ android:windowSoftInputMode="adjustResize|stateVisible" />
=
PrefManager.getVal(PrefName.HomeLayout)
if (toShow.getOrNull(7) != true) return null
- var query = """{"""
- query += "Page1:${status(1)}Page2:${status(2)}"
- query += """}""".trimEnd(',')
+ val query = """{Page1:${status(1)}Page2:${status(2)}}"""
val response = executeQuery(query, show = true)
val list = mutableListOf()
val threeDaysAgo = Calendar.getInstance().apply {
@@ -461,8 +460,9 @@ class AnilistQueries {
}
}
- if (anilistActivities.isEmpty() && Anilist.token != null){
- anilistActivities.add(0,
+ if (anilistActivities.isEmpty() && Anilist.token != null) {
+ anilistActivities.add(
+ 0,
User(
Anilist.userid!!,
Anilist.username!!,
@@ -477,206 +477,176 @@ class AnilistQueries {
} else return null
}
- suspend fun initHomePage(): Map> {
+ suspend fun initHomePage(): Map> {
val removeList = PrefManager.getCustomVal("removeList", setOf())
val hidePrivate = PrefManager.getVal(PrefName.HidePrivate)
val removedMedia = ArrayList()
val toShow: List =
- PrefManager.getVal(PrefName.HomeLayout) // anime continue, anime fav, anime planned, manga continue, manga fav, manga planned, recommendations
- var query = """{"""
- if (toShow.getOrNull(0) == true) query += """currentAnime: ${
- continueMediaQuery(
- "ANIME",
- "CURRENT"
- )
- }, repeatingAnime: ${continueMediaQuery("ANIME", "REPEATING")}"""
- if (toShow.getOrNull(1) == true) query += """favoriteAnime: ${favMediaQuery(true, 1)}"""
- if (toShow.getOrNull(2) == true) query += """plannedAnime: ${
- continueMediaQuery(
- "ANIME",
- "PLANNING"
- )
- }"""
- if (toShow.getOrNull(3) == true) query += """currentManga: ${
- continueMediaQuery(
- "MANGA",
- "CURRENT"
- )
- }, repeatingManga: ${continueMediaQuery("MANGA", "REPEATING")}"""
- if (toShow.getOrNull(4) == true) query += """favoriteManga: ${favMediaQuery(false, 1)}"""
- if (toShow.getOrNull(5) == true) query += """plannedManga: ${
- continueMediaQuery(
- "MANGA",
- "PLANNING"
- )
- }"""
- if (toShow.getOrNull(6) == true) query += """recommendationQuery: ${recommendationQuery()}, recommendationPlannedQueryAnime: ${
- recommendationPlannedQuery(
- "ANIME"
- )
- }, recommendationPlannedQueryManga: ${recommendationPlannedQuery("MANGA")}"""
- query += """}""".trimEnd(',')
+ PrefManager.getVal(PrefName.HomeLayout) // list of booleans for what to show
+ val queries = mutableListOf()
+ if (toShow.getOrNull(0) == true) {
+ queries.add("""currentAnime: ${continueMediaQuery("ANIME", "CURRENT")}""")
+ queries.add("""repeatingAnime: ${continueMediaQuery("ANIME", "REPEATING")}""")
+ }
+ if (toShow.getOrNull(1) == true) queries.add("""favoriteAnime: ${favMediaQuery(true, 1)}""")
+ if (toShow.getOrNull(2) == true) queries.add(
+ """plannedAnime: ${
+ continueMediaQuery(
+ "ANIME",
+ "PLANNING"
+ )
+ }"""
+ )
+ if (toShow.getOrNull(3) == true) {
+ queries.add("""currentManga: ${continueMediaQuery("MANGA", "CURRENT")}""")
+ queries.add("""repeatingManga: ${continueMediaQuery("MANGA", "REPEATING")}""")
+ }
+ if (toShow.getOrNull(4) == true) queries.add(
+ """favoriteManga: ${
+ favMediaQuery(
+ false,
+ 1
+ )
+ }"""
+ )
+ if (toShow.getOrNull(5) == true) queries.add(
+ """plannedManga: ${
+ continueMediaQuery(
+ "MANGA",
+ "PLANNING"
+ )
+ }"""
+ )
+ if (toShow.getOrNull(6) == true) {
+ queries.add("""recommendationQuery: ${recommendationQuery()}""")
+ queries.add("""recommendationPlannedQueryAnime: ${recommendationPlannedQuery("ANIME")}""")
+ queries.add("""recommendationPlannedQueryManga: ${recommendationPlannedQuery("MANGA")}""")
+ }
+
+ val query = "{${queries.joinToString(",")}}"
val response = executeQuery(query, show = true)
- val returnMap = mutableMapOf>()
- fun current(type: String) {
+ val returnMap = mutableMapOf>()
+
+ fun processMedia(
+ type: String,
+ currentMedia: List?,
+ repeatingMedia: List?
+ ) {
val subMap = mutableMapOf()
val returnArray = arrayListOf()
- val current =
- if (type == "Anime") response?.data?.currentAnime else response?.data?.currentManga
- val repeating =
- if (type == "Anime") response?.data?.repeatingAnime else response?.data?.repeatingManga
- current?.lists?.forEach { li ->
- li.entries?.reversed()?.forEach {
- val m = Media(it)
- if (m.id !in removeList && if (hidePrivate) !m.isListPrivate else true) {
- m.cameFromContinue = true
- subMap[m.id] = m
- } else {
- removedMedia.add(m)
- }
+
+ (currentMedia ?: emptyList()).forEach { entry ->
+ val media = Media(entry)
+ if (media.id !in removeList && (!hidePrivate || !media.isListPrivate)) {
+ media.cameFromContinue = true
+ subMap[media.id] = media
+ } else {
+ removedMedia.add(media)
}
}
- repeating?.lists?.forEach { li ->
- li.entries?.reversed()?.forEach {
- val m = Media(it)
- if (m.id !in removeList && if (hidePrivate) !m.isListPrivate else true) {
- m.cameFromContinue = true
- subMap[m.id] = m
- } else {
- removedMedia.add(m)
- }
+ (repeatingMedia ?: emptyList()).forEach { entry ->
+ val media = Media(entry)
+ if (media.id !in removeList && (!hidePrivate || !media.isListPrivate)) {
+ media.cameFromContinue = true
+ subMap[media.id] = media
+ } else {
+ removedMedia.add(media)
}
}
- if (type != "Anime") {
+ @Suppress("UNCHECKED_CAST")
+ val list = PrefManager.getNullableCustomVal(
+ "continue${type}List",
+ listOf(),
+ List::class.java
+ ) as List
+ if (list.isNotEmpty()) {
+ list.reversed().forEach { id ->
+ subMap[id]?.let { returnArray.add(it) }
+ }
+ subMap.values.forEach {
+ if (!returnArray.contains(it)) returnArray.add(it)
+ }
+ } else {
returnArray.addAll(subMap.values)
- returnMap["current$type"] = returnArray
- return
}
- @Suppress("UNCHECKED_CAST")
- 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)
- }
- } else returnArray.addAll(subMap.values)
returnMap["current$type"] = returnArray
-
}
- fun planned(type: String) {
- val subMap = mutableMapOf()
- val returnArray = arrayListOf()
- val current =
- if (type == "Anime") response?.data?.plannedAnime else response?.data?.plannedManga
- current?.lists?.forEach { li ->
- li.entries?.reversed()?.forEach {
- val m = Media(it)
- if (m.id !in removeList && if (hidePrivate) !m.isListPrivate else true) {
- m.cameFromContinue = true
- subMap[m.id] = m
- } else {
- removedMedia.add(m)
- }
- }
- }
- @Suppress("UNCHECKED_CAST")
- 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)
- }
- } else returnArray.addAll(subMap.values)
- returnMap["planned$type"] = returnArray
- }
+ if (toShow.getOrNull(0) == true) processMedia(
+ "Anime",
+ response?.data?.currentAnime?.lists?.flatMap { it.entries ?: emptyList() }?.reversed(),
+ response?.data?.repeatingAnime?.lists?.flatMap { it.entries ?: emptyList() }?.reversed()
+ )
+ if (toShow.getOrNull(2) == true) processMedia(
+ "AnimePlanned",
+ response?.data?.plannedAnime?.lists?.flatMap { it.entries ?: emptyList() }?.reversed(),
+ null
+ )
+ if (toShow.getOrNull(3) == true) processMedia(
+ "Manga",
+ response?.data?.currentManga?.lists?.flatMap { it.entries ?: emptyList() }?.reversed(),
+ response?.data?.repeatingManga?.lists?.flatMap { it.entries ?: emptyList() }?.reversed()
+ )
+ if (toShow.getOrNull(5) == true) processMedia(
+ "MangaPlanned",
+ response?.data?.plannedManga?.lists?.flatMap { it.entries ?: emptyList() }?.reversed(),
+ null
+ )
- fun favorite(type: String) {
- val favourites =
- if (type == "Anime") response?.data?.favoriteAnime?.favourites else response?.data?.favoriteManga?.favourites
- val apiMediaList = if (type == "Anime") favourites?.anime else favourites?.manga
+ fun processFavorites(type: String, favorites: List?) {
val returnArray = arrayListOf()
- apiMediaList?.edges?.forEach {
- it.node?.let { i ->
- val m = Media(i).apply { isFav = true }
- if (m.id !in removeList && if (hidePrivate) !m.isListPrivate else true) {
- returnArray.add(m)
+ favorites?.forEach { edge ->
+ edge.node?.let {
+ val media = Media(it).apply { isFav = true }
+ if (media.id !in removeList && (!hidePrivate || !media.isListPrivate)) {
+ returnArray.add(media)
} else {
- removedMedia.add(m)
+ removedMedia.add(media)
}
}
}
returnMap["favorite$type"] = returnArray
}
- if (toShow.getOrNull(0) == true) {
- current("Anime")
- }
- if (toShow.getOrNull(1) == true) {
- favorite("Anime")
- }
- if (toShow.getOrNull(2) == true) {
- planned("Anime")
- }
- if (toShow.getOrNull(3) == true) {
- current("Manga")
- }
- if (toShow.getOrNull(4) == true) {
- favorite("Manga")
- }
- if (toShow.getOrNull(5) == true) {
- planned("Manga")
- }
+ if (toShow.getOrNull(1) == true) processFavorites(
+ "Anime",
+ response?.data?.favoriteAnime?.favourites?.anime?.edges
+ )
+ if (toShow.getOrNull(4) == true) processFavorites(
+ "Manga",
+ response?.data?.favoriteManga?.favourites?.manga?.edges
+ )
+
if (toShow.getOrNull(6) == true) {
val subMap = mutableMapOf()
- response?.data?.recommendationQuery?.apply {
- recommendations?.onEach {
- val json = it.mediaRecommendation
- if (json != null) {
- val m = Media(json)
- m.relation = json.type?.toString()
- subMap[m.id] = m
- }
+ response?.data?.recommendationQuery?.recommendations?.forEach {
+ it.mediaRecommendation?.let { json ->
+ val media = Media(json)
+ media.relation = json.type?.toString()
+ subMap[media.id] = media
}
}
- response?.data?.recommendationPlannedQueryAnime?.apply {
- lists?.forEach { li ->
- li.entries?.forEach {
- val m = Media(it)
- if (m.status == "RELEASING" || m.status == "FINISHED") {
- m.relation = it.media?.type?.toString()
- subMap[m.id] = m
- }
- }
+ response?.data?.recommendationPlannedQueryAnime?.lists?.flatMap {
+ it.entries ?: emptyList()
+ }?.forEach {
+ val media = Media(it)
+ if (media.status in listOf("RELEASING", "FINISHED")) {
+ media.relation = it.media?.type?.toString()
+ subMap[media.id] = media
}
}
- response?.data?.recommendationPlannedQueryManga?.apply {
- lists?.forEach { li ->
- li.entries?.forEach {
- val m = Media(it)
- if (m.status == "RELEASING" || m.status == "FINISHED") {
- m.relation = it.media?.type?.toString()
- subMap[m.id] = m
- }
- }
+ response?.data?.recommendationPlannedQueryManga?.lists?.flatMap {
+ it.entries ?: emptyList()
+ }?.forEach {
+ val media = Media(it)
+ if (media.status in listOf("RELEASING", "FINISHED")) {
+ media.relation = it.media?.type?.toString()
+ subMap[media.id] = media
}
}
- val list = ArrayList(subMap.values.toList())
- list.sortByDescending { it.meanScore }
+ val list = ArrayList(subMap.values).apply { sortByDescending { it.meanScore } }
returnMap["recommendations"] = list
}
@@ -1061,102 +1031,97 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
media1?.media?.mapTo(combinedList) { Media(it) }
return combinedList
}
- private fun getPreference(pref: PrefName): Boolean = PrefManager.getVal(pref)
- private fun buildQueryString(sort: String, type: String, format: String? = null, country: String? = null): String {
- val includeList = if (type == "ANIME" && !getPreference(PrefName.IncludeAnimeList)) "onList:false" else if (type == "MANGA" && !getPreference(PrefName.IncludeMangaList)) "onList:false" else ""
- val isAdult = if (getPreference(PrefName.AdultOnly)) "isAdult:true" else ""
- val formatFilter = format?.let { "format: $it, " } ?: ""
- val countryFilter = country?.let { "countryOfOrigin: $it, " } ?: ""
- return """Page(page:1,perPage:50){
- pageInfo{hasNextPage total}
- media(sort:$sort, type:$type, $formatFilter $countryFilter $includeList $isAdult){
- id idMal status chapters episodes nextAiringEpisode{episode}
- isAdult type meanScore isFavourite format bannerImage countryOfOrigin
- coverImage{large} title{english romaji userPreferred}
- mediaListEntry{progress private score(format:POINT_100) status}
+ private fun getPreference(pref: PrefName): Boolean = PrefManager.getVal(pref)
+
+ private fun buildQueryString(
+ sort: String,
+ type: String,
+ format: String? = null,
+ country: String? = null
+ ): String {
+ val includeList = when {
+ type == "ANIME" && !getPreference(PrefName.IncludeAnimeList) -> "onList:false"
+ type == "MANGA" && !getPreference(PrefName.IncludeMangaList) -> "onList:false"
+ else -> ""
+ }
+ val isAdult = if (getPreference(PrefName.AdultOnly)) "isAdult:true" else ""
+ val formatFilter = format?.let { "format:$it, " } ?: ""
+ val countryFilter = country?.let { "countryOfOrigin:$it, " } ?: ""
+
+ return buildString {
+ append("""Page(page:1,perPage:50){pageInfo{hasNextPage total}media(sort:$sort, type:$type, $formatFilter $countryFilter $includeList $isAdult){id idMal status chapters episodes nextAiringEpisode{episode} isAdult type meanScore isFavourite format bannerImage countryOfOrigin coverImage{large} title{english romaji userPreferred} mediaListEntry{progress private score(format:POINT_100) status}}}""")
}
- }"""
}
private fun recentAnimeUpdates(page: Int): String {
val currentTime = System.currentTimeMillis() / 1000
- return """Page(page:$page,perPage:50){
- pageInfo{hasNextPage total}
- airingSchedules(airingAt_greater:0 airingAt_lesser:${currentTime - 10000} sort:TIME_DESC){
- episode airingAt media{
- id idMal status chapters episodes nextAiringEpisode{episode}
- isAdult type meanScore isFavourite format bannerImage countryOfOrigin
- coverImage{large} title{english romaji userPreferred}
- mediaListEntry{progress private score(format:POINT_100) status}
- }
+ return buildString {
+ append("""Page(page:$page,perPage:50){pageInfo{hasNextPage total}airingSchedules(airingAt_greater:0 airingAt_lesser:${currentTime - 10000} sort:TIME_DESC){episode airingAt media{id idMal status chapters episodes nextAiringEpisode{episode} isAdult type meanScore isFavourite format bannerImage countryOfOrigin coverImage{large} title{english romaji userPreferred} mediaListEntry{progress private score(format:POINT_100) status}}}}""")
}
- }"""
- }
- private fun queryAnimeList(): String {
- return """{
- recentUpdates:${recentAnimeUpdates(1)}
- recentUpdates2:${recentAnimeUpdates(2)}
- trendingMovies:${buildQueryString("POPULARITY_DESC", "ANIME", "MOVIE")}
- topRated:${buildQueryString("SCORE_DESC", "ANIME")}
- mostFav:${buildQueryString("FAVOURITES_DESC", "ANIME")}
- }"""
- }
- private fun queryMangaList(): String {
- return """{
- trendingManga:${buildQueryString("POPULARITY_DESC", "MANGA", country = "JP")}
- trendingManhwa:${buildQueryString("POPULARITY_DESC", "MANGA", country = "KR")}
- trendingNovel:${buildQueryString("POPULARITY_DESC", "MANGA", format = "NOVEL", country = "JP")}
- topRated:${buildQueryString("SCORE_DESC", "MANGA")}
- mostFav:${buildQueryString("FAVOURITES_DESC", "MANGA")}
-
- }"""
}
- suspend fun loadAnimeList(): Map> {
+ private fun queryAnimeList(): String {
+ return buildString {
+ append("""{recentUpdates:${recentAnimeUpdates(1)} recentUpdates2:${recentAnimeUpdates(2)} trendingMovies:${buildQueryString("POPULARITY_DESC", "ANIME", "MOVIE")} topRated:${buildQueryString("SCORE_DESC", "ANIME")} mostFav:${buildQueryString("FAVOURITES_DESC", "ANIME")}}""")
+ }
+ }
+
+ private fun queryMangaList(): String {
+ return buildString {
+ append("""{trendingManga:${buildQueryString("POPULARITY_DESC", "MANGA", country = "JP")} trendingManhwa:${buildQueryString("POPULARITY_DESC", "MANGA", country = "KR")} trendingNovel:${buildQueryString("POPULARITY_DESC", "MANGA", format = "NOVEL", country = "JP")} topRated:${buildQueryString("SCORE_DESC", "MANGA")} mostFav:${buildQueryString("FAVOURITES_DESC", "MANGA")}}""")
+ }
+ }
+
+ suspend fun loadAnimeList(): Map> = coroutineScope {
val list = mutableMapOf>()
- fun filterRecentUpdates(
- page: Page?,
- ): ArrayList {
- val listOnly: Boolean = PrefManager.getVal(PrefName.RecentlyListOnly)
- val adultOnly: Boolean = PrefManager.getVal(PrefName.AdultOnly)
- val idArr = mutableListOf()
+ fun filterRecentUpdates(page: Page?): ArrayList {
+ val listOnly = getPreference(PrefName.RecentlyListOnly)
+ val adultOnly = getPreference(PrefName.AdultOnly)
+ val idArr = mutableSetOf()
return page?.airingSchedules?.mapNotNull { i ->
- i.media?.let {
- if (!idArr.contains(it.id)) {
- val shouldAdd = when {
- !listOnly && it.countryOfOrigin == "JP" && Anilist.adult && adultOnly && it.isAdult == true -> true
- !listOnly && !adultOnly && it.countryOfOrigin == "JP" && it.isAdult == false -> true
- listOnly && it.mediaListEntry != null -> true
- else -> false
- }
- if (shouldAdd) {
- idArr.add(it.id)
- Media(it)
- } else null
+ i.media?.takeIf { !idArr.contains(it.id) }?.let {
+ val shouldAdd = when {
+ !listOnly && it.countryOfOrigin == "JP" && adultOnly && it.isAdult == true -> true
+ !listOnly && !adultOnly && it.countryOfOrigin == "JP" && it.isAdult == false -> true
+ listOnly && it.mediaListEntry != null -> true
+ else -> false
+ }
+ if (shouldAdd) {
+ idArr.add(it.id)
+ Media(it)
} else null
}
}?.toCollection(ArrayList()) ?: arrayListOf()
}
- executeQuery(queryAnimeList(), force = true)?.data?.apply {
+
+ val animeList = async { executeQuery(queryAnimeList(), force = true) }
+
+ animeList.await()?.data?.apply {
list["recentUpdates"] = filterRecentUpdates(recentUpdates)
list["trendingMovies"] = mediaList(trendingMovies)
list["topRated"] = mediaList(topRated)
list["mostFav"] = mediaList(mostFav)
}
- return list
+
+ list
}
- suspend fun loadMangaList(): Map> {
+
+ suspend fun loadMangaList(): Map> = coroutineScope {
val list = mutableMapOf>()
- executeQuery(queryMangaList(), force = true)?.data?.apply {
+
+ val mangaList = async { executeQuery(queryMangaList(), force = true) }
+
+ mangaList.await()?.data?.apply {
list["trendingManga"] = mediaList(trendingManga)
list["trendingManhwa"] = mediaList(trendingManhwa)
list["trendingNovel"] = mediaList(trendingNovel)
list["topRated"] = mediaList(topRated)
list["mostFav"] = mediaList(mostFav)
}
- return list
+
+ list
}
@@ -1560,10 +1525,10 @@ Page(page:$page,perPage:50) {
resetNotification: Boolean = true,
type: Boolean? = null
): NotificationResponse? {
- val type_in = "type_in:[AIRING,MEDIA_MERGE,MEDIA_DELETION,MEDIA_DATA_CHANGE]"
+ val typeIn = "type_in:[AIRING,MEDIA_MERGE,MEDIA_DELETION,MEDIA_DATA_CHANGE]"
val reset = if (resetNotification) "true" else "false"
val res = executeQuery(
- """{User(id:$id){unreadNotificationCount}Page(page:$page,perPage:$ITEMS_PER_PAGE){pageInfo{currentPage,hasNextPage}notifications(resetNotificationCount:$reset , ${if (type == true) type_in else ""}){__typename...on AiringNotification{id,type,animeId,episode,contexts,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}},}...on FollowingNotification{id,userId,type,context,createdAt,user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMessageNotification{id,userId,type,activityId,context,createdAt,message{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMentionNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplySubscribedNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentMentionNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentReplyNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentSubscribedNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentLikeNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadLikeNotification{id,userId,type,threadId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on RelatedMediaAdditionNotification{id,type,context,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDataChangeNotification{id,type,mediaId,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaMergeNotification{id,type,mediaId,deletedMediaTitles,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDeletionNotification{id,type,deletedMediaTitle,context,reason,createdAt,}}}}""",
+ """{User(id:$id){unreadNotificationCount}Page(page:$page,perPage:$ITEMS_PER_PAGE){pageInfo{currentPage,hasNextPage}notifications(resetNotificationCount:$reset , ${if (type == true) typeIn else ""}){__typename...on AiringNotification{id,type,animeId,episode,contexts,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}},}...on FollowingNotification{id,userId,type,context,createdAt,user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMessageNotification{id,userId,type,activityId,context,createdAt,message{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMentionNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplySubscribedNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentMentionNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentReplyNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentSubscribedNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentLikeNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadLikeNotification{id,userId,type,threadId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on RelatedMediaAdditionNotification{id,type,context,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDataChangeNotification{id,type,mediaId,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaMergeNotification{id,type,mediaId,deletedMediaTitles,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDeletionNotification{id,type,deletedMediaTitle,context,reason,createdAt,}}}}""",
force = true
)
if (res != null && resetNotification) {
diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt
index e75383a1..f1db7ff6 100644
--- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt
+++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt
@@ -91,17 +91,16 @@ class AnilistHomeViewModel : ViewModel() {
fun getHidden(): LiveData> = hidden
- @Suppress("UNCHECKED_CAST")
suspend fun initHomePage() {
val res = Anilist.query.initHomePage()
- res["currentAnime"]?.let { animeContinue.postValue(it as ArrayList?) }
- res["favoriteAnime"]?.let { animeFav.postValue(it as ArrayList?) }
- res["plannedAnime"]?.let { animePlanned.postValue(it as ArrayList?) }
- res["currentManga"]?.let { mangaContinue.postValue(it as ArrayList?) }
- res["favoriteManga"]?.let { mangaFav.postValue(it as ArrayList?) }
- res["plannedManga"]?.let { mangaPlanned.postValue(it as ArrayList?) }
- res["recommendations"]?.let { recommendation.postValue(it as ArrayList?) }
- res["hidden"]?.let { hidden.postValue(it as ArrayList?) }
+ res["currentAnime"]?.let { animeContinue.postValue(it) }
+ res["favoriteAnime"]?.let { animeFav.postValue(it) }
+ res["currentAnimePlanned"]?.let { animePlanned.postValue(it) }
+ res["currentManga"]?.let { mangaContinue.postValue(it) }
+ res["favoriteManga"]?.let { mangaFav.postValue(it) }
+ res["currentMangaPlanned"]?.let { mangaPlanned.postValue(it) }
+ res["recommendations"]?.let { recommendation.postValue(it) }
+ res["hidden"]?.let { hidden.postValue(it) }
}
suspend fun loadMain(context: FragmentActivity) {
diff --git a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
index 4452a2df..ed3cb02c 100644
--- a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
@@ -32,6 +32,7 @@ import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_STAR
import ani.dantotsu.media.manga.MangaReadFragment.Companion.EXTRA_CHAPTER_NUMBER
import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
+import ani.dantotsu.util.NumberConverter.Companion.ofLength
import com.anggrayudi.storage.file.deleteRecursively
import com.anggrayudi.storage.file.forceDelete
import com.anggrayudi.storage.file.openOutputStream
@@ -235,7 +236,7 @@ class MangaDownloaderService : Service() {
}
if (bitmap != null) {
- saveToDisk("$index.jpg", outputDir, bitmap)
+ saveToDisk("${index.ofLength(3)}.jpg", outputDir, bitmap)
}
farthest++
diff --git a/app/src/main/java/ani/dantotsu/home/HomeFragment.kt b/app/src/main/java/ani/dantotsu/home/HomeFragment.kt
index 43bf8bea..dfd39e6a 100644
--- a/app/src/main/java/ani/dantotsu/home/HomeFragment.kt
+++ b/app/src/main/java/ani/dantotsu/home/HomeFragment.kt
@@ -50,6 +50,7 @@ import ani.dantotsu.statusBarHeight
import ani.dantotsu.util.Logger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.math.max
@@ -457,53 +458,56 @@ class HomeFragment : Fragment() {
var running = false
val live = Refresh.activity.getOrPut(1) { MutableLiveData(true) }
- live.observe(viewLifecycleOwner)
- {
- if (!running && it) {
+ live.observe(viewLifecycleOwner) { shouldRefresh ->
+ if (!running && shouldRefresh) {
running = true
scope.launch {
withContext(Dispatchers.IO) {
- //Get userData First
- Anilist.userid =
- PrefManager.getNullableVal(PrefName.AnilistUserId, null)
- ?.toIntOrNull()
+ // Get user data first
+ Anilist.userid = PrefManager.getNullableVal(PrefName.AnilistUserId, null)?.toIntOrNull()
if (Anilist.userid == null) {
- getUserId(requireContext()) {
- load()
- }
- } else {
- CoroutineScope(Dispatchers.IO).launch {
+ withContext(Dispatchers.Main) {
getUserId(requireContext()) {
load()
}
}
+ } else {
+ getUserId(requireContext()) {
+ load()
+ }
}
model.loaded = true
- CoroutineScope(Dispatchers.IO).launch {
- model.setListImages()
- }
+ model.setListImages()
+ }
- var empty = true
- val homeLayoutShow: List =
- PrefManager.getVal(PrefName.HomeLayout)
- model.initHomePage()
- model.initUserStatus()
- (array.indices).forEach { i ->
+ var empty = true
+ val homeLayoutShow: List = PrefManager.getVal(PrefName.HomeLayout)
+
+ withContext(Dispatchers.Main) {
+ homeLayoutShow.indices.forEach { i ->
if (homeLayoutShow.elementAt(i)) {
empty = false
- } else withContext(Dispatchers.Main) {
+ } else {
containers[i].visibility = View.GONE
}
}
- model.empty.postValue(empty)
}
+
+ val initHomePage = async(Dispatchers.IO) { model.initHomePage() }
+ val initUserStatus = async(Dispatchers.IO) { model.initUserStatus() }
+ initHomePage.await()
+ initUserStatus.await()
+
+ withContext(Dispatchers.Main) {
+ model.empty.postValue(empty)
+ binding.homeHiddenItemsContainer.visibility = View.GONE
+ }
+
live.postValue(false)
_binding?.homeRefresh?.isRefreshing = false
running = false
}
- binding.homeHiddenItemsContainer.visibility = View.GONE
}
-
}
}
diff --git a/app/src/main/java/ani/dantotsu/home/status/StatusActivity.kt b/app/src/main/java/ani/dantotsu/home/status/StatusActivity.kt
index 7fc0e78b..ced8b2d8 100644
--- a/app/src/main/java/ani/dantotsu/home/status/StatusActivity.kt
+++ b/app/src/main/java/ani/dantotsu/home/status/StatusActivity.kt
@@ -49,7 +49,10 @@ class StatusActivity : AppCompatActivity(), StoriesCallback {
if (activity.getOrNull(position) != null) {
val startFrom = findFirstNonMatch(watchedActivity, activity[position].activity )
val startIndex = if ( startFrom > 0) startFrom else 0
- binding.stories.setStoriesList(activity[position].activity, this, startIndex + 1)
+ binding.stories.setStoriesList(
+ activityList = activity[position].activity,
+ startIndex = startIndex + 1
+ )
} else {
Logger.log("index out of bounds for position $position of size ${activity.size}")
finish()
@@ -92,7 +95,7 @@ class StatusActivity : AppCompatActivity(), StoriesCallback {
val startFrom = findFirstNonMatch(watchedActivity, activity[position].activity )
val startIndex= if ( startFrom > 0) startFrom else 0
binding.stories.startAnimation(slideOutLeft)
- binding.stories.setStoriesList(activity[position].activity, this, startIndex + 1)
+ binding.stories.setStoriesList(activity[position].activity, startIndex + 1)
binding.stories.startAnimation(slideInRight)
} else {
finish()
@@ -107,7 +110,7 @@ class StatusActivity : AppCompatActivity(), StoriesCallback {
val startFrom = findFirstNonMatch(watchedActivity, activity[position].activity )
val startIndex = if ( startFrom > 0) startFrom else 0
binding.stories.startAnimation(slideOutRight)
- binding.stories.setStoriesList(activity[position].activity, this, startIndex + 1)
+ binding.stories.setStoriesList(activity[position].activity,startIndex + 1)
binding.stories.startAnimation(slideInLeft)
} else {
finish()
diff --git a/app/src/main/java/ani/dantotsu/home/status/Stories.kt b/app/src/main/java/ani/dantotsu/home/status/Stories.kt
index a017b7ab..a141a5d6 100644
--- a/app/src/main/java/ani/dantotsu/home/status/Stories.kt
+++ b/app/src/main/java/ani/dantotsu/home/status/Stories.kt
@@ -49,7 +49,6 @@ import kotlin.math.abs
class Stories @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), View.OnTouchListener {
- private lateinit var activity: FragmentActivity
private lateinit var binding: FragmentStatusBinding
private lateinit var activityList: List
private lateinit var storiesListener: StoriesCallback
@@ -80,10 +79,9 @@ class Stories @JvmOverloads constructor(
fun setStoriesList(
- activityList: List, activity: FragmentActivity, startIndex: Int = 1
+ activityList: List, startIndex: Int = 1
) {
this.activityList = activityList
- this.activity = activity
this.storyIndex = startIndex
addLoadingViews(activityList)
}
@@ -368,7 +366,9 @@ class Stories @JvmOverloads constructor(
if (
story.status?.contains("completed") == false &&
!story.status.contains("plans") &&
- !story.status.contains("repeating")
+ !story.status.contains("repeating")&&
+ !story.status.contains("paused")&&
+ !story.status.contains("dropped")
) {
"of ${story.media?.title?.userPreferred}"
} else {
@@ -389,7 +389,7 @@ class Stories @JvmOverloads constructor(
story.media?.id
),
ActivityOptionsCompat.makeSceneTransitionAnimation(
- activity,
+ (it.context as FragmentActivity),
binding.coverImage,
ViewCompat.getTransitionName(binding.coverImage)!!
).toBundle()
@@ -427,7 +427,7 @@ class Stories @JvmOverloads constructor(
binding.activityReplies.setColorFilter(ContextCompat.getColor(context, R.color.bg_opp))
binding.activityRepliesContainer.setOnClickListener {
RepliesBottomDialog.newInstance(story.id)
- .show(activity.supportFragmentManager, "replies")
+ .show((it.context as FragmentActivity).supportFragmentManager, "replies")
}
binding.activityLike.setColorFilter(if (story.isLiked == true) likeColor else notLikeColor)
binding.activityLikeCount.text = story.likeCount.toString()
@@ -435,10 +435,9 @@ class Stories @JvmOverloads constructor(
like()
}
binding.activityLikeContainer.setOnLongClickListener {
- val context = activity
UsersDialogFragment().apply {
userList(userList)
- show(context.supportFragmentManager, "dialog")
+ show((it.context as FragmentActivity).supportFragmentManager, "dialog")
}
true
}
diff --git a/app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt b/app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt
index 3b9b32d0..8b1500bd 100644
--- a/app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt
+++ b/app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt
@@ -60,7 +60,7 @@ class ActivityFragment : Fragment() {
binding.feedRefresh.updateLayoutParams {
bottomMargin = navBarHeight
}
- binding.emptyTextView.text = getString(R.string.no_activities)
+ binding.emptyTextView.text = getString(R.string.nothing_here)
lifecycleScope.launch {
getList()
if (adapter.itemCount == 0) {
diff --git a/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt b/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt
index 45122fec..40cb8f96 100644
--- a/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt
+++ b/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt
@@ -51,6 +51,7 @@ class FeedActivity : AppCompatActivity() {
binding.notificationBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
val getOne = intent.getIntExtra("activityId", -1)
if (getOne != -1) { navBar.visibility = View.GONE }
+ binding.notificationViewPager.isUserInputEnabled = false
binding.notificationViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, getOne)
binding.notificationViewPager.setOffscreenPageLimit(4)
binding.notificationViewPager.setCurrentItem(selected, false)
@@ -63,13 +64,7 @@ class FeedActivity : AppCompatActivity() {
newTab: AnimatedBottomBar.Tab
) {
selected = newIndex
- binding.notificationViewPager.setCurrentItem(selected, true)
- }
- })
- binding.notificationViewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
- override fun onPageSelected(position: Int) {
- super.onPageSelected(position)
- navBar.selectTabAt(position)
+ binding.notificationViewPager.setCurrentItem(selected, false)
}
})
}
diff --git a/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt b/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt
index 75089031..bd5fad0b 100644
--- a/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt
+++ b/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt
@@ -16,7 +16,8 @@ import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
-import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType
+import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.*
+import ani.dantotsu.profile.notification.NotificationFragment.Companion.newInstance
import nl.joery.animatedbottombar.AnimatedBottomBar
class NotificationActivity : AppCompatActivity() {
@@ -48,6 +49,7 @@ class NotificationActivity : AppCompatActivity() {
binding.notificationBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
val getOne = intent.getIntExtra("activityId", -1)
if (getOne != -1) navBar.isVisible = false
+ binding.notificationViewPager.isUserInputEnabled = false
binding.notificationViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, getOne)
binding.notificationViewPager.setCurrentItem(selected, false)
navBar.selectTabAt(selected)
@@ -59,13 +61,7 @@ class NotificationActivity : AppCompatActivity() {
newTab: AnimatedBottomBar.Tab
) {
selected = newIndex
- binding.notificationViewPager.setCurrentItem(selected, true)
- }
- })
- binding.notificationViewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
- override fun onPageSelected(position: Int) {
- super.onPageSelected(position)
- navBar.selectTabAt(position)
+ binding.notificationViewPager.setCurrentItem(selected, false)
}
})
}
@@ -83,11 +79,11 @@ class NotificationActivity : AppCompatActivity() {
override fun getItemCount(): Int = if (id != -1) 1 else 4
override fun createFragment(position: Int): Fragment = when (position) {
- 0 -> NotificationFragment.newInstance(NotificationType.USER)
- 1 -> NotificationFragment.newInstance(if (id != -1) NotificationType.ONE else NotificationType.MEDIA, id)
- 2 -> NotificationFragment.newInstance(NotificationType.SUBSCRIPTION)
- 3 -> NotificationFragment.newInstance(NotificationType.COMMENT)
- else -> NotificationFragment.newInstance(NotificationType.MEDIA)
+ 0 -> newInstance(USER)
+ 1 -> newInstance(if (id != -1) ONE else MEDIA, id)
+ 2 -> newInstance(SUBSCRIPTION)
+ 3 -> newInstance(COMMENT)
+ else -> newInstance(MEDIA)
}
}
}
diff --git a/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt b/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt
index 6360763d..c38ec0fb 100644
--- a/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt
+++ b/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt
@@ -20,7 +20,11 @@ import ani.dantotsu.notifications.comment.CommentStore
import ani.dantotsu.notifications.subscription.SubscriptionStore
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.profile.activity.FeedActivity
-import ani.dantotsu.setBaseline
+import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.COMMENT
+import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.MEDIA
+import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.ONE
+import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.SUBSCRIPTION
+import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.USER
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import com.xwray.groupie.GroupieAdapter
@@ -29,8 +33,8 @@ import kotlinx.coroutines.launch
class NotificationFragment : Fragment() {
- private lateinit var type : NotificationType
- private var getID : Int = -1
+ private lateinit var type: NotificationType
+ private var getID: Int = -1
private lateinit var binding: FragmentNotificationsBinding
private var adapter: GroupieAdapter = GroupieAdapter()
private var currentPage = 1
@@ -53,12 +57,10 @@ class NotificationFragment : Fragment() {
binding.notificationRecyclerView.adapter = adapter
binding.notificationRecyclerView.layoutManager = LinearLayoutManager(context)
binding.notificationProgressBar.isVisible = true
- binding.emptyTextView.text = getString(R.string.no_notifications)
+ binding.emptyTextView.text = getString(R.string.nothing_here)
lifecycleScope.launch {
getList()
- if (adapter.itemCount == 0) {
- binding.emptyTextView.isVisible = true
- }
+
binding.notificationProgressBar.isVisible = false
}
binding.notificationSwipeRefresh.setOnRefreshListener {
@@ -87,13 +89,16 @@ class NotificationFragment : Fragment() {
private suspend fun getList() {
val list = when (type) {
- NotificationType.ONE -> getNotificationsFiltered(false) { it.id == getID }
- NotificationType.MEDIA -> getNotificationsFiltered(type = true) { it.media != null }
- NotificationType.USER -> getNotificationsFiltered { it.media == null }
- NotificationType.SUBSCRIPTION -> getSubscriptions()
- NotificationType.COMMENT -> getComments()
+ ONE -> getNotificationsFiltered(false) { it.id == getID }
+ MEDIA -> getNotificationsFiltered(type = true) { it.media != null }
+ USER -> getNotificationsFiltered { it.media == null }
+ SUBSCRIPTION -> getSubscriptions()
+ COMMENT -> getComments()
+ }
+ adapter.addAll(list.map { NotificationItem(it, type, adapter, ::onClick) })
+ if (adapter.itemCount == 0) {
+ binding.emptyTextView.isVisible = true
}
- adapter.addAll(list.map { NotificationItem(it, ::onClick) })
}
private suspend fun getNotificationsFiltered(
@@ -114,8 +119,11 @@ class NotificationFragment : Fragment() {
PrefName.SubscriptionNotificationStore,
null
) ?: listOf()
- return list.sortedByDescending { (it.time / 1000L).toInt() }
- .filter { it.image != null }.map {
+
+ return list
+ .sortedByDescending { (it.time / 1000L).toInt() }
+ .filter { it.image != null } // to remove old data
+ .map {
Notification(
it.type,
System.currentTimeMillis().toInt(),
@@ -162,19 +170,31 @@ class NotificationFragment : Fragment() {
fun onClick(id: Int, optional: Int?, type: NotificationClickType) {
val intent = when (type) {
- NotificationClickType.USER -> Intent(requireContext(), ProfileActivity::class.java).apply {
+ NotificationClickType.USER -> Intent(
+ requireContext(),
+ ProfileActivity::class.java
+ ).apply {
putExtra("userId", id)
}
- NotificationClickType.MEDIA -> Intent(requireContext(), MediaDetailsActivity::class.java).apply {
+ NotificationClickType.MEDIA -> Intent(
+ requireContext(),
+ MediaDetailsActivity::class.java
+ ).apply {
putExtra("mediaId", id)
}
- NotificationClickType.ACTIVITY -> Intent(requireContext(), FeedActivity::class.java).apply {
+ NotificationClickType.ACTIVITY -> Intent(
+ requireContext(),
+ FeedActivity::class.java
+ ).apply {
putExtra("activityId", id)
}
- NotificationClickType.COMMENT -> Intent(requireContext(), MediaDetailsActivity::class.java).apply {
+ NotificationClickType.COMMENT -> Intent(
+ requireContext(),
+ MediaDetailsActivity::class.java
+ ).apply {
putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
putExtra("mediaId", id)
putExtra("commentId", optional ?: -1)
@@ -188,6 +208,7 @@ class NotificationFragment : Fragment() {
}
}
+
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
diff --git a/app/src/main/java/ani/dantotsu/profile/notification/NotificationItem.kt b/app/src/main/java/ani/dantotsu/profile/notification/NotificationItem.kt
index f51225ea..61273374 100644
--- a/app/src/main/java/ani/dantotsu/profile/notification/NotificationItem.kt
+++ b/app/src/main/java/ani/dantotsu/profile/notification/NotificationItem.kt
@@ -8,16 +8,27 @@ import ani.dantotsu.connections.anilist.api.Notification
import ani.dantotsu.connections.anilist.api.NotificationType
import ani.dantotsu.databinding.ItemNotificationBinding
import ani.dantotsu.loadImage
-import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationClickType
+import ani.dantotsu.notifications.comment.CommentStore
+import ani.dantotsu.notifications.subscription.SubscriptionStore
import ani.dantotsu.profile.activity.ActivityItemBuilder
+import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationClickType
+import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.COMMENT
+import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.SUBSCRIPTION
import ani.dantotsu.setAnimation
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.toPx
+import ani.dantotsu.util.customAlertDialog
+import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.viewbinding.BindableItem
class NotificationItem(
private val notification: Notification,
- val clickCallback: (Int, Int?, NotificationClickType) -> Unit
-) : BindableItem() {
+ val type: NotificationFragment.Companion.NotificationType,
+ val parentAdapter: GroupieAdapter,
+ val clickCallback: (Int, Int?, NotificationClickType) -> Unit,
+
+ ) : BindableItem() {
private lateinit var binding: ItemNotificationBinding
override fun bind(viewBinding: ItemNotificationBinding, position: Int) {
binding = viewBinding
@@ -25,6 +36,48 @@ class NotificationItem(
setBinding()
}
+ fun dialog() {
+ when (type) {
+ COMMENT, SUBSCRIPTION -> {
+ binding.root.context.customAlertDialog().apply {
+ setTitle(R.string.delete)
+ setMessage(ActivityItemBuilder.getContent(notification))
+ setPosButton(R.string.yes) {
+ when (type) {
+ COMMENT -> {
+ val list = PrefManager.getNullableVal>(
+ PrefName.CommentNotificationStore,
+ null
+ ) ?: listOf()
+ val newList = list.filter { it.commentId != notification.commentId }
+ PrefManager.setVal(PrefName.CommentNotificationStore, newList)
+ parentAdapter.remove(this@NotificationItem)
+
+ }
+
+ SUBSCRIPTION -> {
+ val list = PrefManager.getNullableVal>(
+ PrefName.SubscriptionNotificationStore,
+ null
+ ) ?: listOf()
+ val newList = list.filter { (it.time / 1000L).toInt() != notification.createdAt}
+ PrefManager.setVal(PrefName.SubscriptionNotificationStore, newList)
+ parentAdapter.remove(this@NotificationItem)
+ }
+
+ else -> {}
+ }
+ }
+ setNegButton(R.string.no)
+ show()
+ }
+ }
+
+ else -> {}
+ }
+
+ }
+
override fun getLayout(): Int {
return R.layout.item_notification
}
@@ -33,7 +86,11 @@ class NotificationItem(
return ItemNotificationBinding.bind(view)
}
- private fun image(user: Boolean = false, commentNotification: Boolean = false, newRelease: Boolean = false) {
+ private fun image(
+ user: Boolean = false,
+ commentNotification: Boolean = false,
+ newRelease: Boolean = false
+ ) {
val cover = if (user) notification.user?.bannerImage
?: notification.user?.avatar?.medium else notification.media?.bannerImage
@@ -348,6 +405,14 @@ class NotificationItem(
}
}
}
+ binding.notificationCoverUser.setOnLongClickListener {
+ dialog()
+ true
+ }
+ binding.notificationBannerImage.setOnLongClickListener {
+ dialog()
+ true
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/settings/SubscriptionItem.kt b/app/src/main/java/ani/dantotsu/settings/SubscriptionItem.kt
index bb8c0396..bb60cd4e 100644
--- a/app/src/main/java/ani/dantotsu/settings/SubscriptionItem.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SubscriptionItem.kt
@@ -9,31 +9,25 @@ import ani.dantotsu.loadImage
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.notifications.subscription.SubscriptionHelper
import com.xwray.groupie.GroupieAdapter
-import com.xwray.groupie.Item
import com.xwray.groupie.viewbinding.BindableItem
class SubscriptionItem(
val id: Int,
private val media: SubscriptionHelper.Companion.SubscribeMedia,
- private val adapter: GroupieAdapter
+ private val adapter: GroupieAdapter,
+ private val onItemRemoved: (Int) -> Unit
) : BindableItem() {
private lateinit var binding: ItemSubscriptionBinding
- override fun bind(p0: ItemSubscriptionBinding, p1: Int) {
- val context = p0.root.context
- binding = p0
- val parserName = if (media.isAnime)
- SubscriptionHelper.getAnimeParser(media.id).name
- else
- SubscriptionHelper.getMangaParser(media.id).name
- val mediaName = media.name
- val showName = "$mediaName ($parserName)"
- binding.subscriptionName.text = showName
+
+ override fun bind(viewBinding: ItemSubscriptionBinding, position: Int) {
+ binding = viewBinding
+ val context = binding.root.context
+
+ binding.subscriptionName.text = media.name
binding.root.setOnClickListener {
ContextCompat.startActivity(
context,
- Intent(context, MediaDetailsActivity::class.java).putExtra(
- "mediaId", media.id
- ),
+ Intent(context, MediaDetailsActivity::class.java).putExtra("mediaId", media.id),
null
)
}
@@ -41,14 +35,11 @@ class SubscriptionItem(
binding.deleteSubscription.setOnClickListener {
SubscriptionHelper.deleteSubscription(id, true)
adapter.remove(this)
+ onItemRemoved(id)
}
}
- override fun getLayout(): Int {
- return R.layout.item_subscription
- }
+ override fun getLayout(): Int = R.layout.item_subscription
- override fun initializeViewBinding(p0: View): ItemSubscriptionBinding {
- return ItemSubscriptionBinding.bind(p0)
- }
+ override fun initializeViewBinding(view: View): ItemSubscriptionBinding = ItemSubscriptionBinding.bind(view)
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/settings/SubscriptionSource.kt b/app/src/main/java/ani/dantotsu/settings/SubscriptionSource.kt
new file mode 100644
index 00000000..80975159
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/settings/SubscriptionSource.kt
@@ -0,0 +1,113 @@
+package ani.dantotsu.settings
+
+import android.app.AlertDialog
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.view.View
+import android.view.ViewGroup
+import ani.dantotsu.R
+import ani.dantotsu.databinding.ItemExtensionBinding
+import ani.dantotsu.notifications.subscription.SubscriptionHelper
+import com.xwray.groupie.GroupieAdapter
+import com.xwray.groupie.viewbinding.BindableItem
+
+class SubscriptionSource(
+ private val parserName: String,
+ private val subscriptions: MutableList,
+ private val adapter: GroupieAdapter,
+ private var parserIcon: Drawable? = null,
+ private val onGroupRemoved: (SubscriptionSource) -> Unit
+) : BindableItem() {
+ private lateinit var binding: ItemExtensionBinding
+ private var isExpanded = false
+
+ override fun bind(viewBinding: ItemExtensionBinding, position: Int) {
+ binding = viewBinding
+ binding.extensionNameTextView.text = parserName
+ updateSubscriptionCount()
+ binding.extensionSubscriptions.visibility = View.VISIBLE
+
+ binding.extensionSubscriptions.setOnClickListener(null)
+ binding.root.setOnClickListener {
+ isExpanded = !isExpanded
+ toggleSubscriptions()
+ }
+ binding.subscriptionCount.setOnClickListener {
+ showRemoveAllSubscriptionsDialog(it.context)
+ }
+ binding.extensionIconImageView.visibility = View.VISIBLE
+ val layoutParams = binding.extensionIconImageView.layoutParams as ViewGroup.MarginLayoutParams
+ layoutParams.leftMargin = 28
+ binding.extensionIconImageView.layoutParams = layoutParams
+
+ parserIcon?.let {
+ binding.extensionIconImageView.setImageDrawable(it)
+ } ?: run {
+ binding.extensionIconImageView.setImageResource(R.drawable.control_background_40dp)
+ }
+
+ binding.extensionPinImageView.visibility = View.GONE
+ binding.extensionVersionTextView.visibility = View.GONE
+ binding.closeTextView.visibility = View.GONE
+ binding.settingsImageView.visibility = View.GONE
+ }
+
+ private fun updateSubscriptionCount() {
+ binding.subscriptionCount.text = subscriptions.size.toString()
+ binding.subscriptionCount.visibility = if (subscriptions.isEmpty()) View.GONE else View.VISIBLE
+ }
+
+ private fun showRemoveAllSubscriptionsDialog(context: Context) {
+ AlertDialog.Builder(context, R.style.MyPopup)
+ .setTitle(R.string.remove_all_subscriptions)
+ .setMessage(context.getString(R.string.remove_all_subscriptions_desc, parserName))
+ .setPositiveButton(R.string.apply) { _, _ ->
+ removeAllSubscriptions()
+ }
+ .setNegativeButton(R.string.cancel, null)
+ .show()
+ }
+
+ private fun removeAllSubscriptions() {
+ subscriptions.forEach { subscription ->
+ SubscriptionHelper.deleteSubscription(subscription.id, false)
+ }
+ if (isExpanded) {
+ val startPosition = adapter.getAdapterPosition(this) + 1
+ repeat(subscriptions.size) {
+ adapter.removeGroupAtAdapterPosition(startPosition)
+ }
+ }
+ subscriptions.clear()
+ onGroupRemoved(this)
+ }
+
+ private fun removeSubscription(id: Any?) {
+ subscriptions.removeAll { it.id == id }
+ updateSubscriptionCount()
+ if (subscriptions.isEmpty()) {
+ onGroupRemoved(this)
+ } else {
+ adapter.notifyItemChanged(adapter.getAdapterPosition(this))
+ }
+ }
+
+ private fun toggleSubscriptions() {
+ val startPosition = adapter.getAdapterPosition(this) + 1
+ if (isExpanded) {
+ subscriptions.forEachIndexed { index, subscribeMedia ->
+ adapter.add(startPosition + index, SubscriptionItem(subscribeMedia.id, subscribeMedia, adapter) { removedId ->
+ removeSubscription(removedId)
+ })
+ }
+ } else {
+ repeat(subscriptions.size) {
+ adapter.removeGroupAtAdapterPosition(startPosition)
+ }
+ }
+ }
+
+ override fun getLayout(): Int = R.layout.item_extension
+
+ override fun initializeViewBinding(view: View): ItemExtensionBinding = ItemExtensionBinding.bind(view)
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt b/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt
index 2b833114..93fd58db 100644
--- a/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt
@@ -1,5 +1,6 @@
package ani.dantotsu.settings
+import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -9,13 +10,21 @@ import ani.dantotsu.BottomSheetDialogFragment
import ani.dantotsu.R
import ani.dantotsu.databinding.BottomSheetRecyclerBinding
import ani.dantotsu.notifications.subscription.SubscriptionHelper
+import ani.dantotsu.parsers.novel.NovelExtensionManager
import com.xwray.groupie.GroupieAdapter
+import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
+import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
class SubscriptionsBottomDialog : BottomSheetDialogFragment() {
private var _binding: BottomSheetRecyclerBinding? = null
private val binding get() = _binding!!
private val adapter: GroupieAdapter = GroupieAdapter()
private var subscriptions: Map = mapOf()
+ private val animeExtension: AnimeExtensionManager = Injekt.get()
+ private val mangaExtensions: MangaExtensionManager = Injekt.get()
+ private val novelExtensions: NovelExtensionManager = Injekt.get()
override fun onCreateView(
inflater: LayoutInflater,
@@ -36,8 +45,33 @@ class SubscriptionsBottomDialog : BottomSheetDialogFragment() {
val context = requireContext()
binding.title.text = context.getString(R.string.subscriptions)
binding.replyButton.visibility = View.GONE
- subscriptions.forEach { (id, media) ->
- adapter.add(SubscriptionItem(id, media, adapter))
+
+ val groupedSubscriptions = subscriptions.values.groupBy {
+ if (it.isAnime) SubscriptionHelper.getAnimeParser(it.id).name
+ else SubscriptionHelper.getMangaParser(it.id).name
+ }
+
+ groupedSubscriptions.forEach { (parserName, mediaList) ->
+ adapter.add(SubscriptionSource(
+ parserName,
+ mediaList.toMutableList(),
+ adapter,
+ getParserIcon(parserName)
+ ) { group ->
+ adapter.remove(group)
+ })
+ }
+ }
+
+ private fun getParserIcon(parserName: String): Drawable? {
+ return when {
+ animeExtension.installedExtensionsFlow.value.any { it.name == parserName } ->
+ animeExtension.installedExtensionsFlow.value.find { it.name == parserName }?.icon
+ mangaExtensions.installedExtensionsFlow.value.any { it.name == parserName } ->
+ mangaExtensions.installedExtensionsFlow.value.find { it.name == parserName }?.icon
+ novelExtensions.installedExtensionsFlow.value.any { it.name == parserName } ->
+ novelExtensions.installedExtensionsFlow.value.find { it.name == parserName }?.icon
+ else -> null
}
}
diff --git a/app/src/main/java/ani/dantotsu/util/ActivityMarkdownCreator.kt b/app/src/main/java/ani/dantotsu/util/ActivityMarkdownCreator.kt
index ccddfbab..fdc24937 100644
--- a/app/src/main/java/ani/dantotsu/util/ActivityMarkdownCreator.kt
+++ b/app/src/main/java/ani/dantotsu/util/ActivityMarkdownCreator.kt
@@ -5,7 +5,6 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
-import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.updateLayoutParams
import androidx.core.widget.addTextChangedListener
@@ -16,6 +15,7 @@ import ani.dantotsu.databinding.ActivityMarkdownCreatorBinding
import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.openLinkInBrowser
+import ani.dantotsu.others.AndroidBug5497Workaround
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.toast
@@ -42,7 +42,7 @@ class ActivityMarkdownCreator : AppCompatActivity() {
BOLD("****", 2, R.id.formatBold),
ITALIC("**", 1, R.id.formatItalic),
STRIKETHROUGH("~~~~", 2, R.id.formatStrikethrough),
- SPOILER("||", 2, R.id.formatSpoiler),
+ SPOILER("~||~", 2, R.id.formatSpoiler),
LINK("[Placeholder](%s)", 0, R.id.formatLink),
IMAGE("img(%s)", 0, R.id.formatImage),
YOUTUBE("youtube(%s)", 0, R.id.formatYoutube),
@@ -68,6 +68,7 @@ class ActivityMarkdownCreator : AppCompatActivity() {
bottomMargin += navBarHeight
}
setContentView(binding.root)
+ AndroidBug5497Workaround.assistActivity(this) {}
val params = binding.createButton.layoutParams as ViewGroup.MarginLayoutParams
params.marginEnd = 16 * resources.displayMetrics.density.toInt()
diff --git a/app/src/main/java/ani/dantotsu/util/NumberConverter.kt b/app/src/main/java/ani/dantotsu/util/NumberConverter.kt
index 49d41de7..61aefe4e 100644
--- a/app/src/main/java/ani/dantotsu/util/NumberConverter.kt
+++ b/app/src/main/java/ani/dantotsu/util/NumberConverter.kt
@@ -47,5 +47,9 @@ class NumberConverter {
val intBits = java.lang.Float.floatToIntBits(number)
return Integer.toBinaryString(intBits)
}
+
+ fun Int.ofLength(length: Int): String {
+ return this.toString().padStart(length, '0')
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_status.xml b/app/src/main/res/layout/fragment_status.xml
index 72f488e0..63664c60 100644
--- a/app/src/main/res/layout/fragment_status.xml
+++ b/app/src/main/res/layout/fragment_status.xml
@@ -177,7 +177,7 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginHorizontal="16dp"
- android:layout_marginVertical="32dp"
+ android:layout_marginVertical="6dp"
android:orientation="horizontal">
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index befeed04..832cc9fa 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -83,8 +83,7 @@
- All
- No more notifications
- No more activities
+ Nothing here
Followers
Write a Message
STATUS
@@ -417,6 +416,8 @@
Use Alarm Manager for reliable Notifications
Using Alarm Manger can help fight against battery optimization, but may consume more battery. It also requires the Alarm Manager permission.
Use
+ Remove All Subscriptions
+ Are you sure you want to remove all subscriptions for %1$s?
Notification for Checking Subscriptions
Subscriptions Update Frequency : %1$s
Subscriptions Update Frequency