feat: searching
This commit is contained in:
parent
38d68a7976
commit
7b8af6ea8a
31 changed files with 2109 additions and 702 deletions
|
@ -2,15 +2,25 @@ package ani.dantotsu.connections.anilist
|
||||||
|
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
|
import ani.dantotsu.media.Author
|
||||||
|
import ani.dantotsu.media.Character
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
|
import ani.dantotsu.media.Studio
|
||||||
|
import ani.dantotsu.profile.User
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
data class SearchResults(
|
interface SearchResults<T> {
|
||||||
|
var search: String?
|
||||||
|
var page: Int
|
||||||
|
var results: MutableList<T>
|
||||||
|
var hasNextPage: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
data class AniMangaSearchResults(
|
||||||
val type: String,
|
val type: String,
|
||||||
var isAdult: Boolean,
|
var isAdult: Boolean,
|
||||||
var onList: Boolean? = null,
|
var onList: Boolean? = null,
|
||||||
var perPage: Int? = null,
|
var perPage: Int? = null,
|
||||||
var search: String? = null,
|
|
||||||
var countryOfOrigin: String? = null,
|
var countryOfOrigin: String? = null,
|
||||||
var sort: String? = null,
|
var sort: String? = null,
|
||||||
var genres: MutableList<String>? = null,
|
var genres: MutableList<String>? = null,
|
||||||
|
@ -23,10 +33,11 @@ data class SearchResults(
|
||||||
var seasonYear: Int? = null,
|
var seasonYear: Int? = null,
|
||||||
var startYear: Int? = null,
|
var startYear: Int? = null,
|
||||||
var season: String? = null,
|
var season: String? = null,
|
||||||
var page: Int = 1,
|
override var search: String? = null,
|
||||||
var results: MutableList<Media>,
|
override var page: Int = 1,
|
||||||
var hasNextPage: Boolean,
|
override var results: MutableList<Media>,
|
||||||
) : Serializable {
|
override var hasNextPage: Boolean,
|
||||||
|
) : SearchResults<Media>, Serializable {
|
||||||
fun toChipList(): List<SearchChip> {
|
fun toChipList(): List<SearchChip> {
|
||||||
val list = mutableListOf<SearchChip>()
|
val list = mutableListOf<SearchChip>()
|
||||||
sort?.let {
|
sort?.let {
|
||||||
|
@ -108,4 +119,33 @@ data class SearchResults(
|
||||||
val type: String,
|
val type: String,
|
||||||
val text: String
|
val text: String
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class CharacterSearchResults(
|
||||||
|
override var search: String?,
|
||||||
|
override var page: Int = 1,
|
||||||
|
override var results: MutableList<Character>,
|
||||||
|
override var hasNextPage: Boolean,
|
||||||
|
) : SearchResults<Character>, Serializable
|
||||||
|
|
||||||
|
data class StudioSearchResults(
|
||||||
|
override var search: String?,
|
||||||
|
override var page: Int = 1,
|
||||||
|
override var results: MutableList<Studio>,
|
||||||
|
override var hasNextPage: Boolean,
|
||||||
|
) : SearchResults<Studio>, Serializable
|
||||||
|
|
||||||
|
|
||||||
|
data class StaffSearchResults(
|
||||||
|
override var search: String?,
|
||||||
|
override var page: Int = 1,
|
||||||
|
override var results: MutableList<Author>,
|
||||||
|
override var hasNextPage: Boolean,
|
||||||
|
) : SearchResults<Author>, Serializable
|
||||||
|
|
||||||
|
data class UserSearchResults(
|
||||||
|
override var search: String?,
|
||||||
|
override var page: Int = 1,
|
||||||
|
override var results: MutableList<User>,
|
||||||
|
override var hasNextPage: Boolean,
|
||||||
|
) : SearchResults<User>, Serializable
|
|
@ -311,7 +311,6 @@ object Anilist {
|
||||||
)
|
)
|
||||||
val remaining = json.headers["X-RateLimit-Remaining"]?.toIntOrNull() ?: -1
|
val remaining = json.headers["X-RateLimit-Remaining"]?.toIntOrNull() ?: -1
|
||||||
Logger.log("Remaining requests: $remaining")
|
Logger.log("Remaining requests: $remaining")
|
||||||
println("Remaining requests: $remaining")
|
|
||||||
if (json.code == 429) {
|
if (json.code == 429) {
|
||||||
val retry = json.headers["Retry-After"]?.toIntOrNull() ?: -1
|
val retry = json.headers["Retry-After"]?.toIntOrNull() ?: -1
|
||||||
val passedLimitReset = json.headers["X-RateLimit-Reset"]?.toLongOrNull() ?: 0
|
val passedLimitReset = json.headers["X-RateLimit-Reset"]?.toLongOrNull() ?: 0
|
||||||
|
|
|
@ -44,7 +44,8 @@ class AnilistQueries {
|
||||||
val response: Query.Viewer?
|
val response: Query.Viewer?
|
||||||
measureTimeMillis {
|
measureTimeMillis {
|
||||||
response = executeQuery(
|
response = executeQuery(
|
||||||
"""{Viewer{name options{timezone titleLanguage staffNameLanguage activityMergeTime airingNotifications displayAdultContent restrictMessagesToFollowing} avatar{medium} bannerImage id mediaListOptions{scoreFormat rowOrder animeList{customLists} mangaList{customLists}} statistics{anime{episodesWatched} manga{chaptersRead}} unreadNotificationCount}}""")
|
"""{Viewer{name options{timezone titleLanguage staffNameLanguage activityMergeTime airingNotifications displayAdultContent restrictMessagesToFollowing} avatar{medium} bannerImage id mediaListOptions{scoreFormat rowOrder animeList{customLists} mangaList{customLists}} statistics{anime{episodesWatched} manga{chaptersRead}} unreadNotificationCount}}"""
|
||||||
|
)
|
||||||
}.also { println("time : $it") }
|
}.also { println("time : $it") }
|
||||||
val user = response?.data?.user ?: return false
|
val user = response?.data?.user ?: return false
|
||||||
|
|
||||||
|
@ -96,12 +97,10 @@ class AnilistQueries {
|
||||||
|
|
||||||
fun mediaDetails(media: Media): Media {
|
fun mediaDetails(media: Media): Media {
|
||||||
media.cameFromContinue = false
|
media.cameFromContinue = false
|
||||||
|
|
||||||
val query =
|
|
||||||
"""{Media(id:${media.id}){id favourites popularity episodes chapters streamingEpisodes {title thumbnail url site} mediaListEntry{id status score(format:POINT_100)progress private notes repeat customLists updatedAt startedAt{year month day}completedAt{year month day}}reviews(perPage:3, sort:SCORE_DESC){nodes{id mediaId mediaType summary body(asHtml:true) rating ratingAmount userRating score private siteUrl createdAt updatedAt user{id name bannerImage avatar{medium large}}}}isFavourite siteUrl idMal nextAiringEpisode{episode airingAt}source countryOfOrigin format duration season seasonYear startDate{year month day}endDate{year month day}genres studios(isMain:true){nodes{id name siteUrl}}description trailer{site id}synonyms tags{name rank isMediaSpoiler}characters(sort:[ROLE,FAVOURITES_DESC],perPage:25,page:1){edges{role voiceActors { id name { first middle last full native userPreferred } image { large medium } languageV2 } node{id image{medium}name{userPreferred}isFavourite}}}relations{edges{relationType(version:2)node{id idMal mediaListEntry{progress private score(format:POINT_100)status}episodes chapters nextAiringEpisode{episode}popularity meanScore isAdult isFavourite format title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}staffPreview:staff(perPage:8,sort:[RELEVANCE,ID]){edges{role node{id image{large medium}name{userPreferred}}}}recommendations(sort:RATING_DESC){nodes{mediaRecommendation{id idMal mediaListEntry{progress private score(format:POINT_100)status}episodes chapters nextAiringEpisode{episode}meanScore isAdult isFavourite format title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}externalLinks{url site}}Page(page:1){pageInfo{total perPage currentPage lastPage hasNextPage}mediaList(isFollowing:true,sort:[STATUS],mediaId:${media.id}){id status score(format: POINT_100) progress progressVolumes user{id name avatar{large medium}}}}}"""
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val anilist = async {
|
val anilist = async {
|
||||||
var response = executeQuery<Query.Media>(query, force = true)
|
var response =
|
||||||
|
executeQuery<Query.Media>(fullMediaInformation(media.id), force = true)
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
fun parse() {
|
fun parse() {
|
||||||
val fetchedMedia = response?.data?.media ?: return
|
val fetchedMedia = response?.data?.media ?: return
|
||||||
|
@ -291,7 +290,10 @@ class AnilistQueries {
|
||||||
val firstStudio = get(0)
|
val firstStudio = get(0)
|
||||||
media.anime.mainStudio = Studio(
|
media.anime.mainStudio = Studio(
|
||||||
firstStudio.id.toString(),
|
firstStudio.id.toString(),
|
||||||
firstStudio.name ?: "N/A"
|
firstStudio.name ?: "N/A",
|
||||||
|
firstStudio.isFavourite ?: false,
|
||||||
|
firstStudio.favourites ?: 0,
|
||||||
|
null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -333,7 +335,11 @@ class AnilistQueries {
|
||||||
if (response.data?.media != null) parse()
|
if (response.data?.media != null) parse()
|
||||||
else {
|
else {
|
||||||
snackString(currContext()?.getString(R.string.adult_stuff))
|
snackString(currContext()?.getString(R.string.adult_stuff))
|
||||||
response = executeQuery(query, force = true, useToken = false)
|
response = executeQuery(
|
||||||
|
fullMediaInformation(media.id),
|
||||||
|
force = true,
|
||||||
|
useToken = false
|
||||||
|
)
|
||||||
if (response?.data?.media != null) parse()
|
if (response?.data?.media != null) parse()
|
||||||
else snackString(currContext()?.getString(R.string.what_did_you_open))
|
else snackString(currContext()?.getString(R.string.what_did_you_open))
|
||||||
}
|
}
|
||||||
|
@ -400,8 +406,6 @@ class AnilistQueries {
|
||||||
return media
|
return media
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private suspend fun favMedia(anime: Boolean, id: Int? = Anilist.userid): ArrayList<Media> {
|
private suspend fun favMedia(anime: Boolean, id: Int? = Anilist.userid): ArrayList<Media> {
|
||||||
var hasNextPage = true
|
var hasNextPage = true
|
||||||
var page = 0
|
var page = 0
|
||||||
|
@ -426,8 +430,6 @@ class AnilistQueries {
|
||||||
return responseArray
|
return responseArray
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
suspend fun getUserStatus(): ArrayList<User>? {
|
suspend fun getUserStatus(): ArrayList<User>? {
|
||||||
val toShow: List<Boolean> =
|
val toShow: List<Boolean> =
|
||||||
PrefManager.getVal(PrefName.HomeLayout)
|
PrefManager.getVal(PrefName.HomeLayout)
|
||||||
|
@ -485,19 +487,23 @@ class AnilistQueries {
|
||||||
return list.toCollection(ArrayList())
|
return list.toCollection(ArrayList())
|
||||||
} else return null
|
} else return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun favMediaQuery(anime: Boolean, page: Int, id: Int? = Anilist.userid): String {
|
private fun favMediaQuery(anime: Boolean, page: Int, id: Int? = Anilist.userid): String {
|
||||||
return """User(id:${id}){id favourites{${if (anime) "anime" else "manga"}(page:$page){pageInfo{hasNextPage}edges{favouriteOrder node{id idMal isAdult mediaListEntry{ progress private score(format:POINT_100) status } chapters isFavourite format episodes nextAiringEpisode{episode}meanScore isFavourite format startDate{year month day} title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}}}"""
|
return """User(id:${id}){id favourites{${if (anime) "anime" else "manga"}(page:$page){$standardPageInformation edges{favouriteOrder node{id idMal isAdult mediaListEntry{ progress private score(format:POINT_100) status } chapters isFavourite format episodes nextAiringEpisode{episode}meanScore isFavourite format startDate{year month day} title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}}}"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun recommendationQuery(): String {
|
private fun recommendationQuery(): String {
|
||||||
return """ Page(page: 1, perPage:30) { pageInfo { total currentPage hasNextPage } recommendations(sort: RATING_DESC, onList: true) { rating userRating mediaRecommendation { id idMal isAdult mediaListEntry { progress private score(format:POINT_100) status } chapters isFavourite format episodes nextAiringEpisode {episode} popularity meanScore isFavourite format title {english romaji userPreferred } type status(version: 2) bannerImage coverImage { large } } } } """
|
return """ Page(page: 1, perPage:30) { $standardPageInformation recommendations(sort: RATING_DESC, onList: true) { rating userRating mediaRecommendation { id idMal isAdult mediaListEntry { progress private score(format:POINT_100) status } chapters isFavourite format episodes nextAiringEpisode {episode} popularity meanScore isFavourite format title {english romaji userPreferred } type status(version: 2) bannerImage coverImage { large } } } } """
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun recommendationPlannedQuery(type: String): String {
|
private fun recommendationPlannedQuery(type: String): String {
|
||||||
return """ MediaListCollection(userId: ${Anilist.userid}, type: $type, status: PLANNING${if (type == "ANIME") ", sort: MEDIA_POPULARITY_DESC" else ""} ) { lists { entries { media { id mediaListEntry { progress private score(format:POINT_100) status } idMal type isAdult popularity status(version: 2) chapters episodes nextAiringEpisode {episode} meanScore isFavourite format bannerImage coverImage{large} title { english romaji userPreferred } } } } }"""
|
return """ MediaListCollection(userId: ${Anilist.userid}, type: $type, status: PLANNING${if (type == "ANIME") ", sort: MEDIA_POPULARITY_DESC" else ""} ) { lists { entries { media { id mediaListEntry { progress private score(format:POINT_100) status } idMal type isAdult popularity status(version: 2) chapters episodes nextAiringEpisode {episode} meanScore isFavourite format bannerImage coverImage{large} title { english romaji userPreferred } } } } }"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun continueMediaQuery(type: String, status: String): String {
|
private fun continueMediaQuery(type: String, status: String): String {
|
||||||
return """ MediaListCollection(userId: ${Anilist.userid}, type: $type, status: $status , sort: UPDATED_TIME ) { lists { entries { progress private score(format:POINT_100) status media { id idMal type isAdult status chapters episodes nextAiringEpisode {episode} meanScore isFavourite format bannerImage coverImage{large} title { english romaji userPreferred } } } } } """
|
return """ MediaListCollection(userId: ${Anilist.userid}, type: $type, status: $status , sort: UPDATED_TIME ) { lists { entries { progress private score(format:POINT_100) status media { id idMal type isAdult status chapters episodes nextAiringEpisode {episode} meanScore isFavourite format bannerImage coverImage{large} title { english romaji userPreferred } } } } } """
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun initHomePage(): Map<String, ArrayList<Media>> {
|
suspend fun initHomePage(): Map<String, ArrayList<Media>> {
|
||||||
val removeList = PrefManager.getCustomVal("removeList", setOf<Int>())
|
val removeList = PrefManager.getCustomVal("removeList", setOf<Int>())
|
||||||
val hidePrivate = PrefManager.getVal<Boolean>(PrefName.HidePrivate)
|
val hidePrivate = PrefManager.getVal<Boolean>(PrefName.HidePrivate)
|
||||||
|
@ -888,7 +894,7 @@ class AnilistQueries {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun search(
|
suspend fun searchAniManga(
|
||||||
type: String,
|
type: String,
|
||||||
page: Int? = null,
|
page: Int? = null,
|
||||||
perPage: Int? = null,
|
perPage: Int? = null,
|
||||||
|
@ -910,52 +916,7 @@ class AnilistQueries {
|
||||||
id: Int? = null,
|
id: Int? = null,
|
||||||
hd: Boolean = false,
|
hd: Boolean = false,
|
||||||
adultOnly: Boolean = false
|
adultOnly: Boolean = false
|
||||||
): SearchResults? {
|
): AniMangaSearchResults? {
|
||||||
val query = """
|
|
||||||
query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult: Boolean = false, ${"$"}search: String, ${"$"}format: [MediaFormat], ${"$"}status: MediaStatus, ${"$"}countryOfOrigin: CountryCode, ${"$"}source: MediaSource, ${"$"}season: MediaSeason, ${"$"}seasonYear: Int, ${"$"}year: String, ${"$"}onList: Boolean, ${"$"}yearLesser: FuzzyDateInt, ${"$"}yearGreater: FuzzyDateInt, ${"$"}episodeLesser: Int, ${"$"}episodeGreater: Int, ${"$"}durationLesser: Int, ${"$"}durationGreater: Int, ${"$"}chapterLesser: Int, ${"$"}chapterGreater: Int, ${"$"}volumeLesser: Int, ${"$"}volumeGreater: Int, ${"$"}licensedBy: [String], ${"$"}isLicensed: Boolean, ${"$"}genres: [String], ${"$"}excludedGenres: [String], ${"$"}tags: [String], ${"$"}excludedTags: [String], ${"$"}minimumTagRank: Int, ${"$"}sort: [MediaSort] = [POPULARITY_DESC, SCORE_DESC, START_DATE_DESC]) {
|
|
||||||
Page(page: ${"$"}page, perPage: ${perPage ?: 50}) {
|
|
||||||
pageInfo {
|
|
||||||
total
|
|
||||||
perPage
|
|
||||||
currentPage
|
|
||||||
lastPage
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
media(id: ${"$"}id, type: ${"$"}type, season: ${"$"}season, format_in: ${"$"}format, status: ${"$"}status, countryOfOrigin: ${"$"}countryOfOrigin, source: ${"$"}source, search: ${"$"}search, onList: ${"$"}onList, seasonYear: ${"$"}seasonYear, startDate_like: ${"$"}year, startDate_lesser: ${"$"}yearLesser, startDate_greater: ${"$"}yearGreater, episodes_lesser: ${"$"}episodeLesser, episodes_greater: ${"$"}episodeGreater, duration_lesser: ${"$"}durationLesser, duration_greater: ${"$"}durationGreater, chapters_lesser: ${"$"}chapterLesser, chapters_greater: ${"$"}chapterGreater, volumes_lesser: ${"$"}volumeLesser, volumes_greater: ${"$"}volumeGreater, licensedBy_in: ${"$"}licensedBy, isLicensed: ${"$"}isLicensed, genre_in: ${"$"}genres, genre_not_in: ${"$"}excludedGenres, tag_in: ${"$"}tags, tag_not_in: ${"$"}excludedTags, minimumTagRank: ${"$"}minimumTagRank, sort: ${"$"}sort, isAdult: ${"$"}isAdult) {
|
|
||||||
id
|
|
||||||
idMal
|
|
||||||
isAdult
|
|
||||||
status
|
|
||||||
chapters
|
|
||||||
episodes
|
|
||||||
nextAiringEpisode {
|
|
||||||
episode
|
|
||||||
}
|
|
||||||
type
|
|
||||||
genres
|
|
||||||
meanScore
|
|
||||||
isFavourite
|
|
||||||
format
|
|
||||||
bannerImage
|
|
||||||
coverImage {
|
|
||||||
large
|
|
||||||
extraLarge
|
|
||||||
}
|
|
||||||
title {
|
|
||||||
english
|
|
||||||
romaji
|
|
||||||
userPreferred
|
|
||||||
}
|
|
||||||
mediaListEntry {
|
|
||||||
progress
|
|
||||||
private
|
|
||||||
score(format: POINT_100)
|
|
||||||
status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""".replace("\n", " ").replace(""" """, "")
|
|
||||||
val variables = """{"type":"$type","isAdult":$isAdult
|
val variables = """{"type":"$type","isAdult":$isAdult
|
||||||
${if (adultOnly) ""","isAdult":true""" else ""}
|
${if (adultOnly) ""","isAdult":true""" else ""}
|
||||||
${if (onList != null) ""","onList":$onList""" else ""}
|
${if (onList != null) ""","onList":$onList""" else ""}
|
||||||
|
@ -1000,8 +961,9 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
|
||||||
}]"""
|
}]"""
|
||||||
else ""
|
else ""
|
||||||
}
|
}
|
||||||
}""".replace("\n", " ").replace(""" """, "")
|
}""".prepare()
|
||||||
val response = executeQuery<Query.Page>(query, variables, true)?.data?.page
|
val response =
|
||||||
|
executeQuery<Query.Page>(aniMangaSearch(perPage), variables, true)?.data?.page
|
||||||
if (response?.media != null) {
|
if (response?.media != null) {
|
||||||
val responseArray = arrayListOf<Media>()
|
val responseArray = arrayListOf<Media>()
|
||||||
response.media?.forEach { i ->
|
response.media?.forEach { i ->
|
||||||
|
@ -1021,7 +983,7 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
|
||||||
|
|
||||||
val pageInfo = response.pageInfo ?: return null
|
val pageInfo = response.pageInfo ?: return null
|
||||||
|
|
||||||
return SearchResults(
|
return AniMangaSearchResults(
|
||||||
type = type,
|
type = type,
|
||||||
perPage = perPage,
|
perPage = perPage,
|
||||||
search = search,
|
search = search,
|
||||||
|
@ -1047,6 +1009,169 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun searchCharacters(page: Int, search: String?): CharacterSearchResults? {
|
||||||
|
if (search.isNullOrBlank()) return null
|
||||||
|
val query = """
|
||||||
|
{
|
||||||
|
Page(page: $page, perPage: $ITEMS_PER_PAGE) {
|
||||||
|
$standardPageInformation
|
||||||
|
characters(search: "$search") {
|
||||||
|
${characterInformation(false)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".prepare()
|
||||||
|
|
||||||
|
val response = executeQuery<Query.Page>(query, force = true)?.data?.page
|
||||||
|
|
||||||
|
if (response?.characters != null) {
|
||||||
|
val responseArray = arrayListOf<Character>()
|
||||||
|
response.characters?.forEach { i ->
|
||||||
|
responseArray.add(
|
||||||
|
Character(
|
||||||
|
i.id,
|
||||||
|
i.name?.full,
|
||||||
|
i.image?.medium ?: i.image?.large,
|
||||||
|
null,
|
||||||
|
null.toString(),
|
||||||
|
i.isFavourite ?: false,
|
||||||
|
i.description,
|
||||||
|
i.age,
|
||||||
|
i.gender,
|
||||||
|
i.dateOfBirth,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val pageInfo = response.pageInfo ?: return null
|
||||||
|
|
||||||
|
return CharacterSearchResults(
|
||||||
|
search = search,
|
||||||
|
results = responseArray,
|
||||||
|
page = pageInfo.currentPage ?: 0,
|
||||||
|
hasNextPage = pageInfo.hasNextPage == true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun searchStudios(page: Int, search: String?): StudioSearchResults? {
|
||||||
|
if (search.isNullOrBlank()) return null
|
||||||
|
val query = """
|
||||||
|
{
|
||||||
|
Page(page: $page, perPage: $ITEMS_PER_PAGE) {
|
||||||
|
$standardPageInformation
|
||||||
|
studios(search: "$search") {
|
||||||
|
${studioInformation(1, 1)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".prepare()
|
||||||
|
|
||||||
|
val response = executeQuery<Query.Page>(query, force = true)?.data?.page
|
||||||
|
|
||||||
|
if (response?.studios != null) {
|
||||||
|
val responseArray = arrayListOf<Studio>()
|
||||||
|
response.studios?.forEach { i ->
|
||||||
|
responseArray.add(
|
||||||
|
Studio(
|
||||||
|
i.id.toString(),
|
||||||
|
i.name ?: return null,
|
||||||
|
i.isFavourite ?: false,
|
||||||
|
i.favourites,
|
||||||
|
i.media?.edges?.firstOrNull()?.node?.let { it.coverImage?.large }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val pageInfo = response.pageInfo ?: return null
|
||||||
|
|
||||||
|
return StudioSearchResults(
|
||||||
|
search = search,
|
||||||
|
results = responseArray,
|
||||||
|
page = pageInfo.currentPage ?: 0,
|
||||||
|
hasNextPage = pageInfo.hasNextPage == true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun searchStaff(page: Int, search: String?): StaffSearchResults? {
|
||||||
|
if (search.isNullOrBlank()) return null
|
||||||
|
val query = """
|
||||||
|
{
|
||||||
|
Page(page: $page, perPage: $ITEMS_PER_PAGE) {
|
||||||
|
$standardPageInformation
|
||||||
|
staff(search: "$search") {
|
||||||
|
${staffInformation(1, 1)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".prepare()
|
||||||
|
|
||||||
|
val response = executeQuery<Query.Page>(query, force = true)?.data?.page
|
||||||
|
|
||||||
|
if (response?.staff != null) {
|
||||||
|
val responseArray = arrayListOf<Author>()
|
||||||
|
response.staff?.forEach { i ->
|
||||||
|
responseArray.add(
|
||||||
|
Author(
|
||||||
|
i.id,
|
||||||
|
i.name?.userPreferred ?: return null,
|
||||||
|
i.image?.large,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val pageInfo = response.pageInfo ?: return null
|
||||||
|
|
||||||
|
return StaffSearchResults(
|
||||||
|
search = search,
|
||||||
|
results = responseArray,
|
||||||
|
page = pageInfo.currentPage ?: 0,
|
||||||
|
hasNextPage = pageInfo.hasNextPage == true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun searchUsers(page: Int, search: String?): UserSearchResults? {
|
||||||
|
val query = """
|
||||||
|
{
|
||||||
|
Page(page: $page, perPage: $ITEMS_PER_PAGE) {
|
||||||
|
$standardPageInformation
|
||||||
|
users(search: "$search") {
|
||||||
|
${userInformation()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".prepare()
|
||||||
|
|
||||||
|
val response = executeQuery<Query.Page>(query, force = true)?.data?.page
|
||||||
|
|
||||||
|
if (response?.users != null) {
|
||||||
|
val users = response.users?.map { user ->
|
||||||
|
User(
|
||||||
|
user.id,
|
||||||
|
user.name ?: return null,
|
||||||
|
user.avatar?.medium,
|
||||||
|
user.bannerImage
|
||||||
|
)
|
||||||
|
} ?: return null
|
||||||
|
|
||||||
|
return UserSearchResults(
|
||||||
|
search = search,
|
||||||
|
results = users.toMutableList(),
|
||||||
|
page = response.pageInfo?.currentPage ?: 0,
|
||||||
|
hasNextPage = response.pageInfo?.hasNextPage == true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
private fun mediaList(media1: Page?): ArrayList<Media> {
|
private fun mediaList(media1: Page?): ArrayList<Media> {
|
||||||
val combinedList = arrayListOf<Media>()
|
val combinedList = arrayListOf<Media>()
|
||||||
media1?.media?.mapTo(combinedList) { Media(it) }
|
media1?.media?.mapTo(combinedList) { Media(it) }
|
||||||
|
@ -1071,26 +1196,65 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
|
||||||
val countryFilter = country?.let { "countryOfOrigin:$it, " } ?: ""
|
val countryFilter = country?.let { "countryOfOrigin:$it, " } ?: ""
|
||||||
|
|
||||||
return buildString {
|
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}}}""")
|
append("""Page(page:1,perPage:50){$standardPageInformation 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 {
|
private fun recentAnimeUpdates(page: Int): String {
|
||||||
val currentTime = System.currentTimeMillis() / 1000
|
val currentTime = System.currentTimeMillis() / 1000
|
||||||
return buildString {
|
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}}}}""")
|
append("""Page(page:$page,perPage:50){$standardPageInformation 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 {
|
private fun queryAnimeList(): String {
|
||||||
return buildString {
|
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")}}""")
|
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 {
|
private fun queryMangaList(): String {
|
||||||
return buildString {
|
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")}}""")
|
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")}}"""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1145,7 +1309,6 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
|
||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
suspend fun recentlyUpdated(
|
suspend fun recentlyUpdated(
|
||||||
greater: Long = 0,
|
greater: Long = 0,
|
||||||
lesser: Long = System.currentTimeMillis() / 1000 - 10000
|
lesser: Long = System.currentTimeMillis() / 1000 - 10000
|
||||||
|
@ -1153,10 +1316,7 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
|
||||||
suspend fun execute(page: Int = 1): Page? {
|
suspend fun execute(page: Int = 1): Page? {
|
||||||
val query = """{
|
val query = """{
|
||||||
Page(page:$page,perPage:50) {
|
Page(page:$page,perPage:50) {
|
||||||
pageInfo {
|
$standardPageInformation
|
||||||
hasNextPage
|
|
||||||
total
|
|
||||||
}
|
|
||||||
airingSchedules(
|
airingSchedules(
|
||||||
airingAt_greater: $greater
|
airingAt_greater: $greater
|
||||||
airingAt_lesser: $lesser
|
airingAt_lesser: $lesser
|
||||||
|
@ -1165,35 +1325,11 @@ Page(page:$page,perPage:50) {
|
||||||
episode
|
episode
|
||||||
airingAt
|
airingAt
|
||||||
media {
|
media {
|
||||||
id
|
${standardMediaInformation()}
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}""".replace("\n", " ").replace(""" """, "")
|
}""".prepare()
|
||||||
return executeQuery<Query.Page>(query, force = true)?.data?.page
|
return executeQuery<Query.Page>(query, force = true)?.data?.page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1221,68 +1357,37 @@ Page(page:$page,perPage:50) {
|
||||||
suspend fun getCharacterDetails(character: Character): Character {
|
suspend fun getCharacterDetails(character: Character): Character {
|
||||||
val query = """ {
|
val query = """ {
|
||||||
Character(id: ${character.id}) {
|
Character(id: ${character.id}) {
|
||||||
id
|
${characterInformation(true)}
|
||||||
age
|
|
||||||
gender
|
|
||||||
description
|
|
||||||
dateOfBirth {
|
|
||||||
year
|
|
||||||
month
|
|
||||||
day
|
|
||||||
}
|
|
||||||
media(page: 0,sort:[POPULARITY_DESC,SCORE_DESC]) {
|
|
||||||
pageInfo {
|
|
||||||
total
|
|
||||||
perPage
|
|
||||||
currentPage
|
|
||||||
lastPage
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
id
|
|
||||||
characterRole
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
idMal
|
|
||||||
isAdult
|
|
||||||
status
|
|
||||||
chapters
|
|
||||||
episodes
|
|
||||||
nextAiringEpisode { episode }
|
|
||||||
type
|
|
||||||
meanScore
|
|
||||||
isFavourite
|
|
||||||
format
|
|
||||||
bannerImage
|
|
||||||
countryOfOrigin
|
|
||||||
coverImage { large }
|
|
||||||
title {
|
|
||||||
english
|
|
||||||
romaji
|
|
||||||
userPreferred
|
|
||||||
}
|
|
||||||
mediaListEntry {
|
|
||||||
progress
|
|
||||||
private
|
|
||||||
score(format: POINT_100)
|
|
||||||
status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}""".replace("\n", " ").replace(""" """, "")
|
}""".prepare()
|
||||||
executeQuery<Query.Character>(query, force = true)?.data?.character?.apply {
|
executeQuery<Query.Character>(query, force = true)?.data?.character?.let { i ->
|
||||||
character.age = age
|
return Character(
|
||||||
character.gender = gender
|
i.id,
|
||||||
character.description = description
|
i.name?.full,
|
||||||
character.dateOfBirth = dateOfBirth
|
i.image?.large ?: i.image?.medium,
|
||||||
character.roles = arrayListOf()
|
null,
|
||||||
media?.edges?.forEach { i ->
|
null.toString(),
|
||||||
val m = Media(i)
|
i.isFavourite ?: false,
|
||||||
m.relation = i.characterRole.toString()
|
i.description,
|
||||||
character.roles?.add(m)
|
i.age,
|
||||||
}
|
i.gender,
|
||||||
|
i.dateOfBirth,
|
||||||
|
i.media?.edges?.map {
|
||||||
|
val m = Media(it)
|
||||||
|
m.relation = it.characterRole.toString()
|
||||||
|
m
|
||||||
|
}?.let { ArrayList(it) },
|
||||||
|
i.media?.edges?.flatMap { edge ->
|
||||||
|
edge.voiceActors?.map { va ->
|
||||||
|
Author(
|
||||||
|
va.id,
|
||||||
|
va.name?.userPreferred,
|
||||||
|
va.image?.large ?: va.image?.medium,
|
||||||
|
va.languageV2
|
||||||
|
)
|
||||||
|
} ?: emptyList()
|
||||||
|
} as ArrayList<Author>?
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return character
|
return character
|
||||||
}
|
}
|
||||||
|
@ -1290,45 +1395,9 @@ Page(page:$page,perPage:50) {
|
||||||
suspend fun getStudioDetails(studio: Studio): Studio {
|
suspend fun getStudioDetails(studio: Studio): Studio {
|
||||||
fun query(page: Int = 0) = """ {
|
fun query(page: Int = 0) = """ {
|
||||||
Studio(id: ${studio.id}) {
|
Studio(id: ${studio.id}) {
|
||||||
id
|
${studioInformation(page, ITEMS_PER_PAGE)}
|
||||||
media(page: $page,sort:START_DATE_DESC) {
|
|
||||||
pageInfo{
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
id
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
idMal
|
|
||||||
isAdult
|
|
||||||
status
|
|
||||||
chapters
|
|
||||||
episodes
|
|
||||||
nextAiringEpisode { episode }
|
|
||||||
type
|
|
||||||
meanScore
|
|
||||||
startDate{ year }
|
|
||||||
isFavourite
|
|
||||||
format
|
|
||||||
bannerImage
|
|
||||||
countryOfOrigin
|
|
||||||
coverImage { large }
|
|
||||||
title {
|
|
||||||
english
|
|
||||||
romaji
|
|
||||||
userPreferred
|
|
||||||
}
|
|
||||||
mediaListEntry {
|
|
||||||
progress
|
|
||||||
private
|
|
||||||
score(format: POINT_100)
|
|
||||||
status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}""".replace("\n", " ").replace(""" """, "")
|
}""".prepare()
|
||||||
|
|
||||||
var hasNextPage = true
|
var hasNextPage = true
|
||||||
val yearMedia = mutableMapOf<String, ArrayList<Media>>()
|
val yearMedia = mutableMapOf<String, ArrayList<Media>>()
|
||||||
|
@ -1363,66 +1432,15 @@ Page(page:$page,perPage:50) {
|
||||||
suspend fun getAuthorDetails(author: Author): Author {
|
suspend fun getAuthorDetails(author: Author): Author {
|
||||||
fun query(page: Int = 0) = """ {
|
fun query(page: Int = 0) = """ {
|
||||||
Staff(id: ${author.id}) {
|
Staff(id: ${author.id}) {
|
||||||
id
|
${staffInformation(page, ITEMS_PER_PAGE)}
|
||||||
staffMedia(page: $page,sort:START_DATE_DESC) {
|
|
||||||
pageInfo{
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
staffRole
|
|
||||||
id
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
idMal
|
|
||||||
isAdult
|
|
||||||
status
|
|
||||||
chapters
|
|
||||||
episodes
|
|
||||||
nextAiringEpisode { episode }
|
|
||||||
type
|
|
||||||
meanScore
|
|
||||||
startDate{ year }
|
|
||||||
isFavourite
|
|
||||||
format
|
|
||||||
bannerImage
|
|
||||||
countryOfOrigin
|
|
||||||
coverImage { large }
|
|
||||||
title {
|
|
||||||
english
|
|
||||||
romaji
|
|
||||||
userPreferred
|
|
||||||
}
|
|
||||||
mediaListEntry {
|
|
||||||
progress
|
|
||||||
private
|
|
||||||
score(format: POINT_100)
|
|
||||||
status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
characters(page: $page,sort:FAVOURITES_DESC) {
|
characters(page: $page,sort:FAVOURITES_DESC) {
|
||||||
pageInfo{
|
$standardPageInformation
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
nodes{
|
nodes{
|
||||||
id
|
${characterInformation(false)}
|
||||||
name {
|
|
||||||
first
|
|
||||||
middle
|
|
||||||
last
|
|
||||||
full
|
|
||||||
native
|
|
||||||
userPreferred
|
|
||||||
}
|
|
||||||
image {
|
|
||||||
large
|
|
||||||
medium
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}""".replace("\n", " ").replace(""" """, "")
|
}""".prepare()
|
||||||
|
|
||||||
var hasNextPage = true
|
var hasNextPage = true
|
||||||
val yearMedia = mutableMapOf<String, ArrayList<Media>>()
|
val yearMedia = mutableMapOf<String, ArrayList<Media>>()
|
||||||
|
@ -1433,6 +1451,16 @@ Page(page:$page,perPage:50) {
|
||||||
val query = executeQuery<Query.Author>(
|
val query = executeQuery<Query.Author>(
|
||||||
query(page), force = true
|
query(page), force = true
|
||||||
)?.data?.author
|
)?.data?.author
|
||||||
|
author.age = query?.age
|
||||||
|
author.yearsActive =
|
||||||
|
if (query?.yearsActive?.isEmpty() == true) null else query?.yearsActive
|
||||||
|
author.homeTown = if (query?.homeTown?.isBlank() == true) null else query?.homeTown
|
||||||
|
author.dateOfDeath = if (query?.dateOfDeath?.toStringOrEmpty()
|
||||||
|
?.isBlank() == true
|
||||||
|
) null else query?.dateOfDeath?.toStringOrEmpty()
|
||||||
|
author.dateOfBirth = if (query?.dateOfBirth?.toStringOrEmpty()
|
||||||
|
?.isBlank() == true
|
||||||
|
) null else query?.dateOfBirth?.toStringOrEmpty()
|
||||||
hasNextPage = query?.staffMedia?.let {
|
hasNextPage = query?.staffMedia?.let {
|
||||||
it.edges?.forEach { i ->
|
it.edges?.forEach { i ->
|
||||||
i.node?.apply {
|
i.node?.apply {
|
||||||
|
@ -1480,14 +1508,14 @@ Page(page:$page,perPage:50) {
|
||||||
sort: String = "SCORE_DESC"
|
sort: String = "SCORE_DESC"
|
||||||
): Query.ReviewsResponse? {
|
): Query.ReviewsResponse? {
|
||||||
return executeQuery<Query.ReviewsResponse>(
|
return executeQuery<Query.ReviewsResponse>(
|
||||||
"""{Page(page:$page,perPage:10){pageInfo{currentPage,hasNextPage,total}reviews(mediaId:$mediaId,sort:$sort){id,mediaId,mediaType,summary,body(asHtml:true)rating,ratingAmount,userRating,score,private,siteUrl,createdAt,updatedAt,user{id,name,bannerImage avatar{medium,large}}}}}""",
|
"""{Page(page:$page,perPage:10){$standardPageInformation reviews(mediaId:$mediaId,sort:$sort){id,mediaId,mediaType,summary,body(asHtml:true)rating,ratingAmount,userRating,score,private,siteUrl,createdAt,updatedAt,user{id,name,bannerImage avatar{medium,large}}}}}""",
|
||||||
force = true
|
force = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getUserProfile(id: Int): Query.UserProfileResponse? {
|
suspend fun getUserProfile(id: Int): Query.UserProfileResponse? {
|
||||||
return executeQuery<Query.UserProfileResponse>(
|
return executeQuery<Query.UserProfileResponse>(
|
||||||
"""{followerPage:Page{followers(userId:$id){id}pageInfo{total}}followingPage:Page{following(userId:$id){id}pageInfo{total}}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 name{first middle last full native alternative userPreferred}image{large medium}isFavourite}}staff{nodes{id name{first middle last full native alternative userPreferred}image{large medium}isFavourite}}studios{nodes{id name isFavourite}}}statistics{anime{count meanScore standardDeviation minutesWatched episodesWatched chaptersRead volumesRead}manga{count meanScore standardDeviation minutesWatched episodesWatched chaptersRead volumesRead}}siteUrl}}""",
|
"""{followerPage:Page{followers(userId:$id){id}$standardPageInformation}followingPage:Page{following(userId:$id){id}$standardPageInformation}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 name{first middle last full native alternative userPreferred}image{large medium}isFavourite}}staff{nodes{id name{first middle last full native alternative userPreferred}image{large medium}isFavourite}}studios{nodes{id name isFavourite}}}statistics{anime{count meanScore standardDeviation minutesWatched episodesWatched chaptersRead volumesRead}manga{count meanScore standardDeviation minutesWatched episodesWatched chaptersRead volumesRead}}siteUrl}}""",
|
||||||
force = true
|
force = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1513,7 +1541,7 @@ Page(page:$page,perPage:50) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun userFavMediaQuery(anime: Boolean, id: Int): String {
|
private fun userFavMediaQuery(anime: Boolean, id: Int): String {
|
||||||
return """User(id:${id}){id favourites{${if (anime) "anime" else "manga"}(page:1){pageInfo{hasNextPage}edges{favouriteOrder node{id idMal isAdult mediaListEntry{ progress private score(format:POINT_100) status } chapters isFavourite format episodes nextAiringEpisode{episode}meanScore isFavourite format startDate{year month day} title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}}}"""
|
return """User(id:${id}){id favourites{${if (anime) "anime" else "manga"}(page:1){$standardPageInformation edges{favouriteOrder node{id idMal isAdult mediaListEntry{ progress private score(format:POINT_100) status } chapters isFavourite format episodes nextAiringEpisode{episode}meanScore isFavourite format startDate{year month day} title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}}}"""
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun userFollowing(id: Int): Query.Following? {
|
suspend fun userFollowing(id: Int): Query.Following? {
|
||||||
|
@ -1535,7 +1563,7 @@ Page(page:$page,perPage:50) {
|
||||||
"""{
|
"""{
|
||||||
favoriteAnime:${userFavMediaQuery(true, id)}
|
favoriteAnime:${userFavMediaQuery(true, id)}
|
||||||
favoriteManga:${userFavMediaQuery(false, id)}
|
favoriteManga:${userFavMediaQuery(false, id)}
|
||||||
}""".trimIndent(), force = true
|
}""".prepare(), force = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1549,7 +1577,7 @@ Page(page:$page,perPage:50) {
|
||||||
val typeIn = "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 reset = if (resetNotification) "true" else "false"
|
||||||
val res = executeQuery<NotificationResponse>(
|
val res = executeQuery<NotificationResponse>(
|
||||||
"""{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,}}}}""",
|
"""{User(id:$id){unreadNotificationCount}Page(page:$page,perPage:$ITEMS_PER_PAGE){$standardPageInformation 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
|
force = true
|
||||||
)
|
)
|
||||||
if (res != null && resetNotification) {
|
if (res != null && resetNotification) {
|
||||||
|
@ -1571,7 +1599,8 @@ Page(page:$page,perPage:50) {
|
||||||
else if (userId != null) "userId:$userId,"
|
else if (userId != null) "userId:$userId,"
|
||||||
else if (global) "isFollowing:false,hasRepliesOrTypeText:true,"
|
else if (global) "isFollowing:false,hasRepliesOrTypeText:true,"
|
||||||
else "isFollowing:true,"
|
else "isFollowing:true,"
|
||||||
val typeIn = if (filter == "isFollowing:true,") "type_in:[TEXT,ANIME_LIST,MANGA_LIST,MEDIA_LIST]," else ""
|
val typeIn =
|
||||||
|
if (filter == "isFollowing:true,") "type_in:[TEXT,ANIME_LIST,MANGA_LIST,MEDIA_LIST]," else ""
|
||||||
return executeQuery<FeedResponse>(
|
return executeQuery<FeedResponse>(
|
||||||
"""{Page(page:$page,perPage:$ITEMS_PER_PAGE){activities(${filter}${typeIn}sort:ID_DESC){__typename ... on TextActivity{id userId type replyCount text(asHtml:true)siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on ListActivity{id userId type replyCount status progress siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}media{id title{english romaji native userPreferred}bannerImage coverImage{medium large}isAdult}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on MessageActivity{id recipientId messengerId type replyCount likeCount message(asHtml:true)isLocked isSubscribed isLiked isPrivate siteUrl createdAt recipient{id name bannerImage avatar{medium large}}messenger{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}}}}""",
|
"""{Page(page:$page,perPage:$ITEMS_PER_PAGE){activities(${filter}${typeIn}sort:ID_DESC){__typename ... on TextActivity{id userId type replyCount text(asHtml:true)siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on ListActivity{id userId type replyCount status progress siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}media{id title{english romaji native userPreferred}bannerImage coverImage{medium large}isAdult}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on MessageActivity{id recipientId messengerId type replyCount likeCount message(asHtml:true)isLocked isSubscribed isLiked isPrivate siteUrl createdAt recipient{id name bannerImage avatar{medium large}}messenger{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}}}}""",
|
||||||
force = true
|
force = true
|
||||||
|
|
|
@ -128,7 +128,7 @@ class AnilistHomeViewModel : ViewModel() {
|
||||||
class AnilistAnimeViewModel : ViewModel() {
|
class AnilistAnimeViewModel : ViewModel() {
|
||||||
var searched = false
|
var searched = false
|
||||||
var notSet = true
|
var notSet = true
|
||||||
lateinit var searchResults: SearchResults
|
lateinit var aniMangaSearchResults: AniMangaSearchResults
|
||||||
private val type = "ANIME"
|
private val type = "ANIME"
|
||||||
private val trending: MutableLiveData<MutableList<Media>> =
|
private val trending: MutableLiveData<MutableList<Media>> =
|
||||||
MutableLiveData<MutableList<Media>>(null)
|
MutableLiveData<MutableList<Media>>(null)
|
||||||
|
@ -137,7 +137,7 @@ class AnilistAnimeViewModel : ViewModel() {
|
||||||
suspend fun loadTrending(i: Int) {
|
suspend fun loadTrending(i: Int) {
|
||||||
val (season, year) = Anilist.currentSeasons[i]
|
val (season, year) = Anilist.currentSeasons[i]
|
||||||
trending.postValue(
|
trending.postValue(
|
||||||
Anilist.query.search(
|
Anilist.query.searchAniManga(
|
||||||
type,
|
type,
|
||||||
perPage = 12,
|
perPage = 12,
|
||||||
sort = Anilist.sortBy[2],
|
sort = Anilist.sortBy[2],
|
||||||
|
@ -150,9 +150,9 @@ class AnilistAnimeViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private val animePopular = MutableLiveData<SearchResults?>(null)
|
private val animePopular = MutableLiveData<AniMangaSearchResults?>(null)
|
||||||
|
|
||||||
fun getPopular(): LiveData<SearchResults?> = animePopular
|
fun getPopular(): LiveData<AniMangaSearchResults?> = animePopular
|
||||||
suspend fun loadPopular(
|
suspend fun loadPopular(
|
||||||
type: String,
|
type: String,
|
||||||
searchVal: String? = null,
|
searchVal: String? = null,
|
||||||
|
@ -161,7 +161,7 @@ class AnilistAnimeViewModel : ViewModel() {
|
||||||
onList: Boolean = true,
|
onList: Boolean = true,
|
||||||
) {
|
) {
|
||||||
animePopular.postValue(
|
animePopular.postValue(
|
||||||
Anilist.query.search(
|
Anilist.query.searchAniManga(
|
||||||
type,
|
type,
|
||||||
search = searchVal,
|
search = searchVal,
|
||||||
onList = if (onList) null else false,
|
onList = if (onList) null else false,
|
||||||
|
@ -173,8 +173,8 @@ class AnilistAnimeViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
suspend fun loadNextPage(r: SearchResults) = animePopular.postValue(
|
suspend fun loadNextPage(r: AniMangaSearchResults) = animePopular.postValue(
|
||||||
Anilist.query.search(
|
Anilist.query.searchAniManga(
|
||||||
r.type,
|
r.type,
|
||||||
r.page + 1,
|
r.page + 1,
|
||||||
r.perPage,
|
r.perPage,
|
||||||
|
@ -224,7 +224,7 @@ class AnilistAnimeViewModel : ViewModel() {
|
||||||
class AnilistMangaViewModel : ViewModel() {
|
class AnilistMangaViewModel : ViewModel() {
|
||||||
var searched = false
|
var searched = false
|
||||||
var notSet = true
|
var notSet = true
|
||||||
lateinit var searchResults: SearchResults
|
lateinit var aniMangaSearchResults: AniMangaSearchResults
|
||||||
private val type = "MANGA"
|
private val type = "MANGA"
|
||||||
private val trending: MutableLiveData<MutableList<Media>> =
|
private val trending: MutableLiveData<MutableList<Media>> =
|
||||||
MutableLiveData<MutableList<Media>>(null)
|
MutableLiveData<MutableList<Media>>(null)
|
||||||
|
@ -232,7 +232,7 @@ class AnilistMangaViewModel : ViewModel() {
|
||||||
fun getTrending(): LiveData<MutableList<Media>> = trending
|
fun getTrending(): LiveData<MutableList<Media>> = trending
|
||||||
suspend fun loadTrending() =
|
suspend fun loadTrending() =
|
||||||
trending.postValue(
|
trending.postValue(
|
||||||
Anilist.query.search(
|
Anilist.query.searchAniManga(
|
||||||
type,
|
type,
|
||||||
perPage = 10,
|
perPage = 10,
|
||||||
sort = Anilist.sortBy[2],
|
sort = Anilist.sortBy[2],
|
||||||
|
@ -242,8 +242,8 @@ class AnilistMangaViewModel : ViewModel() {
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
private val mangaPopular = MutableLiveData<SearchResults?>(null)
|
private val mangaPopular = MutableLiveData<AniMangaSearchResults?>(null)
|
||||||
fun getPopular(): LiveData<SearchResults?> = mangaPopular
|
fun getPopular(): LiveData<AniMangaSearchResults?> = mangaPopular
|
||||||
suspend fun loadPopular(
|
suspend fun loadPopular(
|
||||||
type: String,
|
type: String,
|
||||||
searchVal: String? = null,
|
searchVal: String? = null,
|
||||||
|
@ -252,7 +252,7 @@ class AnilistMangaViewModel : ViewModel() {
|
||||||
onList: Boolean = true,
|
onList: Boolean = true,
|
||||||
) {
|
) {
|
||||||
mangaPopular.postValue(
|
mangaPopular.postValue(
|
||||||
Anilist.query.search(
|
Anilist.query.searchAniManga(
|
||||||
type,
|
type,
|
||||||
search = searchVal,
|
search = searchVal,
|
||||||
onList = if (onList) null else false,
|
onList = if (onList) null else false,
|
||||||
|
@ -264,8 +264,8 @@ class AnilistMangaViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
suspend fun loadNextPage(r: SearchResults) = mangaPopular.postValue(
|
suspend fun loadNextPage(r: AniMangaSearchResults) = mangaPopular.postValue(
|
||||||
Anilist.query.search(
|
Anilist.query.searchAniManga(
|
||||||
r.type,
|
r.type,
|
||||||
r.page + 1,
|
r.page + 1,
|
||||||
r.perPage,
|
r.perPage,
|
||||||
|
@ -325,14 +325,126 @@ class AnilistMangaViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AnilistSearch : ViewModel() {
|
class AnilistSearch : ViewModel() {
|
||||||
|
|
||||||
|
enum class SearchType {
|
||||||
|
ANIME, MANGA, CHARACTER, STAFF, STUDIO, USER;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun SearchType.toAnilistString(): String {
|
||||||
|
return when (this) {
|
||||||
|
ANIME -> "ANIME"
|
||||||
|
MANGA -> "MANGA"
|
||||||
|
CHARACTER -> "CHARACTER"
|
||||||
|
STAFF -> "STAFF"
|
||||||
|
STUDIO -> "STUDIO"
|
||||||
|
USER -> "USER"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromString(string: String): SearchType {
|
||||||
|
return when (string.uppercase()) {
|
||||||
|
"ANIME" -> ANIME
|
||||||
|
"MANGA" -> MANGA
|
||||||
|
"CHARACTER" -> CHARACTER
|
||||||
|
"STAFF" -> STAFF
|
||||||
|
"STUDIO" -> STUDIO
|
||||||
|
"USER" -> USER
|
||||||
|
else -> throw IllegalArgumentException("Invalid search type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var searched = false
|
var searched = false
|
||||||
var notSet = true
|
var notSet = true
|
||||||
lateinit var searchResults: SearchResults
|
lateinit var aniMangaSearchResults: AniMangaSearchResults
|
||||||
private val result: MutableLiveData<SearchResults?> = MutableLiveData<SearchResults?>(null)
|
private val aniMangaResult: MutableLiveData<AniMangaSearchResults?> = MutableLiveData<AniMangaSearchResults?>(null)
|
||||||
|
|
||||||
fun getSearch(): LiveData<SearchResults?> = result
|
lateinit var characterSearchResults: CharacterSearchResults
|
||||||
suspend fun loadSearch(r: SearchResults) = result.postValue(
|
private val characterResult: MutableLiveData<CharacterSearchResults?> = MutableLiveData<CharacterSearchResults?>(null)
|
||||||
Anilist.query.search(
|
|
||||||
|
lateinit var studioSearchResults: StudioSearchResults
|
||||||
|
private val studioResult: MutableLiveData<StudioSearchResults?> = MutableLiveData<StudioSearchResults?>(null)
|
||||||
|
|
||||||
|
lateinit var staffSearchResults: StaffSearchResults
|
||||||
|
private val staffResult: MutableLiveData<StaffSearchResults?> = MutableLiveData<StaffSearchResults?>(null)
|
||||||
|
|
||||||
|
lateinit var userSearchResults: UserSearchResults
|
||||||
|
private val userResult: MutableLiveData<UserSearchResults?> = MutableLiveData<UserSearchResults?>(null)
|
||||||
|
|
||||||
|
fun <T> getSearch(type: SearchType): MutableLiveData<T?> {
|
||||||
|
return when (type) {
|
||||||
|
SearchType.ANIME, SearchType.MANGA -> aniMangaResult as MutableLiveData<T?>
|
||||||
|
SearchType.CHARACTER -> characterResult as MutableLiveData<T?>
|
||||||
|
SearchType.STUDIO -> studioResult as MutableLiveData<T?>
|
||||||
|
SearchType.STAFF -> staffResult as MutableLiveData<T?>
|
||||||
|
SearchType.USER -> userResult as MutableLiveData<T?>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun loadSearch(type: SearchType) {
|
||||||
|
when (type) {
|
||||||
|
SearchType.ANIME, SearchType.MANGA -> loadAniMangaSearch(aniMangaSearchResults)
|
||||||
|
SearchType.CHARACTER -> loadCharacterSearch(characterSearchResults)
|
||||||
|
SearchType.STUDIO -> loadStudiosSearch(studioSearchResults)
|
||||||
|
SearchType.STAFF -> loadStaffSearch(staffSearchResults)
|
||||||
|
SearchType.USER -> loadUserSearch(userSearchResults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun loadNextPage(type: SearchType) {
|
||||||
|
when (type) {
|
||||||
|
SearchType.ANIME, SearchType.MANGA -> loadNextAniMangaPage(aniMangaSearchResults)
|
||||||
|
SearchType.CHARACTER -> loadNextCharacterPage(characterSearchResults)
|
||||||
|
SearchType.STUDIO -> loadNextStudiosPage(studioSearchResults)
|
||||||
|
SearchType.STAFF -> loadNextStaffPage(staffSearchResults)
|
||||||
|
SearchType.USER -> loadNextUserPage(userSearchResults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasNextPage(type: SearchType): Boolean {
|
||||||
|
return when (type) {
|
||||||
|
SearchType.ANIME, SearchType.MANGA -> aniMangaSearchResults.hasNextPage
|
||||||
|
SearchType.CHARACTER -> characterSearchResults.hasNextPage
|
||||||
|
SearchType.STUDIO -> studioSearchResults.hasNextPage
|
||||||
|
SearchType.STAFF -> staffSearchResults.hasNextPage
|
||||||
|
SearchType.USER -> userSearchResults.hasNextPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resultsIsNotEmpty(type: SearchType): Boolean {
|
||||||
|
return when (type) {
|
||||||
|
SearchType.ANIME, SearchType.MANGA -> aniMangaSearchResults.results.isNotEmpty()
|
||||||
|
SearchType.CHARACTER -> characterSearchResults.results.isNotEmpty()
|
||||||
|
SearchType.STUDIO -> studioSearchResults.results.isNotEmpty()
|
||||||
|
SearchType.STAFF -> staffSearchResults.results.isNotEmpty()
|
||||||
|
SearchType.USER -> userSearchResults.results.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun size(type: SearchType): Int {
|
||||||
|
return when (type) {
|
||||||
|
SearchType.ANIME, SearchType.MANGA -> aniMangaSearchResults.results.size
|
||||||
|
SearchType.CHARACTER -> characterSearchResults.results.size
|
||||||
|
SearchType.STUDIO -> studioSearchResults.results.size
|
||||||
|
SearchType.STAFF -> staffSearchResults.results.size
|
||||||
|
SearchType.USER -> userSearchResults.results.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearResults(type: SearchType) {
|
||||||
|
when (type) {
|
||||||
|
SearchType.ANIME, SearchType.MANGA -> aniMangaSearchResults.results.clear()
|
||||||
|
SearchType.CHARACTER -> characterSearchResults.results.clear()
|
||||||
|
SearchType.STUDIO -> studioSearchResults.results.clear()
|
||||||
|
SearchType.STAFF -> staffSearchResults.results.clear()
|
||||||
|
SearchType.USER -> userSearchResults.results.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun loadAniMangaSearch(r: AniMangaSearchResults) = aniMangaResult.postValue(
|
||||||
|
Anilist.query.searchAniManga(
|
||||||
r.type,
|
r.type,
|
||||||
r.page,
|
r.page,
|
||||||
r.perPage,
|
r.perPage,
|
||||||
|
@ -354,8 +466,36 @@ class AnilistSearch : ViewModel() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun loadNextPage(r: SearchResults) = result.postValue(
|
private suspend fun loadCharacterSearch(r: CharacterSearchResults) = characterResult.postValue(
|
||||||
Anilist.query.search(
|
Anilist.query.searchCharacters(
|
||||||
|
r.page,
|
||||||
|
r.search,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private suspend fun loadStudiosSearch(r: StudioSearchResults) = studioResult.postValue(
|
||||||
|
Anilist.query.searchStudios(
|
||||||
|
r.page,
|
||||||
|
r.search,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private suspend fun loadStaffSearch(r: StaffSearchResults) = staffResult.postValue(
|
||||||
|
Anilist.query.searchStaff(
|
||||||
|
r.page,
|
||||||
|
r.search,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private suspend fun loadUserSearch(r: UserSearchResults) = userResult.postValue(
|
||||||
|
Anilist.query.searchUsers(
|
||||||
|
r.page,
|
||||||
|
r.search,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private suspend fun loadNextAniMangaPage(r: AniMangaSearchResults) = aniMangaResult.postValue(
|
||||||
|
Anilist.query.searchAniManga(
|
||||||
r.type,
|
r.type,
|
||||||
r.page + 1,
|
r.page + 1,
|
||||||
r.perPage,
|
r.perPage,
|
||||||
|
@ -376,6 +516,34 @@ class AnilistSearch : ViewModel() {
|
||||||
r.season
|
r.season
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private suspend fun loadNextCharacterPage(r: CharacterSearchResults) = characterResult.postValue(
|
||||||
|
Anilist.query.searchCharacters(
|
||||||
|
r.page + 1,
|
||||||
|
r.search,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private suspend fun loadNextStudiosPage(r: StudioSearchResults) = studioResult.postValue(
|
||||||
|
Anilist.query.searchStudios(
|
||||||
|
r.page + 1,
|
||||||
|
r.search,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private suspend fun loadNextStaffPage(r: StaffSearchResults) = staffResult.postValue(
|
||||||
|
Anilist.query.searchStaff(
|
||||||
|
r.page + 1,
|
||||||
|
r.search,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private suspend fun loadNextUserPage(r: UserSearchResults) = userResult.postValue(
|
||||||
|
Anilist.query.searchUsers(
|
||||||
|
r.page + 1,
|
||||||
|
r.search,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class GenresViewModel : ViewModel() {
|
class GenresViewModel : ViewModel() {
|
||||||
|
|
|
@ -0,0 +1,429 @@
|
||||||
|
package ani.dantotsu.connections.anilist
|
||||||
|
|
||||||
|
val standardPageInformation = """
|
||||||
|
pageInfo {
|
||||||
|
total
|
||||||
|
perPage
|
||||||
|
currentPage
|
||||||
|
lastPage
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
""".prepare()
|
||||||
|
|
||||||
|
fun String.prepare() = this.trimIndent().replace("\n", " ").replace(""" """, "")
|
||||||
|
|
||||||
|
fun characterInformation(includeMediaInfo: Boolean) = """
|
||||||
|
id
|
||||||
|
name {
|
||||||
|
first
|
||||||
|
middle
|
||||||
|
last
|
||||||
|
full
|
||||||
|
native
|
||||||
|
userPreferred
|
||||||
|
}
|
||||||
|
image {
|
||||||
|
large
|
||||||
|
medium
|
||||||
|
}
|
||||||
|
age
|
||||||
|
gender
|
||||||
|
description(asHtml: true)
|
||||||
|
dateOfBirth {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
${if (includeMediaInfo) """
|
||||||
|
media(page: 0,sort:[POPULARITY_DESC,SCORE_DESC]) {
|
||||||
|
$standardPageInformation
|
||||||
|
edges {
|
||||||
|
id
|
||||||
|
voiceActors {
|
||||||
|
id,
|
||||||
|
name {
|
||||||
|
userPreferred
|
||||||
|
}
|
||||||
|
languageV2,
|
||||||
|
image {
|
||||||
|
medium,
|
||||||
|
large
|
||||||
|
}
|
||||||
|
}
|
||||||
|
characterRole
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
idMal
|
||||||
|
isAdult
|
||||||
|
status
|
||||||
|
chapters
|
||||||
|
episodes
|
||||||
|
nextAiringEpisode { episode }
|
||||||
|
type
|
||||||
|
meanScore
|
||||||
|
isFavourite
|
||||||
|
format
|
||||||
|
bannerImage
|
||||||
|
countryOfOrigin
|
||||||
|
coverImage { large }
|
||||||
|
title {
|
||||||
|
english
|
||||||
|
romaji
|
||||||
|
userPreferred
|
||||||
|
}
|
||||||
|
mediaListEntry {
|
||||||
|
progress
|
||||||
|
private
|
||||||
|
score(format: POINT_100)
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}""".prepare() else ""}
|
||||||
|
""".prepare()
|
||||||
|
|
||||||
|
fun studioInformation(page: Int, perPage: Int) = """
|
||||||
|
id
|
||||||
|
name
|
||||||
|
isFavourite
|
||||||
|
favourites
|
||||||
|
media(page: $page, sort:START_DATE_DESC, perPage: $perPage) {
|
||||||
|
$standardPageInformation
|
||||||
|
edges {
|
||||||
|
id
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
idMal
|
||||||
|
isAdult
|
||||||
|
status
|
||||||
|
chapters
|
||||||
|
episodes
|
||||||
|
nextAiringEpisode { episode }
|
||||||
|
type
|
||||||
|
meanScore
|
||||||
|
startDate{ year }
|
||||||
|
isFavourite
|
||||||
|
format
|
||||||
|
bannerImage
|
||||||
|
countryOfOrigin
|
||||||
|
coverImage { large }
|
||||||
|
title {
|
||||||
|
english
|
||||||
|
romaji
|
||||||
|
userPreferred
|
||||||
|
}
|
||||||
|
mediaListEntry {
|
||||||
|
progress
|
||||||
|
private
|
||||||
|
score(format: POINT_100)
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".prepare()
|
||||||
|
|
||||||
|
fun staffInformation(page: Int, perPage: Int) = """
|
||||||
|
id
|
||||||
|
name {
|
||||||
|
first
|
||||||
|
middle
|
||||||
|
last
|
||||||
|
full
|
||||||
|
native
|
||||||
|
userPreferred
|
||||||
|
}
|
||||||
|
image {
|
||||||
|
large
|
||||||
|
medium
|
||||||
|
}
|
||||||
|
dateOfBirth {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
dateOfDeath {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
age
|
||||||
|
yearsActive
|
||||||
|
homeTown
|
||||||
|
staffMedia(page: $page,sort:START_DATE_DESC, perPage: $perPage) {
|
||||||
|
$standardPageInformation
|
||||||
|
edges {
|
||||||
|
staffRole
|
||||||
|
id
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
idMal
|
||||||
|
isAdult
|
||||||
|
status
|
||||||
|
chapters
|
||||||
|
episodes
|
||||||
|
nextAiringEpisode { episode }
|
||||||
|
type
|
||||||
|
meanScore
|
||||||
|
startDate{ year }
|
||||||
|
isFavourite
|
||||||
|
format
|
||||||
|
bannerImage
|
||||||
|
countryOfOrigin
|
||||||
|
coverImage { large }
|
||||||
|
title {
|
||||||
|
english
|
||||||
|
romaji
|
||||||
|
userPreferred
|
||||||
|
}
|
||||||
|
mediaListEntry {
|
||||||
|
progress
|
||||||
|
private
|
||||||
|
score(format: POINT_100)
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".prepare()
|
||||||
|
|
||||||
|
fun userInformation() = """
|
||||||
|
id
|
||||||
|
name
|
||||||
|
about(asHtml: true)
|
||||||
|
avatar {
|
||||||
|
large
|
||||||
|
medium
|
||||||
|
}
|
||||||
|
bannerImage
|
||||||
|
isFollowing
|
||||||
|
isFollower
|
||||||
|
isBlocked
|
||||||
|
siteUrl
|
||||||
|
""".prepare()
|
||||||
|
|
||||||
|
fun aniMangaSearch(perPage: Int?) = """
|
||||||
|
query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult: Boolean = false, ${"$"}search: String, ${"$"}format: [MediaFormat], ${"$"}status: MediaStatus, ${"$"}countryOfOrigin: CountryCode, ${"$"}source: MediaSource, ${"$"}season: MediaSeason, ${"$"}seasonYear: Int, ${"$"}year: String, ${"$"}onList: Boolean, ${"$"}yearLesser: FuzzyDateInt, ${"$"}yearGreater: FuzzyDateInt, ${"$"}episodeLesser: Int, ${"$"}episodeGreater: Int, ${"$"}durationLesser: Int, ${"$"}durationGreater: Int, ${"$"}chapterLesser: Int, ${"$"}chapterGreater: Int, ${"$"}volumeLesser: Int, ${"$"}volumeGreater: Int, ${"$"}licensedBy: [String], ${"$"}isLicensed: Boolean, ${"$"}genres: [String], ${"$"}excludedGenres: [String], ${"$"}tags: [String], ${"$"}excludedTags: [String], ${"$"}minimumTagRank: Int, ${"$"}sort: [MediaSort] = [POPULARITY_DESC, SCORE_DESC, START_DATE_DESC]) {
|
||||||
|
Page(page: ${"$"}page, perPage: ${perPage ?: 50}) {
|
||||||
|
$standardPageInformation
|
||||||
|
media(id: ${"$"}id, type: ${"$"}type, season: ${"$"}season, format_in: ${"$"}format, status: ${"$"}status, countryOfOrigin: ${"$"}countryOfOrigin, source: ${"$"}source, search: ${"$"}search, onList: ${"$"}onList, seasonYear: ${"$"}seasonYear, startDate_like: ${"$"}year, startDate_lesser: ${"$"}yearLesser, startDate_greater: ${"$"}yearGreater, episodes_lesser: ${"$"}episodeLesser, episodes_greater: ${"$"}episodeGreater, duration_lesser: ${"$"}durationLesser, duration_greater: ${"$"}durationGreater, chapters_lesser: ${"$"}chapterLesser, chapters_greater: ${"$"}chapterGreater, volumes_lesser: ${"$"}volumeLesser, volumes_greater: ${"$"}volumeGreater, licensedBy_in: ${"$"}licensedBy, isLicensed: ${"$"}isLicensed, genre_in: ${"$"}genres, genre_not_in: ${"$"}excludedGenres, tag_in: ${"$"}tags, tag_not_in: ${"$"}excludedTags, minimumTagRank: ${"$"}minimumTagRank, sort: ${"$"}sort, isAdult: ${"$"}isAdult) {
|
||||||
|
${standardMediaInformation()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".prepare()
|
||||||
|
|
||||||
|
fun standardMediaInformation() = """
|
||||||
|
id
|
||||||
|
idMal
|
||||||
|
siteUrl
|
||||||
|
isAdult
|
||||||
|
status(version: 2)
|
||||||
|
chapters
|
||||||
|
episodes
|
||||||
|
nextAiringEpisode {
|
||||||
|
episode
|
||||||
|
airingAt
|
||||||
|
}
|
||||||
|
type
|
||||||
|
genres
|
||||||
|
meanScore
|
||||||
|
popularity
|
||||||
|
favourites
|
||||||
|
isFavourite
|
||||||
|
format
|
||||||
|
bannerImage
|
||||||
|
countryOfOrigin
|
||||||
|
coverImage {
|
||||||
|
large
|
||||||
|
extraLarge
|
||||||
|
}
|
||||||
|
title {
|
||||||
|
english
|
||||||
|
romaji
|
||||||
|
userPreferred
|
||||||
|
}
|
||||||
|
mediaListEntry {
|
||||||
|
progress
|
||||||
|
private
|
||||||
|
score(format: POINT_100)
|
||||||
|
status
|
||||||
|
}
|
||||||
|
""".prepare()
|
||||||
|
|
||||||
|
fun fullMediaInformation(id: Int) = """
|
||||||
|
{
|
||||||
|
Media(id: $id) {
|
||||||
|
streamingEpisodes {
|
||||||
|
title
|
||||||
|
thumbnail
|
||||||
|
url
|
||||||
|
site
|
||||||
|
}
|
||||||
|
mediaListEntry {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
score(format: POINT_100)
|
||||||
|
progress
|
||||||
|
private
|
||||||
|
notes
|
||||||
|
repeat
|
||||||
|
customLists
|
||||||
|
updatedAt
|
||||||
|
startedAt {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
completedAt {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reviews(perPage: 3, sort: SCORE_DESC) {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
mediaId
|
||||||
|
mediaType
|
||||||
|
summary
|
||||||
|
body(asHtml: true)
|
||||||
|
rating
|
||||||
|
ratingAmount
|
||||||
|
userRating
|
||||||
|
score
|
||||||
|
private
|
||||||
|
siteUrl
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
bannerImage
|
||||||
|
avatar {
|
||||||
|
medium
|
||||||
|
large
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${standardMediaInformation()}
|
||||||
|
source
|
||||||
|
duration
|
||||||
|
season
|
||||||
|
seasonYear
|
||||||
|
startDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
endDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
studios(isMain: true) {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
siteUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description
|
||||||
|
trailer {
|
||||||
|
site
|
||||||
|
id
|
||||||
|
}
|
||||||
|
synonyms
|
||||||
|
tags {
|
||||||
|
name
|
||||||
|
rank
|
||||||
|
isMediaSpoiler
|
||||||
|
}
|
||||||
|
characters(sort: [ROLE, FAVOURITES_DESC], perPage: 25, page: 1) {
|
||||||
|
edges {
|
||||||
|
role
|
||||||
|
voiceActors {
|
||||||
|
id
|
||||||
|
name {
|
||||||
|
first
|
||||||
|
middle
|
||||||
|
last
|
||||||
|
full
|
||||||
|
native
|
||||||
|
userPreferred
|
||||||
|
}
|
||||||
|
image {
|
||||||
|
large
|
||||||
|
medium
|
||||||
|
}
|
||||||
|
languageV2
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
image {
|
||||||
|
medium
|
||||||
|
}
|
||||||
|
name {
|
||||||
|
userPreferred
|
||||||
|
}
|
||||||
|
isFavourite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
relations {
|
||||||
|
edges {
|
||||||
|
relationType(version: 2)
|
||||||
|
node {
|
||||||
|
${standardMediaInformation()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
staffPreview: staff(perPage: 8, sort: [RELEVANCE, ID]) {
|
||||||
|
edges {
|
||||||
|
role
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
image {
|
||||||
|
large
|
||||||
|
medium
|
||||||
|
}
|
||||||
|
name {
|
||||||
|
userPreferred
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recommendations(sort: RATING_DESC) {
|
||||||
|
nodes {
|
||||||
|
mediaRecommendation {
|
||||||
|
${standardMediaInformation()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
externalLinks {
|
||||||
|
url
|
||||||
|
site
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Page(page: 1) {
|
||||||
|
$standardPageInformation
|
||||||
|
mediaList(isFollowing: true, sort: [STATUS], mediaId: $id) {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
score(format: POINT_100)
|
||||||
|
progress
|
||||||
|
progressVolumes
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
avatar {
|
||||||
|
large
|
||||||
|
medium
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
""".prepare()
|
|
@ -446,7 +446,7 @@ data class MediaEdge(
|
||||||
@SerialName("staffRole") var staffRole: String?,
|
@SerialName("staffRole") var staffRole: String?,
|
||||||
|
|
||||||
// The voice actors of the character
|
// The voice actors of the character
|
||||||
// @SerialName("voiceActors") var voiceActors: List<Staff>?,
|
@SerialName("voiceActors") var voiceActors: List<Staff>?,
|
||||||
|
|
||||||
// The voice actors of the character with role date
|
// The voice actors of the character with role date
|
||||||
// @SerialName("voiceActorRoles") var voiceActorRoles: List<StaffRoleType>?,
|
// @SerialName("voiceActorRoles") var voiceActorRoles: List<StaffRoleType>?,
|
||||||
|
|
|
@ -24,7 +24,7 @@ import ani.dantotsu.Refresh
|
||||||
import ani.dantotsu.bottomBar
|
import ani.dantotsu.bottomBar
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.connections.anilist.AnilistAnimeViewModel
|
import ani.dantotsu.connections.anilist.AnilistAnimeViewModel
|
||||||
import ani.dantotsu.connections.anilist.SearchResults
|
import ani.dantotsu.connections.anilist.AniMangaSearchResults
|
||||||
import ani.dantotsu.connections.anilist.getUserId
|
import ani.dantotsu.connections.anilist.getUserId
|
||||||
import ani.dantotsu.databinding.FragmentAnimeBinding
|
import ani.dantotsu.databinding.FragmentAnimeBinding
|
||||||
import ani.dantotsu.media.MediaAdaptor
|
import ani.dantotsu.media.MediaAdaptor
|
||||||
|
@ -100,7 +100,7 @@ class AnimeFragment : Fragment() {
|
||||||
var loading = true
|
var loading = true
|
||||||
if (model.notSet) {
|
if (model.notSet) {
|
||||||
model.notSet = false
|
model.notSet = false
|
||||||
model.searchResults = SearchResults(
|
model.aniMangaSearchResults = AniMangaSearchResults(
|
||||||
"ANIME",
|
"ANIME",
|
||||||
isAdult = false,
|
isAdult = false,
|
||||||
onList = false,
|
onList = false,
|
||||||
|
@ -109,7 +109,7 @@ class AnimeFragment : Fragment() {
|
||||||
sort = Anilist.sortBy[1]
|
sort = Anilist.sortBy[1]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val popularAdaptor = MediaAdaptor(1, model.searchResults.results, requireActivity())
|
val popularAdaptor = MediaAdaptor(1, model.aniMangaSearchResults.results, requireActivity())
|
||||||
val progressAdaptor = ProgressAdapter(searched = model.searched)
|
val progressAdaptor = ProgressAdapter(searched = model.searched)
|
||||||
val adapter = ConcatAdapter(animePageAdapter, popularAdaptor, progressAdaptor)
|
val adapter = ConcatAdapter(animePageAdapter, popularAdaptor, progressAdaptor)
|
||||||
binding.animePageRecyclerView.adapter = adapter
|
binding.animePageRecyclerView.adapter = adapter
|
||||||
|
@ -142,7 +142,7 @@ class AnimeFragment : Fragment() {
|
||||||
animePageAdapter.onIncludeListClick = { checked ->
|
animePageAdapter.onIncludeListClick = { checked ->
|
||||||
oldIncludeList = !checked
|
oldIncludeList = !checked
|
||||||
loading = true
|
loading = true
|
||||||
model.searchResults.results.clear()
|
model.aniMangaSearchResults.results.clear()
|
||||||
popularAdaptor.notifyDataSetChanged()
|
popularAdaptor.notifyDataSetChanged()
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
model.loadPopular("ANIME", sort = Anilist.sortBy[1], onList = checked)
|
model.loadPopular("ANIME", sort = Anilist.sortBy[1], onList = checked)
|
||||||
|
@ -152,17 +152,17 @@ class AnimeFragment : Fragment() {
|
||||||
model.getPopular().observe(viewLifecycleOwner) {
|
model.getPopular().observe(viewLifecycleOwner) {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
if (oldIncludeList == (it.onList != false)) {
|
if (oldIncludeList == (it.onList != false)) {
|
||||||
val prev = model.searchResults.results.size
|
val prev = model.aniMangaSearchResults.results.size
|
||||||
model.searchResults.results.addAll(it.results)
|
model.aniMangaSearchResults.results.addAll(it.results)
|
||||||
popularAdaptor.notifyItemRangeInserted(prev, it.results.size)
|
popularAdaptor.notifyItemRangeInserted(prev, it.results.size)
|
||||||
} else {
|
} else {
|
||||||
model.searchResults.results.addAll(it.results)
|
model.aniMangaSearchResults.results.addAll(it.results)
|
||||||
popularAdaptor.notifyDataSetChanged()
|
popularAdaptor.notifyDataSetChanged()
|
||||||
oldIncludeList = it.onList ?: true
|
oldIncludeList = it.onList ?: true
|
||||||
}
|
}
|
||||||
model.searchResults.onList = it.onList
|
model.aniMangaSearchResults.onList = it.onList
|
||||||
model.searchResults.hasNextPage = it.hasNextPage
|
model.aniMangaSearchResults.hasNextPage = it.hasNextPage
|
||||||
model.searchResults.page = it.page
|
model.aniMangaSearchResults.page = it.page
|
||||||
if (it.hasNextPage)
|
if (it.hasNextPage)
|
||||||
progressAdaptor.bar?.visibility = View.VISIBLE
|
progressAdaptor.bar?.visibility = View.VISIBLE
|
||||||
else {
|
else {
|
||||||
|
@ -177,10 +177,10 @@ class AnimeFragment : Fragment() {
|
||||||
RecyclerView.OnScrollListener() {
|
RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(v: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(v: RecyclerView, dx: Int, dy: Int) {
|
||||||
if (!v.canScrollVertically(1)) {
|
if (!v.canScrollVertically(1)) {
|
||||||
if (model.searchResults.hasNextPage && model.searchResults.results.isNotEmpty() && !loading) {
|
if (model.aniMangaSearchResults.hasNextPage && model.aniMangaSearchResults.results.isNotEmpty() && !loading) {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
loading = true
|
loading = true
|
||||||
model.loadNextPage(model.searchResults)
|
model.loadNextPage(model.aniMangaSearchResults)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
@ -21,7 +20,6 @@ import androidx.viewpager2.widget.ViewPager2
|
||||||
import ani.dantotsu.MediaPageTransformer
|
import ani.dantotsu.MediaPageTransformer
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.currContext
|
|
||||||
import ani.dantotsu.databinding.ItemAnimePageBinding
|
import ani.dantotsu.databinding.ItemAnimePageBinding
|
||||||
import ani.dantotsu.databinding.LayoutTrendingBinding
|
import ani.dantotsu.databinding.LayoutTrendingBinding
|
||||||
import ani.dantotsu.getAppString
|
import ani.dantotsu.getAppString
|
||||||
|
@ -32,7 +30,6 @@ import ani.dantotsu.media.GenreActivity
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
import ani.dantotsu.media.MediaAdaptor
|
import ani.dantotsu.media.MediaAdaptor
|
||||||
import ani.dantotsu.media.MediaListViewActivity
|
import ani.dantotsu.media.MediaListViewActivity
|
||||||
import ani.dantotsu.media.SearchActivity
|
|
||||||
import ani.dantotsu.profile.ProfileActivity
|
import ani.dantotsu.profile.ProfileActivity
|
||||||
import ani.dantotsu.px
|
import ani.dantotsu.px
|
||||||
import ani.dantotsu.setSafeOnClickListener
|
import ani.dantotsu.setSafeOnClickListener
|
||||||
|
@ -83,12 +80,11 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
|
||||||
|
|
||||||
updateAvatar()
|
updateAvatar()
|
||||||
|
|
||||||
trendingBinding.searchBar.hint = "ANIME"
|
trendingBinding.searchBar.hint = binding.root.context.getString(R.string.search)
|
||||||
trendingBinding.searchBarText.setOnClickListener {
|
trendingBinding.searchBarText.setOnClickListener {
|
||||||
ContextCompat.startActivity(
|
SearchBottomSheet.newInstance().show(
|
||||||
it.context,
|
(binding.root.context as AppCompatActivity).supportFragmentManager,
|
||||||
Intent(it.context, SearchActivity::class.java).putExtra("type", "ANIME"),
|
"search"
|
||||||
null
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import ani.dantotsu.Refresh
|
||||||
import ani.dantotsu.bottomBar
|
import ani.dantotsu.bottomBar
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.connections.anilist.AnilistMangaViewModel
|
import ani.dantotsu.connections.anilist.AnilistMangaViewModel
|
||||||
import ani.dantotsu.connections.anilist.SearchResults
|
import ani.dantotsu.connections.anilist.AniMangaSearchResults
|
||||||
import ani.dantotsu.connections.anilist.getUserId
|
import ani.dantotsu.connections.anilist.getUserId
|
||||||
import ani.dantotsu.databinding.FragmentMangaBinding
|
import ani.dantotsu.databinding.FragmentMangaBinding
|
||||||
import ani.dantotsu.media.MediaAdaptor
|
import ani.dantotsu.media.MediaAdaptor
|
||||||
|
@ -94,7 +94,7 @@ class MangaFragment : Fragment() {
|
||||||
var loading = true
|
var loading = true
|
||||||
if (model.notSet) {
|
if (model.notSet) {
|
||||||
model.notSet = false
|
model.notSet = false
|
||||||
model.searchResults = SearchResults(
|
model.aniMangaSearchResults = AniMangaSearchResults(
|
||||||
"MANGA",
|
"MANGA",
|
||||||
isAdult = false,
|
isAdult = false,
|
||||||
onList = false,
|
onList = false,
|
||||||
|
@ -103,7 +103,7 @@ class MangaFragment : Fragment() {
|
||||||
sort = Anilist.sortBy[1]
|
sort = Anilist.sortBy[1]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val popularAdaptor = MediaAdaptor(1, model.searchResults.results, requireActivity())
|
val popularAdaptor = MediaAdaptor(1, model.aniMangaSearchResults.results, requireActivity())
|
||||||
val progressAdaptor = ProgressAdapter(searched = model.searched)
|
val progressAdaptor = ProgressAdapter(searched = model.searched)
|
||||||
binding.mangaPageRecyclerView.adapter =
|
binding.mangaPageRecyclerView.adapter =
|
||||||
ConcatAdapter(mangaPageAdapter, popularAdaptor, progressAdaptor)
|
ConcatAdapter(mangaPageAdapter, popularAdaptor, progressAdaptor)
|
||||||
|
@ -135,10 +135,10 @@ class MangaFragment : Fragment() {
|
||||||
RecyclerView.OnScrollListener() {
|
RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(v: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(v: RecyclerView, dx: Int, dy: Int) {
|
||||||
if (!v.canScrollVertically(1)) {
|
if (!v.canScrollVertically(1)) {
|
||||||
if (model.searchResults.hasNextPage && model.searchResults.results.isNotEmpty() && !loading) {
|
if (model.aniMangaSearchResults.hasNextPage && model.aniMangaSearchResults.results.isNotEmpty() && !loading) {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
loading = true
|
loading = true
|
||||||
model.loadNextPage(model.searchResults)
|
model.loadNextPage(model.aniMangaSearchResults)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ class MangaFragment : Fragment() {
|
||||||
mangaPageAdapter.onIncludeListClick = { checked ->
|
mangaPageAdapter.onIncludeListClick = { checked ->
|
||||||
oldIncludeList = !checked
|
oldIncludeList = !checked
|
||||||
loading = true
|
loading = true
|
||||||
model.searchResults.results.clear()
|
model.aniMangaSearchResults.results.clear()
|
||||||
popularAdaptor.notifyDataSetChanged()
|
popularAdaptor.notifyDataSetChanged()
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
model.loadPopular("MANGA", sort = Anilist.sortBy[1], onList = checked)
|
model.loadPopular("MANGA", sort = Anilist.sortBy[1], onList = checked)
|
||||||
|
@ -230,17 +230,17 @@ class MangaFragment : Fragment() {
|
||||||
model.getPopular().observe(viewLifecycleOwner) {
|
model.getPopular().observe(viewLifecycleOwner) {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
if (oldIncludeList == (it.onList != false)) {
|
if (oldIncludeList == (it.onList != false)) {
|
||||||
val prev = model.searchResults.results.size
|
val prev = model.aniMangaSearchResults.results.size
|
||||||
model.searchResults.results.addAll(it.results)
|
model.aniMangaSearchResults.results.addAll(it.results)
|
||||||
popularAdaptor.notifyItemRangeInserted(prev, it.results.size)
|
popularAdaptor.notifyItemRangeInserted(prev, it.results.size)
|
||||||
} else {
|
} else {
|
||||||
model.searchResults.results.addAll(it.results)
|
model.aniMangaSearchResults.results.addAll(it.results)
|
||||||
popularAdaptor.notifyDataSetChanged()
|
popularAdaptor.notifyDataSetChanged()
|
||||||
oldIncludeList = it.onList ?: true
|
oldIncludeList = it.onList ?: true
|
||||||
}
|
}
|
||||||
model.searchResults.onList = it.onList
|
model.aniMangaSearchResults.onList = it.onList
|
||||||
model.searchResults.hasNextPage = it.hasNextPage
|
model.aniMangaSearchResults.hasNextPage = it.hasNextPage
|
||||||
model.searchResults.page = it.page
|
model.aniMangaSearchResults.page = it.page
|
||||||
if (it.hasNextPage)
|
if (it.hasNextPage)
|
||||||
progressAdaptor.bar?.visibility = View.VISIBLE
|
progressAdaptor.bar?.visibility = View.VISIBLE
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -82,12 +82,11 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
|
||||||
trendingBinding.notificationCount.isVisible = Anilist.unreadNotificationCount > 0
|
trendingBinding.notificationCount.isVisible = Anilist.unreadNotificationCount > 0
|
||||||
&& PrefManager.getVal<Boolean>(PrefName.ShowNotificationRedDot) == true
|
&& PrefManager.getVal<Boolean>(PrefName.ShowNotificationRedDot) == true
|
||||||
trendingBinding.notificationCount.text = Anilist.unreadNotificationCount.toString()
|
trendingBinding.notificationCount.text = Anilist.unreadNotificationCount.toString()
|
||||||
trendingBinding.searchBar.hint = "MANGA"
|
trendingBinding.searchBar.hint = binding.root.context.getString(R.string.search)
|
||||||
trendingBinding.searchBarText.setOnClickListener {
|
trendingBinding.searchBarText.setOnClickListener {
|
||||||
ContextCompat.startActivity(
|
SearchBottomSheet.newInstance().show(
|
||||||
it.context,
|
(binding.root.context as AppCompatActivity).supportFragmentManager,
|
||||||
Intent(it.context, SearchActivity::class.java).putExtra("type", "MANGA"),
|
"search"
|
||||||
null
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
74
app/src/main/java/ani/dantotsu/home/SearchBottomSheet.kt
Normal file
74
app/src/main/java/ani/dantotsu/home/SearchBottomSheet.kt
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package ani.dantotsu.home
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import ani.dantotsu.BottomSheetDialogFragment
|
||||||
|
import ani.dantotsu.connections.anilist.AnilistSearch.SearchType
|
||||||
|
import ani.dantotsu.connections.anilist.AnilistSearch.SearchType.Companion.toAnilistString
|
||||||
|
import ani.dantotsu.databinding.BottomSheetSearchBinding
|
||||||
|
import ani.dantotsu.media.SearchActivity
|
||||||
|
|
||||||
|
class SearchBottomSheet : BottomSheetDialogFragment() {
|
||||||
|
private var _binding: BottomSheetSearchBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
_binding = BottomSheetSearchBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
binding.animeSearch.setOnClickListener {
|
||||||
|
startActivity(requireContext(), SearchType.ANIME)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.mangaSearch.setOnClickListener {
|
||||||
|
startActivity(requireContext(), SearchType.MANGA)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.characterSearch.setOnClickListener {
|
||||||
|
startActivity(requireContext(), SearchType.CHARACTER)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.staffSearch.setOnClickListener {
|
||||||
|
startActivity(requireContext(), SearchType.STAFF)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.studioSearch.setOnClickListener {
|
||||||
|
startActivity(requireContext(), SearchType.STUDIO)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.userSearch.setOnClickListener {
|
||||||
|
startActivity(requireContext(), SearchType.USER)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startActivity(context: Context, type: SearchType) {
|
||||||
|
ContextCompat.startActivity(
|
||||||
|
context,
|
||||||
|
Intent(context, SearchActivity::class.java).putExtra("type", type.toAnilistString()),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance() = SearchBottomSheet()
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,12 @@ data class Author(
|
||||||
var name: String?,
|
var name: String?,
|
||||||
var image: String?,
|
var image: String?,
|
||||||
var role: String?,
|
var role: String?,
|
||||||
|
var age: Int? = null,
|
||||||
|
var yearsActive: List<Int>? = null,
|
||||||
|
var dateOfBirth: String? = null,
|
||||||
|
var dateOfDeath: String? = null,
|
||||||
|
var homeTown: String? = null,
|
||||||
var yearMedia: MutableMap<String, ArrayList<Media>>? = null,
|
var yearMedia: MutableMap<String, ArrayList<Media>>? = null,
|
||||||
var character: ArrayList<Character>? = null
|
var character: ArrayList<Character>? = null,
|
||||||
|
var isFav: Boolean = false
|
||||||
) : Serializable
|
) : Serializable
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package ani.dantotsu.media
|
package ani.dantotsu.media
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.math.MathUtils.clamp
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
@ -16,57 +18,127 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import ani.dantotsu.EmptyAdapter
|
import ani.dantotsu.EmptyAdapter
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.Refresh
|
import ani.dantotsu.Refresh
|
||||||
import ani.dantotsu.databinding.ActivityAuthorBinding
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
|
import ani.dantotsu.connections.anilist.AnilistMutations
|
||||||
|
import ani.dantotsu.databinding.ActivityCharacterBinding
|
||||||
import ani.dantotsu.initActivity
|
import ani.dantotsu.initActivity
|
||||||
|
import ani.dantotsu.loadImage
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
|
import ani.dantotsu.openLinkInBrowser
|
||||||
|
import ani.dantotsu.others.ImageViewDialog
|
||||||
|
import ani.dantotsu.others.SpoilerPlugin
|
||||||
import ani.dantotsu.others.getSerialized
|
import ani.dantotsu.others.getSerialized
|
||||||
import ani.dantotsu.px
|
import ani.dantotsu.px
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
|
import ani.dantotsu.snackString
|
||||||
import ani.dantotsu.statusBarHeight
|
import ani.dantotsu.statusBarHeight
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
import io.noties.markwon.Markwon
|
||||||
|
import io.noties.markwon.SoftBreakAddsNewLinePlugin
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
class AuthorActivity : AppCompatActivity() {
|
class AuthorActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListener {
|
||||||
private lateinit var binding: ActivityAuthorBinding
|
private lateinit var binding: ActivityCharacterBinding
|
||||||
private val scope = lifecycleScope
|
private val scope = lifecycleScope
|
||||||
private val model: OtherDetailsViewModel by viewModels()
|
private val model: OtherDetailsViewModel by viewModels()
|
||||||
private var author: Author? = null
|
private lateinit var author: Author
|
||||||
private var loaded = false
|
private var loaded = false
|
||||||
|
|
||||||
|
private var screenWidth: Float = 0f
|
||||||
|
private val percent = 30
|
||||||
|
private var mMaxScrollSize = 0
|
||||||
|
private var isCollapsed = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
binding = ActivityAuthorBinding.inflate(layoutInflater)
|
binding = ActivityCharacterBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
initActivity(this)
|
initActivity(this)
|
||||||
this.window.statusBarColor = ContextCompat.getColor(this, R.color.nav_bg)
|
screenWidth = resources.displayMetrics.run { widthPixels / density }
|
||||||
|
if (PrefManager.getVal(PrefName.ImmersiveMode)) this.window.statusBarColor =
|
||||||
|
ContextCompat.getColor(this, R.color.transparent)
|
||||||
|
|
||||||
val screenWidth = resources.displayMetrics.run { widthPixels / density }
|
val banner =
|
||||||
|
if (PrefManager.getVal(PrefName.BannerAnimations)) binding.characterBanner else binding.characterBannerNoKen
|
||||||
|
|
||||||
binding.root.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
|
banner.updateLayoutParams { height += statusBarHeight }
|
||||||
binding.studioRecycler.updatePadding(bottom = 64f.px + navBarHeight)
|
binding.characterClose.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
|
||||||
binding.studioTitle.isSelected = true
|
binding.characterCollapsing.minimumHeight = statusBarHeight
|
||||||
|
binding.characterCover.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
|
||||||
|
binding.characterRecyclerView.updatePadding(bottom = 64f.px + navBarHeight)
|
||||||
|
binding.characterTitle.isSelected = true
|
||||||
|
binding.characterAppBar.addOnOffsetChangedListener(this)
|
||||||
|
|
||||||
author = intent.getSerialized("author")
|
binding.characterClose.setOnClickListener {
|
||||||
binding.studioTitle.text = author?.name
|
|
||||||
|
|
||||||
binding.studioClose.setOnClickListener {
|
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
author = intent.getSerialized("author") ?: return
|
||||||
|
binding.characterTitle.text = author.name
|
||||||
|
binding.characterCoverImage.loadImage(author.image)
|
||||||
|
binding.characterCoverImage.setOnLongClickListener {
|
||||||
|
ImageViewDialog.newInstance(
|
||||||
|
this,
|
||||||
|
author.name,
|
||||||
|
author.image
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val link = "https://anilist.co/staff/${author.id}"
|
||||||
|
binding.characterShare.setOnClickListener {
|
||||||
|
val i = Intent(Intent.ACTION_SEND)
|
||||||
|
i.type = "text/plain"
|
||||||
|
i.putExtra(Intent.EXTRA_TEXT, link)
|
||||||
|
startActivity(Intent.createChooser(i, author.name))
|
||||||
|
}
|
||||||
|
binding.characterShare.setOnLongClickListener {
|
||||||
|
openLinkInBrowser(link)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
lifecycleScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
author.isFav =
|
||||||
|
Anilist.query.isUserFav(AnilistMutations.FavType.STAFF, author.id)
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
binding.characterFav.setImageResource(
|
||||||
|
if (author.isFav) R.drawable.ic_round_favorite_24 else R.drawable.ic_round_favorite_border_24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.characterFav.setOnClickListener {
|
||||||
|
scope.launch {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
if (Anilist.mutation.toggleFav(AnilistMutations.FavType.CHARACTER, author.id)) {
|
||||||
|
author.isFav = !author.isFav
|
||||||
|
binding.characterFav.setImageResource(
|
||||||
|
if (author.isFav) R.drawable.ic_round_favorite_24 else R.drawable.ic_round_favorite_border_24
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
snackString("Failed to toggle favorite")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
model.getAuthor().observe(this) {
|
model.getAuthor().observe(this) {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
author = it
|
author = it
|
||||||
loaded = true
|
loaded = true
|
||||||
binding.studioProgressBar.visibility = View.GONE
|
binding.characterProgress.visibility = View.GONE
|
||||||
binding.studioRecycler.visibility = View.VISIBLE
|
binding.characterRecyclerView.visibility = View.VISIBLE
|
||||||
if (author!!.yearMedia.isNullOrEmpty()) {
|
if (author.yearMedia.isNullOrEmpty()) {
|
||||||
binding.studioRecycler.visibility = View.GONE
|
binding.characterRecyclerView.visibility = View.GONE
|
||||||
}
|
}
|
||||||
val titlePosition = arrayListOf<Int>()
|
val titlePosition = arrayListOf<Int>()
|
||||||
val concatAdapter = ConcatAdapter()
|
val concatAdapter = ConcatAdapter()
|
||||||
val map = author!!.yearMedia ?: return@observe
|
val map = author.yearMedia ?: return@observe
|
||||||
val keys = map.keys.toTypedArray()
|
val keys = map.keys.toTypedArray()
|
||||||
var pos = 0
|
var pos = 0
|
||||||
|
|
||||||
|
@ -80,6 +152,10 @@ class AuthorActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val desc = createDesc(author)
|
||||||
|
val markWon = Markwon.builder(this).usePlugin(SoftBreakAddsNewLinePlugin.create())
|
||||||
|
.usePlugin(SpoilerPlugin()).build()
|
||||||
|
markWon.setMarkdown(binding.authorCharacterDesc, desc)
|
||||||
for (i in keys.indices) {
|
for (i in keys.indices) {
|
||||||
val medias = map[keys[i]]!!
|
val medias = map[keys[i]]!!
|
||||||
val empty = if (medias.size >= 4) medias.size % 4 else 4 - medias.size
|
val empty = if (medias.size >= 4) medias.size % 4 else 4 - medias.size
|
||||||
|
@ -90,18 +166,18 @@ class AuthorActivity : AppCompatActivity() {
|
||||||
concatAdapter.addAdapter(MediaAdaptor(0, medias, this, true))
|
concatAdapter.addAdapter(MediaAdaptor(0, medias, this, true))
|
||||||
concatAdapter.addAdapter(EmptyAdapter(empty))
|
concatAdapter.addAdapter(EmptyAdapter(empty))
|
||||||
}
|
}
|
||||||
binding.studioRecycler.adapter = concatAdapter
|
binding.characterRecyclerView.adapter = concatAdapter
|
||||||
binding.studioRecycler.layoutManager = gridLayoutManager
|
binding.characterRecyclerView.layoutManager = gridLayoutManager
|
||||||
|
|
||||||
binding.charactersRecycler.visibility = View.VISIBLE
|
binding.authorCharactersRecycler.visibility = View.VISIBLE
|
||||||
binding.charactersText.visibility = View.VISIBLE
|
binding.AuthorCharactersText.visibility = View.VISIBLE
|
||||||
binding.charactersRecycler.adapter =
|
binding.authorCharactersRecycler.adapter =
|
||||||
CharacterAdapter(author!!.character ?: arrayListOf())
|
CharacterAdapter(author.character ?: arrayListOf())
|
||||||
binding.charactersRecycler.layoutManager =
|
binding.authorCharactersRecycler.layoutManager =
|
||||||
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
|
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
|
||||||
if (author!!.character.isNullOrEmpty()) {
|
if (author.character.isNullOrEmpty()) {
|
||||||
binding.charactersRecycler.visibility = View.GONE
|
binding.authorCharactersRecycler.visibility = View.GONE
|
||||||
binding.charactersText.visibility = View.GONE
|
binding.AuthorCharactersText.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,14 +185,28 @@ class AuthorActivity : AppCompatActivity() {
|
||||||
live.observe(this) {
|
live.observe(this) {
|
||||||
if (it) {
|
if (it) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
if (author != null)
|
withContext(Dispatchers.IO) { model.loadAuthor(author) }
|
||||||
withContext(Dispatchers.IO) { model.loadAuthor(author!!) }
|
|
||||||
live.postValue(false)
|
live.postValue(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createDesc(author: Author): String {
|
||||||
|
val age = if (author.age != null) "${getString(R.string.age)} ${author.age}" else ""
|
||||||
|
val yearsActive =
|
||||||
|
if (author.yearsActive != null) "${getString(R.string.years_active)} ${author.yearsActive}" else ""
|
||||||
|
val dob =
|
||||||
|
if (author.dateOfBirth != null) "${getString(R.string.birthday)} ${author.dateOfBirth}" else ""
|
||||||
|
val homeTown =
|
||||||
|
if (author.homeTown != null) "${getString(R.string.hometown)} ${author.homeTown}" else ""
|
||||||
|
val dod =
|
||||||
|
if (author.dateOfDeath != null) "${getString(R.string.date_of_death)} ${author.dateOfDeath}" else ""
|
||||||
|
|
||||||
|
return "$age $yearsActive $dob $homeTown $dod"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
if (Refresh.activity.containsKey(this.hashCode())) {
|
if (Refresh.activity.containsKey(this.hashCode())) {
|
||||||
Refresh.activity.remove(this.hashCode())
|
Refresh.activity.remove(this.hashCode())
|
||||||
|
@ -125,7 +215,31 @@ class AuthorActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
binding.studioProgressBar.visibility = if (!loaded) View.VISIBLE else View.GONE
|
binding.characterProgress.visibility = if (!loaded) View.VISIBLE else View.GONE
|
||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onOffsetChanged(appBar: AppBarLayout, i: Int) {
|
||||||
|
if (mMaxScrollSize == 0) mMaxScrollSize = appBar.totalScrollRange
|
||||||
|
val percentage = abs(i) * 100 / mMaxScrollSize
|
||||||
|
val cap = clamp((percent - percentage) / percent.toFloat(), 0f, 1f)
|
||||||
|
|
||||||
|
binding.characterCover.scaleX = 1f * cap
|
||||||
|
binding.characterCover.scaleY = 1f * cap
|
||||||
|
binding.characterCover.cardElevation = 32f * cap
|
||||||
|
|
||||||
|
binding.characterCover.visibility =
|
||||||
|
if (binding.characterCover.scaleX == 0f) View.GONE else View.VISIBLE
|
||||||
|
val immersiveMode: Boolean = PrefManager.getVal(PrefName.ImmersiveMode)
|
||||||
|
if (percentage >= percent && !isCollapsed) {
|
||||||
|
isCollapsed = true
|
||||||
|
if (immersiveMode) this.window.statusBarColor =
|
||||||
|
ContextCompat.getColor(this, R.color.nav_bg)
|
||||||
|
}
|
||||||
|
if (percentage <= percent && isCollapsed) {
|
||||||
|
isCollapsed = false
|
||||||
|
if (immersiveMode) this.window.statusBarColor =
|
||||||
|
ContextCompat.getColor(this, R.color.transparent)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@ import ani.dantotsu.setAnimation
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
class AuthorAdapter(
|
class AuthorAdapter(
|
||||||
private val authorList: ArrayList<Author>,
|
private val authorList: MutableList<Author>,
|
||||||
) : RecyclerView.Adapter<AuthorAdapter.AuthorViewHolder>() {
|
) : RecyclerView.Adapter<AuthorAdapter.AuthorViewHolder>() {
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AuthorViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AuthorViewHolder {
|
||||||
val binding =
|
val binding =
|
||||||
|
@ -26,7 +26,7 @@ class AuthorAdapter(
|
||||||
override fun onBindViewHolder(holder: AuthorViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: AuthorViewHolder, position: Int) {
|
||||||
val binding = holder.binding
|
val binding = holder.binding
|
||||||
setAnimation(binding.root.context, holder.binding.root)
|
setAnimation(binding.root.context, holder.binding.root)
|
||||||
val author = authorList[position]
|
val author = authorList.getOrNull(position) ?: return
|
||||||
binding.itemCompactRelation.text = author.role
|
binding.itemCompactRelation.text = author.role
|
||||||
binding.itemCompactImage.loadImage(author.image)
|
binding.itemCompactImage.loadImage(author.image)
|
||||||
binding.itemCompactTitle.text = author.name
|
binding.itemCompactTitle.text = author.name
|
||||||
|
|
|
@ -16,7 +16,7 @@ import ani.dantotsu.setAnimation
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
class CharacterAdapter(
|
class CharacterAdapter(
|
||||||
private val characterList: ArrayList<Character>
|
private val characterList: MutableList<Character>
|
||||||
) : RecyclerView.Adapter<CharacterAdapter.CharacterViewHolder>() {
|
) : RecyclerView.Adapter<CharacterAdapter.CharacterViewHolder>() {
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CharacterViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CharacterViewHolder {
|
||||||
val binding =
|
val binding =
|
||||||
|
@ -27,9 +27,8 @@ class CharacterAdapter(
|
||||||
override fun onBindViewHolder(holder: CharacterViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: CharacterViewHolder, position: Int) {
|
||||||
val binding = holder.binding
|
val binding = holder.binding
|
||||||
setAnimation(binding.root.context, holder.binding.root)
|
setAnimation(binding.root.context, holder.binding.root)
|
||||||
val character = characterList[position]
|
val character = characterList.getOrNull(position) ?: return
|
||||||
val whitespace = "${character.role} "
|
val whitespace = "${if (character.role.lowercase() == "null") "" else character.role} "
|
||||||
character.voiceActor
|
|
||||||
binding.itemCompactRelation.text = whitespace
|
binding.itemCompactRelation.text = whitespace
|
||||||
binding.itemCompactImage.loadImage(character.image)
|
binding.itemCompactImage.loadImage(character.image)
|
||||||
binding.itemCompactTitle.text = character.name
|
binding.itemCompactTitle.text = character.name
|
||||||
|
|
|
@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.math.MathUtils.clamp
|
import androidx.core.math.MathUtils.clamp
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
@ -45,6 +46,11 @@ class CharacterDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChang
|
||||||
private lateinit var character: Character
|
private lateinit var character: Character
|
||||||
private var loaded = false
|
private var loaded = false
|
||||||
|
|
||||||
|
private var isCollapsed = false
|
||||||
|
private val percent = 30
|
||||||
|
private var mMaxScrollSize = 0
|
||||||
|
private var screenWidth: Float = 0f
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
@ -71,6 +77,11 @@ class CharacterDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChang
|
||||||
binding.characterClose.setOnClickListener {
|
binding.characterClose.setOnClickListener {
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.authorCharactersRecycler.isVisible = false
|
||||||
|
binding.AuthorCharactersText.isVisible = false
|
||||||
|
binding.authorCharacterDesc.isVisible = false
|
||||||
|
|
||||||
character = intent.getSerialized("character") ?: return
|
character = intent.getSerialized("character") ?: return
|
||||||
binding.characterTitle.text = character.name
|
binding.characterTitle.text = character.name
|
||||||
banner.loadImage(character.banner)
|
banner.loadImage(character.banner)
|
||||||
|
@ -158,11 +169,6 @@ class CharacterDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChang
|
||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isCollapsed = false
|
|
||||||
private val percent = 30
|
|
||||||
private var mMaxScrollSize = 0
|
|
||||||
private var screenWidth: Float = 0f
|
|
||||||
|
|
||||||
override fun onOffsetChanged(appBar: AppBarLayout, i: Int) {
|
override fun onOffsetChanged(appBar: AppBarLayout, i: Int) {
|
||||||
if (mMaxScrollSize == 0) mMaxScrollSize = appBar.totalScrollRange
|
if (mMaxScrollSize == 0) mMaxScrollSize = appBar.totalScrollRange
|
||||||
val percentage = abs(i) * 100 / mMaxScrollSize
|
val percentage = abs(i) * 100 / mMaxScrollSize
|
||||||
|
|
77
app/src/main/java/ani/dantotsu/media/HeaderInterface.kt
Normal file
77
app/src/main/java/ani/dantotsu/media/HeaderInterface.kt
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package ani.dantotsu.media
|
||||||
|
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.animation.AlphaAnimation
|
||||||
|
import android.view.animation.Animation
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import ani.dantotsu.databinding.ItemSearchHeaderBinding
|
||||||
|
|
||||||
|
abstract class HeaderInterface: RecyclerView.Adapter<HeaderInterface.SearchHeaderViewHolder>() {
|
||||||
|
private val itemViewType = 6969
|
||||||
|
var search: Runnable? = null
|
||||||
|
var requestFocus: Runnable? = null
|
||||||
|
protected var textWatcher: TextWatcher? = null
|
||||||
|
protected lateinit var searchHistoryAdapter: SearchHistoryAdapter
|
||||||
|
protected lateinit var binding: ItemSearchHeaderBinding
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHeaderViewHolder {
|
||||||
|
val binding =
|
||||||
|
ItemSearchHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
return SearchHeaderViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setHistoryVisibility(visible: Boolean) {
|
||||||
|
if (visible) {
|
||||||
|
binding.searchResultLayout.startAnimation(fadeOutAnimation())
|
||||||
|
binding.searchHistoryList.startAnimation(fadeInAnimation())
|
||||||
|
binding.searchResultLayout.visibility = View.GONE
|
||||||
|
binding.searchHistoryList.visibility = View.VISIBLE
|
||||||
|
binding.searchByImage.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
if (binding.searchResultLayout.visibility != View.VISIBLE) {
|
||||||
|
binding.searchResultLayout.startAnimation(fadeInAnimation())
|
||||||
|
binding.searchHistoryList.startAnimation(fadeOutAnimation())
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.searchResultLayout.visibility = View.VISIBLE
|
||||||
|
binding.clearHistory.visibility = View.GONE
|
||||||
|
binding.searchHistoryList.visibility = View.GONE
|
||||||
|
binding.searchByImage.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fadeInAnimation(): Animation {
|
||||||
|
return AlphaAnimation(0f, 1f).apply {
|
||||||
|
duration = 150
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun fadeOutAnimation(): Animation {
|
||||||
|
return AlphaAnimation(1f, 0f).apply {
|
||||||
|
duration = 150
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun updateClearHistoryVisibility() {
|
||||||
|
binding.clearHistory.visibility =
|
||||||
|
if (searchHistoryAdapter.itemCount > 0) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addHistory() {
|
||||||
|
if (::searchHistoryAdapter.isInitialized && binding.searchBarText.text.toString()
|
||||||
|
.isNotBlank()
|
||||||
|
) searchHistoryAdapter.add(binding.searchBarText.text.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class SearchHeaderViewHolder(val binding: ItemSearchHeaderBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = 1
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return itemViewType
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,10 +15,16 @@ import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.connections.anilist.AnilistSearch
|
import ani.dantotsu.connections.anilist.AnilistSearch
|
||||||
import ani.dantotsu.connections.anilist.SearchResults
|
import ani.dantotsu.connections.anilist.AnilistSearch.SearchType
|
||||||
|
import ani.dantotsu.connections.anilist.AniMangaSearchResults
|
||||||
|
import ani.dantotsu.connections.anilist.CharacterSearchResults
|
||||||
|
import ani.dantotsu.connections.anilist.StaffSearchResults
|
||||||
|
import ani.dantotsu.connections.anilist.StudioSearchResults
|
||||||
|
import ani.dantotsu.connections.anilist.UserSearchResults
|
||||||
import ani.dantotsu.databinding.ActivitySearchBinding
|
import ani.dantotsu.databinding.ActivitySearchBinding
|
||||||
import ani.dantotsu.initActivity
|
import ani.dantotsu.initActivity
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
|
import ani.dantotsu.profile.UsersAdapter
|
||||||
import ani.dantotsu.px
|
import ani.dantotsu.px
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
|
@ -35,14 +41,25 @@ class SearchActivity : AppCompatActivity() {
|
||||||
val model: AnilistSearch by viewModels()
|
val model: AnilistSearch by viewModels()
|
||||||
|
|
||||||
var style: Int = 0
|
var style: Int = 0
|
||||||
|
lateinit var searchType: SearchType
|
||||||
private var screenWidth: Float = 0f
|
private var screenWidth: Float = 0f
|
||||||
|
|
||||||
private lateinit var mediaAdaptor: MediaAdaptor
|
private lateinit var mediaAdaptor: MediaAdaptor
|
||||||
|
private lateinit var characterAdaptor: CharacterAdapter
|
||||||
|
private lateinit var studioAdaptor: StudioAdapter
|
||||||
|
private lateinit var staffAdaptor: AuthorAdapter
|
||||||
|
private lateinit var usersAdapter: UsersAdapter
|
||||||
|
|
||||||
private lateinit var progressAdapter: ProgressAdapter
|
private lateinit var progressAdapter: ProgressAdapter
|
||||||
private lateinit var concatAdapter: ConcatAdapter
|
private lateinit var concatAdapter: ConcatAdapter
|
||||||
private lateinit var headerAdaptor: SearchAdapter
|
private lateinit var headerAdaptor: HeaderInterface
|
||||||
|
|
||||||
|
lateinit var aniMangaResult: AniMangaSearchResults
|
||||||
|
lateinit var characterResult: CharacterSearchResults
|
||||||
|
lateinit var studioResult: StudioSearchResults
|
||||||
|
lateinit var staffResult: StaffSearchResults
|
||||||
|
lateinit var userResult: UserSearchResults
|
||||||
|
|
||||||
lateinit var result: SearchResults
|
|
||||||
lateinit var updateChips: (() -> Unit)
|
lateinit var updateChips: (() -> Unit)
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
@ -59,39 +76,117 @@ class SearchActivity : AppCompatActivity() {
|
||||||
bottom = navBarHeight + 80f.px
|
bottom = navBarHeight + 80f.px
|
||||||
)
|
)
|
||||||
|
|
||||||
style = PrefManager.getVal(PrefName.SearchStyle)
|
|
||||||
var listOnly: Boolean? = intent.getBooleanExtra("listOnly", false)
|
|
||||||
if (!listOnly!!) listOnly = null
|
|
||||||
|
|
||||||
val notSet = model.notSet
|
val notSet = model.notSet
|
||||||
if (model.notSet) {
|
searchType = SearchType.fromString(intent.getStringExtra("type") ?: "ANIME")
|
||||||
model.notSet = false
|
when (searchType) {
|
||||||
model.searchResults = SearchResults(
|
SearchType.ANIME, SearchType.MANGA -> {
|
||||||
intent.getStringExtra("type") ?: "ANIME",
|
style = PrefManager.getVal(PrefName.SearchStyle)
|
||||||
isAdult = if (Anilist.adult) intent.getBooleanExtra("hentai", false) else false,
|
var listOnly: Boolean? = intent.getBooleanExtra("listOnly", false)
|
||||||
onList = listOnly,
|
if (!listOnly!!) listOnly = null
|
||||||
search = intent.getStringExtra("query"),
|
|
||||||
genres = intent.getStringExtra("genre")?.let { mutableListOf(it) },
|
if (model.notSet) {
|
||||||
tags = intent.getStringExtra("tag")?.let { mutableListOf(it) },
|
model.notSet = false
|
||||||
sort = intent.getStringExtra("sortBy"),
|
model.aniMangaSearchResults = AniMangaSearchResults(
|
||||||
status = intent.getStringExtra("status"),
|
intent.getStringExtra("type") ?: "ANIME",
|
||||||
source = intent.getStringExtra("source"),
|
isAdult = if (Anilist.adult) intent.getBooleanExtra(
|
||||||
countryOfOrigin = intent.getStringExtra("country"),
|
"hentai",
|
||||||
season = intent.getStringExtra("season"),
|
false
|
||||||
seasonYear = if (intent.getStringExtra("type") == "ANIME") intent.getStringExtra("seasonYear")
|
) else false,
|
||||||
?.toIntOrNull() else null,
|
onList = listOnly,
|
||||||
startYear = if (intent.getStringExtra("type") == "MANGA") intent.getStringExtra("seasonYear")
|
search = intent.getStringExtra("query"),
|
||||||
?.toIntOrNull() else null,
|
genres = intent.getStringExtra("genre")?.let { mutableListOf(it) },
|
||||||
results = mutableListOf(),
|
tags = intent.getStringExtra("tag")?.let { mutableListOf(it) },
|
||||||
hasNextPage = false
|
sort = intent.getStringExtra("sortBy"),
|
||||||
)
|
status = intent.getStringExtra("status"),
|
||||||
|
source = intent.getStringExtra("source"),
|
||||||
|
countryOfOrigin = intent.getStringExtra("country"),
|
||||||
|
season = intent.getStringExtra("season"),
|
||||||
|
seasonYear = if (intent.getStringExtra("type") == "ANIME") intent.getStringExtra(
|
||||||
|
"seasonYear"
|
||||||
|
)
|
||||||
|
?.toIntOrNull() else null,
|
||||||
|
startYear = if (intent.getStringExtra("type") == "MANGA") intent.getStringExtra(
|
||||||
|
"seasonYear"
|
||||||
|
)
|
||||||
|
?.toIntOrNull() else null,
|
||||||
|
results = mutableListOf(),
|
||||||
|
hasNextPage = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
aniMangaResult = model.aniMangaSearchResults
|
||||||
|
mediaAdaptor =
|
||||||
|
MediaAdaptor(
|
||||||
|
style,
|
||||||
|
model.aniMangaSearchResults.results,
|
||||||
|
this,
|
||||||
|
matchParent = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.CHARACTER -> {
|
||||||
|
if (model.notSet) {
|
||||||
|
model.notSet = false
|
||||||
|
model.characterSearchResults = CharacterSearchResults(
|
||||||
|
search = intent.getStringExtra("query"),
|
||||||
|
results = mutableListOf(),
|
||||||
|
hasNextPage = false
|
||||||
|
)
|
||||||
|
|
||||||
|
characterResult = model.characterSearchResults
|
||||||
|
characterAdaptor = CharacterAdapter(model.characterSearchResults.results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.STUDIO -> {
|
||||||
|
if (model.notSet) {
|
||||||
|
model.notSet = false
|
||||||
|
model.studioSearchResults = StudioSearchResults(
|
||||||
|
search = intent.getStringExtra("query"),
|
||||||
|
results = mutableListOf(),
|
||||||
|
hasNextPage = false
|
||||||
|
)
|
||||||
|
|
||||||
|
studioResult = model.studioSearchResults
|
||||||
|
studioAdaptor = StudioAdapter(model.studioSearchResults.results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.STAFF -> {
|
||||||
|
if (model.notSet) {
|
||||||
|
model.notSet = false
|
||||||
|
model.staffSearchResults = StaffSearchResults(
|
||||||
|
search = intent.getStringExtra("query"),
|
||||||
|
results = mutableListOf(),
|
||||||
|
hasNextPage = false
|
||||||
|
)
|
||||||
|
|
||||||
|
staffResult = model.staffSearchResults
|
||||||
|
staffAdaptor = AuthorAdapter(model.staffSearchResults.results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.USER -> {
|
||||||
|
if (model.notSet) {
|
||||||
|
model.notSet = false
|
||||||
|
model.userSearchResults = UserSearchResults(
|
||||||
|
search = intent.getStringExtra("query"),
|
||||||
|
results = mutableListOf(),
|
||||||
|
hasNextPage = false
|
||||||
|
)
|
||||||
|
|
||||||
|
userResult = model.userSearchResults
|
||||||
|
usersAdapter = UsersAdapter(model.userSearchResults.results, grid = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = model.searchResults
|
|
||||||
|
|
||||||
progressAdapter = ProgressAdapter(searched = model.searched)
|
progressAdapter = ProgressAdapter(searched = model.searched)
|
||||||
mediaAdaptor = MediaAdaptor(style, model.searchResults.results, this, matchParent = true)
|
headerAdaptor = if (searchType == SearchType.ANIME || searchType == SearchType.MANGA) {
|
||||||
headerAdaptor = SearchAdapter(this, model.searchResults.type)
|
SearchAdapter(this, searchType)
|
||||||
|
} else {
|
||||||
|
SupportingSearchAdapter(this, searchType)
|
||||||
|
}
|
||||||
|
|
||||||
val gridSize = (screenWidth / 120f).toInt()
|
val gridSize = (screenWidth / 120f).toInt()
|
||||||
val gridLayoutManager = GridLayoutManager(this, gridSize)
|
val gridLayoutManager = GridLayoutManager(this, gridSize)
|
||||||
|
@ -108,7 +203,27 @@ class SearchActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
concatAdapter = ConcatAdapter(headerAdaptor, mediaAdaptor, progressAdapter)
|
concatAdapter = when (searchType) {
|
||||||
|
SearchType.ANIME, SearchType.MANGA -> {
|
||||||
|
ConcatAdapter(headerAdaptor, mediaAdaptor, progressAdapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.CHARACTER -> {
|
||||||
|
ConcatAdapter(headerAdaptor, characterAdaptor, progressAdapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.STUDIO -> {
|
||||||
|
ConcatAdapter(headerAdaptor, studioAdaptor, progressAdapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.STAFF -> {
|
||||||
|
ConcatAdapter(headerAdaptor, staffAdaptor, progressAdapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.USER -> {
|
||||||
|
ConcatAdapter(headerAdaptor, usersAdapter, progressAdapter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
binding.searchRecyclerView.layoutManager = gridLayoutManager
|
binding.searchRecyclerView.layoutManager = gridLayoutManager
|
||||||
binding.searchRecyclerView.adapter = concatAdapter
|
binding.searchRecyclerView.adapter = concatAdapter
|
||||||
|
@ -117,9 +232,9 @@ class SearchActivity : AppCompatActivity() {
|
||||||
RecyclerView.OnScrollListener() {
|
RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(v: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(v: RecyclerView, dx: Int, dy: Int) {
|
||||||
if (!v.canScrollVertically(1)) {
|
if (!v.canScrollVertically(1)) {
|
||||||
if (model.searchResults.hasNextPage && model.searchResults.results.isNotEmpty() && !loading) {
|
if (model.hasNextPage(searchType) && model.resultsIsNotEmpty(searchType) && !loading) {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
model.loadNextPage(model.searchResults)
|
model.loadNextPage(searchType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,34 +242,110 @@ class SearchActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
model.getSearch().observe(this) {
|
when (searchType) {
|
||||||
if (it != null) {
|
SearchType.ANIME, SearchType.MANGA -> {
|
||||||
model.searchResults.apply {
|
model.getSearch<AniMangaSearchResults>(searchType).observe(this) {
|
||||||
onList = it.onList
|
if (it != null) {
|
||||||
isAdult = it.isAdult
|
model.aniMangaSearchResults.apply {
|
||||||
perPage = it.perPage
|
onList = it.onList
|
||||||
search = it.search
|
isAdult = it.isAdult
|
||||||
sort = it.sort
|
perPage = it.perPage
|
||||||
genres = it.genres
|
search = it.search
|
||||||
excludedGenres = it.excludedGenres
|
sort = it.sort
|
||||||
excludedTags = it.excludedTags
|
genres = it.genres
|
||||||
tags = it.tags
|
excludedGenres = it.excludedGenres
|
||||||
season = it.season
|
excludedTags = it.excludedTags
|
||||||
startYear = it.startYear
|
tags = it.tags
|
||||||
seasonYear = it.seasonYear
|
season = it.season
|
||||||
status = it.status
|
startYear = it.startYear
|
||||||
source = it.source
|
seasonYear = it.seasonYear
|
||||||
format = it.format
|
status = it.status
|
||||||
countryOfOrigin = it.countryOfOrigin
|
source = it.source
|
||||||
page = it.page
|
format = it.format
|
||||||
hasNextPage = it.hasNextPage
|
countryOfOrigin = it.countryOfOrigin
|
||||||
|
page = it.page
|
||||||
|
hasNextPage = it.hasNextPage
|
||||||
|
}
|
||||||
|
|
||||||
|
val prev = model.aniMangaSearchResults.results.size
|
||||||
|
model.aniMangaSearchResults.results.addAll(it.results)
|
||||||
|
mediaAdaptor.notifyItemRangeInserted(prev, it.results.size)
|
||||||
|
|
||||||
|
progressAdapter.bar?.isVisible = it.hasNextPage
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val prev = model.searchResults.results.size
|
SearchType.CHARACTER -> {
|
||||||
model.searchResults.results.addAll(it.results)
|
model.getSearch<CharacterSearchResults>(searchType).observe(this) {
|
||||||
mediaAdaptor.notifyItemRangeInserted(prev, it.results.size)
|
if (it != null) {
|
||||||
|
model.characterSearchResults.apply {
|
||||||
|
search = it.search
|
||||||
|
page = it.page
|
||||||
|
hasNextPage = it.hasNextPage
|
||||||
|
}
|
||||||
|
|
||||||
progressAdapter.bar?.isVisible = it.hasNextPage
|
val prev = model.characterSearchResults.results.size
|
||||||
|
model.characterSearchResults.results.addAll(it.results)
|
||||||
|
characterAdaptor.notifyItemRangeInserted(prev, it.results.size)
|
||||||
|
|
||||||
|
progressAdapter.bar?.isVisible = it.hasNextPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.STUDIO -> {
|
||||||
|
model.getSearch<StudioSearchResults>(searchType).observe(this) {
|
||||||
|
if (it != null) {
|
||||||
|
model.studioSearchResults.apply {
|
||||||
|
search = it.search
|
||||||
|
page = it.page
|
||||||
|
hasNextPage = it.hasNextPage
|
||||||
|
}
|
||||||
|
|
||||||
|
val prev = model.studioSearchResults.results.size
|
||||||
|
model.studioSearchResults.results.addAll(it.results)
|
||||||
|
studioAdaptor.notifyItemRangeInserted(prev, it.results.size)
|
||||||
|
|
||||||
|
progressAdapter.bar?.isVisible = it.hasNextPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.STAFF -> {
|
||||||
|
model.getSearch<StaffSearchResults>(searchType).observe(this) {
|
||||||
|
if (it != null) {
|
||||||
|
model.staffSearchResults.apply {
|
||||||
|
search = it.search
|
||||||
|
page = it.page
|
||||||
|
hasNextPage = it.hasNextPage
|
||||||
|
}
|
||||||
|
|
||||||
|
val prev = model.staffSearchResults.results.size
|
||||||
|
model.staffSearchResults.results.addAll(it.results)
|
||||||
|
staffAdaptor.notifyItemRangeInserted(prev, it.results.size)
|
||||||
|
|
||||||
|
progressAdapter.bar?.isVisible = it.hasNextPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.USER -> {
|
||||||
|
model.getSearch<UserSearchResults>(searchType).observe(this) {
|
||||||
|
if (it != null) {
|
||||||
|
model.userSearchResults.apply {
|
||||||
|
search = it.search
|
||||||
|
page = it.page
|
||||||
|
hasNextPage = it.hasNextPage
|
||||||
|
}
|
||||||
|
|
||||||
|
val prev = model.userSearchResults.results.size
|
||||||
|
model.userSearchResults.results.addAll(it.results)
|
||||||
|
usersAdapter.notifyItemRangeInserted(prev, it.results.size)
|
||||||
|
|
||||||
|
progressAdapter.bar?.isVisible = it.hasNextPage
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,8 +370,32 @@ class SearchActivity : AppCompatActivity() {
|
||||||
fun emptyMediaAdapter() {
|
fun emptyMediaAdapter() {
|
||||||
searchTimer.cancel()
|
searchTimer.cancel()
|
||||||
searchTimer.purge()
|
searchTimer.purge()
|
||||||
mediaAdaptor.notifyItemRangeRemoved(0, model.searchResults.results.size)
|
when (searchType) {
|
||||||
model.searchResults.results.clear()
|
SearchType.ANIME, SearchType.MANGA -> {
|
||||||
|
mediaAdaptor.notifyItemRangeRemoved(0, model.aniMangaSearchResults.results.size)
|
||||||
|
model.aniMangaSearchResults.results.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.CHARACTER -> {
|
||||||
|
characterAdaptor.notifyItemRangeRemoved(0, model.characterSearchResults.results.size)
|
||||||
|
model.characterSearchResults.results.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.STUDIO -> {
|
||||||
|
studioAdaptor.notifyItemRangeRemoved(0, model.studioSearchResults.results.size)
|
||||||
|
model.studioSearchResults.results.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.STAFF -> {
|
||||||
|
staffAdaptor.notifyItemRangeRemoved(0, model.staffSearchResults.results.size)
|
||||||
|
model.staffSearchResults.results.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.USER -> {
|
||||||
|
usersAdapter.notifyItemRangeRemoved(0, model.userSearchResults.results.size)
|
||||||
|
model.userSearchResults.results.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
progressAdapter.bar?.visibility = View.GONE
|
progressAdapter.bar?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,10 +403,30 @@ class SearchActivity : AppCompatActivity() {
|
||||||
private var loading = false
|
private var loading = false
|
||||||
fun search() {
|
fun search() {
|
||||||
headerAdaptor.setHistoryVisibility(false)
|
headerAdaptor.setHistoryVisibility(false)
|
||||||
val size = model.searchResults.results.size
|
val size = model.size(searchType)
|
||||||
model.searchResults.results.clear()
|
model.clearResults(searchType)
|
||||||
binding.searchRecyclerView.post {
|
binding.searchRecyclerView.post {
|
||||||
mediaAdaptor.notifyItemRangeRemoved(0, size)
|
when (searchType) {
|
||||||
|
SearchType.ANIME, SearchType.MANGA -> {
|
||||||
|
mediaAdaptor.notifyItemRangeRemoved(0, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.CHARACTER -> {
|
||||||
|
characterAdaptor.notifyItemRangeRemoved(0, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.STUDIO -> {
|
||||||
|
studioAdaptor.notifyItemRangeRemoved(0, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.STAFF -> {
|
||||||
|
staffAdaptor.notifyItemRangeRemoved(0, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.USER -> {
|
||||||
|
usersAdapter.notifyItemRangeRemoved(0, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
progressAdapter.bar?.visibility = View.VISIBLE
|
progressAdapter.bar?.visibility = View.VISIBLE
|
||||||
|
@ -202,7 +437,7 @@ class SearchActivity : AppCompatActivity() {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
loading = true
|
loading = true
|
||||||
model.loadSearch(result)
|
model.loadSearch(searchType)
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,8 +448,10 @@ class SearchActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
fun recycler() {
|
fun recycler() {
|
||||||
mediaAdaptor.type = style
|
if (searchType == SearchType.ANIME || searchType == SearchType.MANGA) {
|
||||||
mediaAdaptor.notifyDataSetChanged()
|
mediaAdaptor.type = style
|
||||||
|
mediaAdaptor.notifyDataSetChanged()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var state: Parcelable? = null
|
var state: Parcelable? = null
|
||||||
|
|
|
@ -9,8 +9,6 @@ import android.view.LayoutInflater
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.animation.AlphaAnimation
|
|
||||||
import android.view.animation.Animation
|
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.PopupMenu
|
import android.widget.PopupMenu
|
||||||
|
@ -22,8 +20,8 @@ import androidx.recyclerview.widget.RecyclerView.HORIZONTAL
|
||||||
import ani.dantotsu.App.Companion.context
|
import ani.dantotsu.App.Companion.context
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
|
import ani.dantotsu.connections.anilist.AnilistSearch.SearchType
|
||||||
import ani.dantotsu.databinding.ItemChipBinding
|
import ani.dantotsu.databinding.ItemChipBinding
|
||||||
import ani.dantotsu.databinding.ItemSearchHeaderBinding
|
|
||||||
import ani.dantotsu.openLinkInBrowser
|
import ani.dantotsu.openLinkInBrowser
|
||||||
import ani.dantotsu.others.imagesearch.ImageSearchActivity
|
import ani.dantotsu.others.imagesearch.ImageSearchActivity
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
@ -36,18 +34,11 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class SearchAdapter(private val activity: SearchActivity, private val type: SearchType) :
|
||||||
class SearchAdapter(private val activity: SearchActivity, private val type: String) :
|
HeaderInterface() {
|
||||||
RecyclerView.Adapter<SearchAdapter.SearchHeaderViewHolder>() {
|
|
||||||
private val itemViewType = 6969
|
|
||||||
var search: Runnable? = null
|
|
||||||
var requestFocus: Runnable? = null
|
|
||||||
private var textWatcher: TextWatcher? = null
|
|
||||||
private lateinit var searchHistoryAdapter: SearchHistoryAdapter
|
|
||||||
private lateinit var binding: ItemSearchHeaderBinding
|
|
||||||
|
|
||||||
private fun updateFilterTextViewDrawable() {
|
private fun updateFilterTextViewDrawable() {
|
||||||
val filterDrawable = when (activity.result.sort) {
|
val filterDrawable = when (activity.aniMangaResult.sort) {
|
||||||
Anilist.sortBy[0] -> R.drawable.ic_round_area_chart_24
|
Anilist.sortBy[0] -> R.drawable.ic_round_area_chart_24
|
||||||
Anilist.sortBy[1] -> R.drawable.ic_round_filter_peak_24
|
Anilist.sortBy[1] -> R.drawable.ic_round_filter_peak_24
|
||||||
Anilist.sortBy[2] -> R.drawable.ic_round_star_graph_24
|
Anilist.sortBy[2] -> R.drawable.ic_round_star_graph_24
|
||||||
|
@ -60,12 +51,6 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
|
||||||
binding.filterTextView.setCompoundDrawablesWithIntrinsicBounds(filterDrawable, 0, 0, 0)
|
binding.filterTextView.setCompoundDrawablesWithIntrinsicBounds(filterDrawable, 0, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHeaderViewHolder {
|
|
||||||
val binding =
|
|
||||||
ItemSearchHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
|
||||||
return SearchHeaderViewHolder(binding)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
override fun onBindViewHolder(holder: SearchHeaderViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: SearchHeaderViewHolder, position: Int) {
|
||||||
binding = holder.binding
|
binding = holder.binding
|
||||||
|
@ -79,6 +64,10 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
|
||||||
val imm: InputMethodManager =
|
val imm: InputMethodManager =
|
||||||
activity.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
activity.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
|
||||||
|
if (activity.searchType != SearchType.MANGA && activity.searchType != SearchType.ANIME) {
|
||||||
|
throw IllegalArgumentException("Invalid search type (wrong adapter)")
|
||||||
|
}
|
||||||
|
|
||||||
when (activity.style) {
|
when (activity.style) {
|
||||||
0 -> {
|
0 -> {
|
||||||
binding.searchResultGrid.alpha = 1f
|
binding.searchResultGrid.alpha = 1f
|
||||||
|
@ -91,7 +80,7 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.searchBar.hint = activity.result.type
|
binding.searchBar.hint = activity.aniMangaResult.type
|
||||||
if (PrefManager.getVal(PrefName.Incognito)) {
|
if (PrefManager.getVal(PrefName.Incognito)) {
|
||||||
val startIconDrawableRes = R.drawable.ic_incognito_24
|
val startIconDrawableRes = R.drawable.ic_incognito_24
|
||||||
val startIconDrawable: Drawable? =
|
val startIconDrawable: Drawable? =
|
||||||
|
@ -99,11 +88,11 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
|
||||||
binding.searchBar.startIconDrawable = startIconDrawable
|
binding.searchBar.startIconDrawable = startIconDrawable
|
||||||
}
|
}
|
||||||
|
|
||||||
var adult = activity.result.isAdult
|
var adult = activity.aniMangaResult.isAdult
|
||||||
var listOnly = activity.result.onList
|
var listOnly = activity.aniMangaResult.onList
|
||||||
|
|
||||||
binding.searchBarText.removeTextChangedListener(textWatcher)
|
binding.searchBarText.removeTextChangedListener(textWatcher)
|
||||||
binding.searchBarText.setText(activity.result.search)
|
binding.searchBarText.setText(activity.aniMangaResult.search)
|
||||||
|
|
||||||
binding.searchAdultCheck.isChecked = adult
|
binding.searchAdultCheck.isChecked = adult
|
||||||
binding.searchList.isChecked = listOnly == true
|
binding.searchList.isChecked = listOnly == true
|
||||||
|
@ -124,49 +113,49 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
|
||||||
popupMenu.setOnMenuItemClickListener { item ->
|
popupMenu.setOnMenuItemClickListener { item ->
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.sort_by_score -> {
|
R.id.sort_by_score -> {
|
||||||
activity.result.sort = Anilist.sortBy[0]
|
activity.aniMangaResult.sort = Anilist.sortBy[0]
|
||||||
activity.updateChips.invoke()
|
activity.updateChips.invoke()
|
||||||
activity.search()
|
activity.search()
|
||||||
updateFilterTextViewDrawable()
|
updateFilterTextViewDrawable()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.sort_by_popular -> {
|
R.id.sort_by_popular -> {
|
||||||
activity.result.sort = Anilist.sortBy[1]
|
activity.aniMangaResult.sort = Anilist.sortBy[1]
|
||||||
activity.updateChips.invoke()
|
activity.updateChips.invoke()
|
||||||
activity.search()
|
activity.search()
|
||||||
updateFilterTextViewDrawable()
|
updateFilterTextViewDrawable()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.sort_by_trending -> {
|
R.id.sort_by_trending -> {
|
||||||
activity.result.sort = Anilist.sortBy[2]
|
activity.aniMangaResult.sort = Anilist.sortBy[2]
|
||||||
activity.updateChips.invoke()
|
activity.updateChips.invoke()
|
||||||
activity.search()
|
activity.search()
|
||||||
updateFilterTextViewDrawable()
|
updateFilterTextViewDrawable()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.sort_by_recent -> {
|
R.id.sort_by_recent -> {
|
||||||
activity.result.sort = Anilist.sortBy[3]
|
activity.aniMangaResult.sort = Anilist.sortBy[3]
|
||||||
activity.updateChips.invoke()
|
activity.updateChips.invoke()
|
||||||
activity.search()
|
activity.search()
|
||||||
updateFilterTextViewDrawable()
|
updateFilterTextViewDrawable()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.sort_by_a_z -> {
|
R.id.sort_by_a_z -> {
|
||||||
activity.result.sort = Anilist.sortBy[4]
|
activity.aniMangaResult.sort = Anilist.sortBy[4]
|
||||||
activity.updateChips.invoke()
|
activity.updateChips.invoke()
|
||||||
activity.search()
|
activity.search()
|
||||||
updateFilterTextViewDrawable()
|
updateFilterTextViewDrawable()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.sort_by_z_a -> {
|
R.id.sort_by_z_a -> {
|
||||||
activity.result.sort = Anilist.sortBy[5]
|
activity.aniMangaResult.sort = Anilist.sortBy[5]
|
||||||
activity.updateChips.invoke()
|
activity.updateChips.invoke()
|
||||||
activity.search()
|
activity.search()
|
||||||
updateFilterTextViewDrawable()
|
updateFilterTextViewDrawable()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.sort_by_pure_pain -> {
|
R.id.sort_by_pure_pain -> {
|
||||||
activity.result.sort = Anilist.sortBy[6]
|
activity.aniMangaResult.sort = Anilist.sortBy[6]
|
||||||
activity.updateChips.invoke()
|
activity.updateChips.invoke()
|
||||||
activity.search()
|
activity.search()
|
||||||
updateFilterTextViewDrawable()
|
updateFilterTextViewDrawable()
|
||||||
|
@ -177,7 +166,7 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
|
||||||
popupMenu.show()
|
popupMenu.show()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
if (activity.result.type != "ANIME") {
|
if (activity.aniMangaResult.type != "ANIME") {
|
||||||
binding.searchByImage.visibility = View.GONE
|
binding.searchByImage.visibility = View.GONE
|
||||||
}
|
}
|
||||||
binding.searchByImage.setOnClickListener {
|
binding.searchByImage.setOnClickListener {
|
||||||
|
@ -190,7 +179,7 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
|
||||||
}
|
}
|
||||||
updateClearHistoryVisibility()
|
updateClearHistoryVisibility()
|
||||||
fun searchTitle() {
|
fun searchTitle() {
|
||||||
activity.result.apply {
|
activity.aniMangaResult.apply {
|
||||||
search =
|
search =
|
||||||
if (binding.searchBarText.text.toString() != "") binding.searchBarText.text.toString() else null
|
if (binding.searchBarText.text.toString() != "") binding.searchBarText.text.toString() else null
|
||||||
onList = listOnly
|
onList = listOnly
|
||||||
|
@ -292,67 +281,12 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
|
||||||
requestFocus = Runnable { binding.searchBarText.requestFocus() }
|
requestFocus = Runnable { binding.searchBarText.requestFocus() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setHistoryVisibility(visible: Boolean) {
|
|
||||||
if (visible) {
|
|
||||||
binding.searchResultLayout.startAnimation(fadeOutAnimation())
|
|
||||||
binding.searchHistoryList.startAnimation(fadeInAnimation())
|
|
||||||
binding.searchResultLayout.visibility = View.GONE
|
|
||||||
binding.searchHistoryList.visibility = View.VISIBLE
|
|
||||||
binding.searchByImage.visibility = View.VISIBLE
|
|
||||||
} else {
|
|
||||||
if (binding.searchResultLayout.visibility != View.VISIBLE) {
|
|
||||||
binding.searchResultLayout.startAnimation(fadeInAnimation())
|
|
||||||
binding.searchHistoryList.startAnimation(fadeOutAnimation())
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.searchResultLayout.visibility = View.VISIBLE
|
|
||||||
binding.clearHistory.visibility = View.GONE
|
|
||||||
binding.searchHistoryList.visibility = View.GONE
|
|
||||||
binding.searchByImage.visibility = View.GONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateClearHistoryVisibility() {
|
|
||||||
binding.clearHistory.visibility =
|
|
||||||
if (searchHistoryAdapter.itemCount > 0) View.VISIBLE else View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fadeInAnimation(): Animation {
|
|
||||||
return AlphaAnimation(0f, 1f).apply {
|
|
||||||
duration = 150
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fadeOutAnimation(): Animation {
|
|
||||||
return AlphaAnimation(1f, 0f).apply {
|
|
||||||
duration = 150
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun addHistory() {
|
|
||||||
if (::searchHistoryAdapter.isInitialized &&
|
|
||||||
binding.searchBarText.text.toString().isNotBlank()
|
|
||||||
)
|
|
||||||
searchHistoryAdapter.add(binding.searchBarText.text.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int = 1
|
|
||||||
|
|
||||||
inner class SearchHeaderViewHolder(val binding: ItemSearchHeaderBinding) :
|
|
||||||
RecyclerView.ViewHolder(binding.root)
|
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
|
||||||
return itemViewType
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class SearchChipAdapter(
|
class SearchChipAdapter(
|
||||||
val activity: SearchActivity,
|
val activity: SearchActivity,
|
||||||
private val searchAdapter: SearchAdapter
|
private val searchAdapter: SearchAdapter
|
||||||
) :
|
) :
|
||||||
RecyclerView.Adapter<SearchChipAdapter.SearchChipViewHolder>() {
|
RecyclerView.Adapter<SearchChipAdapter.SearchChipViewHolder>() {
|
||||||
private var chips = activity.result.toChipList()
|
private var chips = activity.aniMangaResult.toChipList()
|
||||||
|
|
||||||
inner class SearchChipViewHolder(val binding: ItemChipBinding) :
|
inner class SearchChipViewHolder(val binding: ItemChipBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root)
|
RecyclerView.ViewHolder(binding.root)
|
||||||
|
@ -369,7 +303,7 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
|
||||||
holder.binding.root.apply {
|
holder.binding.root.apply {
|
||||||
text = chip.text.replace("_", " ")
|
text = chip.text.replace("_", " ")
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
activity.result.removeChip(chip)
|
activity.aniMangaResult.removeChip(chip)
|
||||||
update()
|
update()
|
||||||
activity.search()
|
activity.search()
|
||||||
searchAdapter.updateFilterTextViewDrawable()
|
searchAdapter.updateFilterTextViewDrawable()
|
||||||
|
@ -379,7 +313,7 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
fun update() {
|
fun update() {
|
||||||
chips = activity.result.toChipList()
|
chips = activity.aniMangaResult.toChipList()
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
searchAdapter.updateFilterTextViewDrawable()
|
searchAdapter.updateFilterTextViewDrawable()
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setSortByFilterImage() {
|
private fun setSortByFilterImage() {
|
||||||
val filterDrawable = when (activity.result.sort) {
|
val filterDrawable = when (activity.aniMangaResult.sort) {
|
||||||
Anilist.sortBy[0] -> R.drawable.ic_round_area_chart_24
|
Anilist.sortBy[0] -> R.drawable.ic_round_area_chart_24
|
||||||
Anilist.sortBy[1] -> R.drawable.ic_round_filter_peak_24
|
Anilist.sortBy[1] -> R.drawable.ic_round_filter_peak_24
|
||||||
Anilist.sortBy[2] -> R.drawable.ic_round_star_graph_24
|
Anilist.sortBy[2] -> R.drawable.ic_round_star_graph_24
|
||||||
|
@ -71,10 +71,10 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resetSearchFilter() {
|
private fun resetSearchFilter() {
|
||||||
activity.result.sort = null
|
activity.aniMangaResult.sort = null
|
||||||
binding.sortByFilter.setImageResource(R.drawable.ic_round_filter_alt_24)
|
binding.sortByFilter.setImageResource(R.drawable.ic_round_filter_alt_24)
|
||||||
startBounceZoomAnimation(binding.sortByFilter)
|
startBounceZoomAnimation(binding.sortByFilter)
|
||||||
activity.result.countryOfOrigin = null
|
activity.aniMangaResult.countryOfOrigin = null
|
||||||
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_search_googlefonts)
|
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_search_googlefonts)
|
||||||
startBounceZoomAnimation(binding.countryFilter)
|
startBounceZoomAnimation(binding.countryFilter)
|
||||||
|
|
||||||
|
@ -98,10 +98,10 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
activity = requireActivity() as SearchActivity
|
activity = requireActivity() as SearchActivity
|
||||||
|
|
||||||
selectedGenres = activity.result.genres ?: mutableListOf()
|
selectedGenres = activity.aniMangaResult.genres ?: mutableListOf()
|
||||||
exGenres = activity.result.excludedGenres ?: mutableListOf()
|
exGenres = activity.aniMangaResult.excludedGenres ?: mutableListOf()
|
||||||
selectedTags = activity.result.tags ?: mutableListOf()
|
selectedTags = activity.aniMangaResult.tags ?: mutableListOf()
|
||||||
exTags = activity.result.excludedTags ?: mutableListOf()
|
exTags = activity.aniMangaResult.excludedTags ?: mutableListOf()
|
||||||
setSortByFilterImage()
|
setSortByFilterImage()
|
||||||
|
|
||||||
binding.resetSearchFilter.setOnClickListener {
|
binding.resetSearchFilter.setOnClickListener {
|
||||||
|
@ -126,7 +126,7 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
resetSearchFilter()
|
resetSearchFilter()
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
activity.result.apply {
|
activity.aniMangaResult.apply {
|
||||||
status =
|
status =
|
||||||
binding.searchStatus.text.toString().replace(" ", "_").ifBlank { null }
|
binding.searchStatus.text.toString().replace(" ", "_").ifBlank { null }
|
||||||
source =
|
source =
|
||||||
|
@ -135,7 +135,7 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
season = binding.searchSeason.text.toString().ifBlank { null }
|
season = binding.searchSeason.text.toString().ifBlank { null }
|
||||||
startYear = binding.searchYear.text.toString().toIntOrNull()
|
startYear = binding.searchYear.text.toString().toIntOrNull()
|
||||||
seasonYear = binding.searchYear.text.toString().toIntOrNull()
|
seasonYear = binding.searchYear.text.toString().toIntOrNull()
|
||||||
sort = activity.result.sort
|
sort = activity.aniMangaResult.sort
|
||||||
genres = selectedGenres
|
genres = selectedGenres
|
||||||
tags = selectedTags
|
tags = selectedTags
|
||||||
excludedGenres = exGenres
|
excludedGenres = exGenres
|
||||||
|
@ -155,43 +155,43 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
popupMenu.setOnMenuItemClickListener { menuItem ->
|
popupMenu.setOnMenuItemClickListener { menuItem ->
|
||||||
when (menuItem.itemId) {
|
when (menuItem.itemId) {
|
||||||
R.id.sort_by_score -> {
|
R.id.sort_by_score -> {
|
||||||
activity.result.sort = Anilist.sortBy[0]
|
activity.aniMangaResult.sort = Anilist.sortBy[0]
|
||||||
binding.sortByFilter.setImageResource(R.drawable.ic_round_area_chart_24)
|
binding.sortByFilter.setImageResource(R.drawable.ic_round_area_chart_24)
|
||||||
startBounceZoomAnimation()
|
startBounceZoomAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.sort_by_popular -> {
|
R.id.sort_by_popular -> {
|
||||||
activity.result.sort = Anilist.sortBy[1]
|
activity.aniMangaResult.sort = Anilist.sortBy[1]
|
||||||
binding.sortByFilter.setImageResource(R.drawable.ic_round_filter_peak_24)
|
binding.sortByFilter.setImageResource(R.drawable.ic_round_filter_peak_24)
|
||||||
startBounceZoomAnimation()
|
startBounceZoomAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.sort_by_trending -> {
|
R.id.sort_by_trending -> {
|
||||||
activity.result.sort = Anilist.sortBy[2]
|
activity.aniMangaResult.sort = Anilist.sortBy[2]
|
||||||
binding.sortByFilter.setImageResource(R.drawable.ic_round_star_graph_24)
|
binding.sortByFilter.setImageResource(R.drawable.ic_round_star_graph_24)
|
||||||
startBounceZoomAnimation()
|
startBounceZoomAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.sort_by_recent -> {
|
R.id.sort_by_recent -> {
|
||||||
activity.result.sort = Anilist.sortBy[3]
|
activity.aniMangaResult.sort = Anilist.sortBy[3]
|
||||||
binding.sortByFilter.setImageResource(R.drawable.ic_round_new_releases_24)
|
binding.sortByFilter.setImageResource(R.drawable.ic_round_new_releases_24)
|
||||||
startBounceZoomAnimation()
|
startBounceZoomAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.sort_by_a_z -> {
|
R.id.sort_by_a_z -> {
|
||||||
activity.result.sort = Anilist.sortBy[4]
|
activity.aniMangaResult.sort = Anilist.sortBy[4]
|
||||||
binding.sortByFilter.setImageResource(R.drawable.ic_round_filter_list_24)
|
binding.sortByFilter.setImageResource(R.drawable.ic_round_filter_list_24)
|
||||||
startBounceZoomAnimation()
|
startBounceZoomAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.sort_by_z_a -> {
|
R.id.sort_by_z_a -> {
|
||||||
activity.result.sort = Anilist.sortBy[5]
|
activity.aniMangaResult.sort = Anilist.sortBy[5]
|
||||||
binding.sortByFilter.setImageResource(R.drawable.ic_round_filter_list_24_reverse)
|
binding.sortByFilter.setImageResource(R.drawable.ic_round_filter_list_24_reverse)
|
||||||
startBounceZoomAnimation()
|
startBounceZoomAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.sort_by_pure_pain -> {
|
R.id.sort_by_pure_pain -> {
|
||||||
activity.result.sort = Anilist.sortBy[6]
|
activity.aniMangaResult.sort = Anilist.sortBy[6]
|
||||||
binding.sortByFilter.setImageResource(R.drawable.ic_round_assist_walker_24)
|
binding.sortByFilter.setImageResource(R.drawable.ic_round_assist_walker_24)
|
||||||
startBounceZoomAnimation()
|
startBounceZoomAnimation()
|
||||||
}
|
}
|
||||||
|
@ -212,25 +212,25 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.country_china -> {
|
R.id.country_china -> {
|
||||||
activity.result.countryOfOrigin = "CN"
|
activity.aniMangaResult.countryOfOrigin = "CN"
|
||||||
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_china_googlefonts)
|
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_china_googlefonts)
|
||||||
startBounceZoomAnimation(binding.countryFilter)
|
startBounceZoomAnimation(binding.countryFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.country_south_korea -> {
|
R.id.country_south_korea -> {
|
||||||
activity.result.countryOfOrigin = "KR"
|
activity.aniMangaResult.countryOfOrigin = "KR"
|
||||||
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_south_korea_googlefonts)
|
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_south_korea_googlefonts)
|
||||||
startBounceZoomAnimation(binding.countryFilter)
|
startBounceZoomAnimation(binding.countryFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.country_japan -> {
|
R.id.country_japan -> {
|
||||||
activity.result.countryOfOrigin = "JP"
|
activity.aniMangaResult.countryOfOrigin = "JP"
|
||||||
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_japan_googlefonts)
|
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_japan_googlefonts)
|
||||||
startBounceZoomAnimation(binding.countryFilter)
|
startBounceZoomAnimation(binding.countryFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.country_taiwan -> {
|
R.id.country_taiwan -> {
|
||||||
activity.result.countryOfOrigin = "TW"
|
activity.aniMangaResult.countryOfOrigin = "TW"
|
||||||
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_taiwan_googlefonts)
|
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_taiwan_googlefonts)
|
||||||
startBounceZoomAnimation(binding.countryFilter)
|
startBounceZoomAnimation(binding.countryFilter)
|
||||||
}
|
}
|
||||||
|
@ -241,18 +241,18 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.searchFilterApply.setOnClickListener {
|
binding.searchFilterApply.setOnClickListener {
|
||||||
activity.result.apply {
|
activity.aniMangaResult.apply {
|
||||||
status = binding.searchStatus.text.toString().replace(" ", "_").ifBlank { null }
|
status = binding.searchStatus.text.toString().replace(" ", "_").ifBlank { null }
|
||||||
source = binding.searchSource.text.toString().replace(" ", "_").ifBlank { null }
|
source = binding.searchSource.text.toString().replace(" ", "_").ifBlank { null }
|
||||||
format = binding.searchFormat.text.toString().ifBlank { null }
|
format = binding.searchFormat.text.toString().ifBlank { null }
|
||||||
season = binding.searchSeason.text.toString().ifBlank { null }
|
season = binding.searchSeason.text.toString().ifBlank { null }
|
||||||
if (activity.result.type == "ANIME") {
|
if (activity.aniMangaResult.type == "ANIME") {
|
||||||
seasonYear = binding.searchYear.text.toString().toIntOrNull()
|
seasonYear = binding.searchYear.text.toString().toIntOrNull()
|
||||||
} else {
|
} else {
|
||||||
startYear = binding.searchYear.text.toString().toIntOrNull()
|
startYear = binding.searchYear.text.toString().toIntOrNull()
|
||||||
}
|
}
|
||||||
sort = activity.result.sort
|
sort = activity.aniMangaResult.sort
|
||||||
countryOfOrigin = activity.result.countryOfOrigin
|
countryOfOrigin = activity.aniMangaResult.countryOfOrigin
|
||||||
genres = selectedGenres
|
genres = selectedGenres
|
||||||
tags = selectedTags
|
tags = selectedTags
|
||||||
excludedGenres = exGenres
|
excludedGenres = exGenres
|
||||||
|
@ -266,8 +266,8 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
val format =
|
val format =
|
||||||
if (activity.result.type == "ANIME") Anilist.animeStatus else Anilist.mangaStatus
|
if (activity.aniMangaResult.type == "ANIME") Anilist.animeStatus else Anilist.mangaStatus
|
||||||
binding.searchStatus.setText(activity.result.status?.replace("_", " "))
|
binding.searchStatus.setText(activity.aniMangaResult.status?.replace("_", " "))
|
||||||
binding.searchStatus.setAdapter(
|
binding.searchStatus.setAdapter(
|
||||||
ArrayAdapter(
|
ArrayAdapter(
|
||||||
binding.root.context,
|
binding.root.context,
|
||||||
|
@ -276,7 +276,7 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.searchSource.setText(activity.result.source?.replace("_", " "))
|
binding.searchSource.setText(activity.aniMangaResult.source?.replace("_", " "))
|
||||||
binding.searchSource.setAdapter(
|
binding.searchSource.setAdapter(
|
||||||
ArrayAdapter(
|
ArrayAdapter(
|
||||||
binding.root.context,
|
binding.root.context,
|
||||||
|
@ -285,19 +285,19 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.searchFormat.setText(activity.result.format)
|
binding.searchFormat.setText(activity.aniMangaResult.format)
|
||||||
binding.searchFormat.setAdapter(
|
binding.searchFormat.setAdapter(
|
||||||
ArrayAdapter(
|
ArrayAdapter(
|
||||||
binding.root.context,
|
binding.root.context,
|
||||||
R.layout.item_dropdown,
|
R.layout.item_dropdown,
|
||||||
(if (activity.result.type == "ANIME") Anilist.animeFormats else Anilist.mangaFormats).toTypedArray()
|
(if (activity.aniMangaResult.type == "ANIME") Anilist.animeFormats else Anilist.mangaFormats).toTypedArray()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (activity.result.type == "ANIME") {
|
if (activity.aniMangaResult.type == "ANIME") {
|
||||||
binding.searchYear.setText(activity.result.seasonYear?.toString())
|
binding.searchYear.setText(activity.aniMangaResult.seasonYear?.toString())
|
||||||
} else {
|
} else {
|
||||||
binding.searchYear.setText(activity.result.startYear?.toString())
|
binding.searchYear.setText(activity.aniMangaResult.startYear?.toString())
|
||||||
}
|
}
|
||||||
binding.searchYear.setAdapter(
|
binding.searchYear.setAdapter(
|
||||||
ArrayAdapter(
|
ArrayAdapter(
|
||||||
|
@ -308,9 +308,9 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (activity.result.type == "MANGA") binding.searchSeasonCont.visibility = GONE
|
if (activity.aniMangaResult.type == "MANGA") binding.searchSeasonCont.visibility = GONE
|
||||||
else {
|
else {
|
||||||
binding.searchSeason.setText(activity.result.season)
|
binding.searchSeason.setText(activity.aniMangaResult.season)
|
||||||
binding.searchSeason.setAdapter(
|
binding.searchSeason.setAdapter(
|
||||||
ArrayAdapter(
|
ArrayAdapter(
|
||||||
binding.root.context,
|
binding.root.context,
|
||||||
|
@ -346,7 +346,7 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
binding.searchGenresGrid.isChecked = false
|
binding.searchGenresGrid.isChecked = false
|
||||||
|
|
||||||
binding.searchFilterTags.adapter =
|
binding.searchFilterTags.adapter =
|
||||||
FilterChipAdapter(Anilist.tags?.get(activity.result.isAdult) ?: listOf()) { chip ->
|
FilterChipAdapter(Anilist.tags?.get(activity.aniMangaResult.isAdult) ?: listOf()) { chip ->
|
||||||
val tag = chip.text.toString()
|
val tag = chip.text.toString()
|
||||||
chip.isChecked = selectedTags.contains(tag)
|
chip.isChecked = selectedTags.contains(tag)
|
||||||
chip.isCloseIconVisible = exTags.contains(tag)
|
chip.isCloseIconVisible = exTags.contains(tag)
|
||||||
|
|
|
@ -7,23 +7,26 @@ import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
|
import ani.dantotsu.connections.anilist.AnilistSearch.SearchType
|
||||||
import ani.dantotsu.databinding.ItemSearchHistoryBinding
|
import ani.dantotsu.databinding.ItemSearchHistoryBinding
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefManager.asLiveStringSet
|
import ani.dantotsu.settings.saving.PrefManager.asLiveStringSet
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.settings.saving.SharedPreferenceStringSetLiveData
|
import ani.dantotsu.settings.saving.SharedPreferenceStringSetLiveData
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class SearchHistoryAdapter(private val type: String, private val searchClicked: (String) -> Unit) :
|
class SearchHistoryAdapter(type: SearchType, private val searchClicked: (String) -> Unit) :
|
||||||
ListAdapter<String, SearchHistoryAdapter.SearchHistoryViewHolder>(
|
ListAdapter<String, SearchHistoryAdapter.SearchHistoryViewHolder>(
|
||||||
DIFF_CALLBACK_INSTALLED
|
DIFF_CALLBACK_INSTALLED
|
||||||
) {
|
) {
|
||||||
private var searchHistoryLiveData: SharedPreferenceStringSetLiveData? = null
|
private var searchHistoryLiveData: SharedPreferenceStringSetLiveData? = null
|
||||||
private var searchHistory: MutableSet<String>? = null
|
private var searchHistory: MutableSet<String>? = null
|
||||||
private var historyType: PrefName = when (type.lowercase(Locale.ROOT)) {
|
private var historyType: PrefName = when (type) {
|
||||||
"anime" -> PrefName.AnimeSearchHistory
|
SearchType.ANIME -> PrefName.AnimeSearchHistory
|
||||||
"manga" -> PrefName.MangaSearchHistory
|
SearchType.MANGA -> PrefName.MangaSearchHistory
|
||||||
else -> throw IllegalArgumentException("Invalid type")
|
SearchType.CHARACTER -> PrefName.CharacterSearchHistory
|
||||||
|
SearchType.STAFF -> PrefName.StaffSearchHistory
|
||||||
|
SearchType.STUDIO -> PrefName.StudioSearchHistory
|
||||||
|
SearchType.USER -> PrefName.UserSearchHistory
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
|
@ -5,5 +5,8 @@ import java.io.Serializable
|
||||||
data class Studio(
|
data class Studio(
|
||||||
val id: String,
|
val id: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
|
val isFavourite: Boolean?,
|
||||||
|
val favourites: Int?,
|
||||||
|
val imageUrl: String?,
|
||||||
var yearMedia: MutableMap<String, ArrayList<Media>>? = null
|
var yearMedia: MutableMap<String, ArrayList<Media>>? = null
|
||||||
) : Serializable
|
) : Serializable
|
||||||
|
|
61
app/src/main/java/ani/dantotsu/media/StudioAdapter.kt
Normal file
61
app/src/main/java/ani/dantotsu/media/StudioAdapter.kt
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package ani.dantotsu.media
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.app.ActivityOptionsCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.util.Pair
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import ani.dantotsu.copyToClipboard
|
||||||
|
import ani.dantotsu.databinding.ItemCharacterBinding
|
||||||
|
import ani.dantotsu.loadImage
|
||||||
|
import ani.dantotsu.setAnimation
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
class StudioAdapter(
|
||||||
|
private val studioList: MutableList<Studio>
|
||||||
|
) : RecyclerView.Adapter<StudioAdapter.StudioViewHolder>() {
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StudioViewHolder {
|
||||||
|
val binding =
|
||||||
|
ItemCharacterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
return StudioViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: StudioViewHolder, position: Int) {
|
||||||
|
val binding = holder.binding
|
||||||
|
setAnimation(binding.root.context, holder.binding.root)
|
||||||
|
val studio = studioList.getOrNull(position) ?: return
|
||||||
|
binding.itemCompactRelation.isVisible = false
|
||||||
|
binding.itemCompactImage.loadImage(studio.imageUrl)
|
||||||
|
binding.itemCompactTitle.text = studio.name
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = studioList.size
|
||||||
|
inner class StudioViewHolder(val binding: ItemCharacterBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
init {
|
||||||
|
itemView.setOnClickListener {
|
||||||
|
val studio = studioList[bindingAdapterPosition]
|
||||||
|
ContextCompat.startActivity(
|
||||||
|
itemView.context,
|
||||||
|
Intent(
|
||||||
|
itemView.context,
|
||||||
|
StudioActivity::class.java
|
||||||
|
).putExtra("studio", studio as Serializable),
|
||||||
|
ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||||
|
itemView.context as Activity,
|
||||||
|
Pair.create(
|
||||||
|
binding.itemCompactImage,
|
||||||
|
ViewCompat.getTransitionName(binding.itemCompactImage)!!
|
||||||
|
),
|
||||||
|
).toBundle()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
itemView.setOnLongClickListener { copyToClipboard(studioList[bindingAdapterPosition].name ?: ""); true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
142
app/src/main/java/ani/dantotsu/media/SupportingSearchAdapter.kt
Normal file
142
app/src/main/java/ani/dantotsu/media/SupportingSearchAdapter.kt
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
package ani.dantotsu.media
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import ani.dantotsu.App.Companion.context
|
||||||
|
import ani.dantotsu.R
|
||||||
|
import ani.dantotsu.connections.anilist.AnilistSearch.SearchType
|
||||||
|
import ani.dantotsu.connections.anilist.AnilistSearch.SearchType.Companion.toAnilistString
|
||||||
|
import ani.dantotsu.connections.anilist.SearchResults
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class SupportingSearchAdapter(private val activity: SearchActivity, private val type: SearchType) :
|
||||||
|
HeaderInterface() {
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
override fun onBindViewHolder(holder: SearchHeaderViewHolder, position: Int) {
|
||||||
|
binding = holder.binding
|
||||||
|
|
||||||
|
searchHistoryAdapter = SearchHistoryAdapter(type) {
|
||||||
|
binding.searchBarText.setText(it)
|
||||||
|
}
|
||||||
|
binding.searchHistoryList.layoutManager = LinearLayoutManager(binding.root.context)
|
||||||
|
binding.searchHistoryList.adapter = searchHistoryAdapter
|
||||||
|
|
||||||
|
val imm: InputMethodManager =
|
||||||
|
activity.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
|
||||||
|
if (activity.searchType == SearchType.MANGA || activity.searchType == SearchType.ANIME) {
|
||||||
|
throw IllegalArgumentException("Invalid search type (wrong adapter)")
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.searchByImage.visibility = View.GONE
|
||||||
|
binding.searchResultGrid.visibility = View.GONE
|
||||||
|
binding.searchResultList.visibility = View.GONE
|
||||||
|
binding.searchFilter.visibility = View.GONE
|
||||||
|
binding.searchAdultCheck.visibility = View.GONE
|
||||||
|
binding.searchList.visibility = View.GONE
|
||||||
|
binding.searchChipRecycler.visibility = View.GONE
|
||||||
|
|
||||||
|
binding.searchBar.hint = activity.searchType.toAnilistString()
|
||||||
|
if (PrefManager.getVal(PrefName.Incognito)) {
|
||||||
|
val startIconDrawableRes = R.drawable.ic_incognito_24
|
||||||
|
val startIconDrawable: Drawable? =
|
||||||
|
context?.let { AppCompatResources.getDrawable(it, startIconDrawableRes) }
|
||||||
|
binding.searchBar.startIconDrawable = startIconDrawable
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.searchBarText.removeTextChangedListener(textWatcher)
|
||||||
|
when (type) {
|
||||||
|
SearchType.CHARACTER -> {
|
||||||
|
binding.searchBarText.setText(activity.characterResult.search)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.STUDIO -> {
|
||||||
|
binding.searchBarText.setText(activity.studioResult.search)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.STAFF -> {
|
||||||
|
binding.searchBarText.setText(activity.staffResult.search)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.USER -> {
|
||||||
|
binding.searchBarText.setText(activity.userResult.search)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw IllegalArgumentException("Invalid search type")
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.clearHistory.setOnClickListener {
|
||||||
|
it.startAnimation(fadeOutAnimation())
|
||||||
|
it.visibility = View.GONE
|
||||||
|
searchHistoryAdapter.clearHistory()
|
||||||
|
}
|
||||||
|
updateClearHistoryVisibility()
|
||||||
|
fun searchTitle() {
|
||||||
|
val searchText = binding.searchBarText.text.toString().takeIf { it.isNotEmpty() }
|
||||||
|
|
||||||
|
val result: SearchResults<*> = when (type) {
|
||||||
|
SearchType.CHARACTER -> activity.characterResult
|
||||||
|
SearchType.STUDIO -> activity.studioResult
|
||||||
|
SearchType.STAFF -> activity.staffResult
|
||||||
|
SearchType.USER -> activity.userResult
|
||||||
|
else -> throw IllegalArgumentException("Invalid search type")
|
||||||
|
}
|
||||||
|
|
||||||
|
result.search = searchText
|
||||||
|
activity.search()
|
||||||
|
}
|
||||||
|
|
||||||
|
textWatcher = object : TextWatcher {
|
||||||
|
override fun afterTextChanged(s: Editable) {}
|
||||||
|
|
||||||
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||||
|
|
||||||
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||||
|
if (s.toString().isBlank()) {
|
||||||
|
activity.emptyMediaAdapter()
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
delay(200)
|
||||||
|
activity.runOnUiThread {
|
||||||
|
setHistoryVisibility(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setHistoryVisibility(false)
|
||||||
|
searchTitle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.searchBarText.addTextChangedListener(textWatcher)
|
||||||
|
|
||||||
|
binding.searchBarText.setOnEditorActionListener { _, actionId, _ ->
|
||||||
|
return@setOnEditorActionListener when (actionId) {
|
||||||
|
EditorInfo.IME_ACTION_SEARCH -> {
|
||||||
|
searchTitle()
|
||||||
|
binding.searchBarText.clearFocus()
|
||||||
|
imm.hideSoftInputFromWindow(binding.searchBarText.windowToken, 0)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.searchBar.setEndIconOnClickListener { searchTitle() }
|
||||||
|
|
||||||
|
search = Runnable { searchTitle() }
|
||||||
|
requestFocus = Runnable { binding.searchBarText.requestFocus() }
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,16 +4,19 @@ import android.content.Intent
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
import ani.dantotsu.databinding.ItemFollowerBinding
|
import ani.dantotsu.databinding.ItemFollowerBinding
|
||||||
|
import ani.dantotsu.databinding.ItemFollowerGridBinding
|
||||||
import ani.dantotsu.loadImage
|
import ani.dantotsu.loadImage
|
||||||
import ani.dantotsu.setAnimation
|
import ani.dantotsu.setAnimation
|
||||||
|
|
||||||
|
|
||||||
class UsersAdapter(private val user: ArrayList<User>) :
|
class UsersAdapter(private val user: MutableList<User>, private val grid: Boolean = false) :
|
||||||
RecyclerView.Adapter<UsersAdapter.UsersViewHolder>() {
|
RecyclerView.Adapter<UsersAdapter.UsersViewHolder>() {
|
||||||
|
|
||||||
inner class UsersViewHolder(val binding: ItemFollowerBinding) :
|
inner class UsersViewHolder(val binding: ViewBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
init {
|
init {
|
||||||
itemView.setOnClickListener {
|
itemView.setOnClickListener {
|
||||||
|
@ -27,6 +30,11 @@ class UsersAdapter(private val user: ArrayList<User>) :
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UsersViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UsersViewHolder {
|
||||||
return UsersViewHolder(
|
return UsersViewHolder(
|
||||||
|
if (grid) ItemFollowerGridBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
) else
|
||||||
ItemFollowerBinding.inflate(
|
ItemFollowerBinding.inflate(
|
||||||
LayoutInflater.from(parent.context),
|
LayoutInflater.from(parent.context),
|
||||||
parent,
|
parent,
|
||||||
|
@ -36,12 +44,21 @@ class UsersAdapter(private val user: ArrayList<User>) :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: UsersViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: UsersViewHolder, position: Int) {
|
||||||
val b = holder.binding
|
setAnimation(holder.binding.root.context, holder.binding.root)
|
||||||
setAnimation(b.root.context, b.root)
|
val user = user.getOrNull(position) ?: return
|
||||||
val user = user[position]
|
if (grid) {
|
||||||
b.profileUserAvatar.loadImage(user.pfp)
|
val b = holder.binding as ItemFollowerGridBinding
|
||||||
b.profileBannerImage.loadImage(user.banner ?: user.pfp)
|
b.profileUserAvatar.loadImage(user.pfp)
|
||||||
b.profileUserName.text = user.name
|
b.profileUserName.text = user.name
|
||||||
|
b.profileCompactScoreBG.isVisible = false
|
||||||
|
b.profileInfo.isVisible = false
|
||||||
|
b.profileCompactProgressContainer.isVisible = false
|
||||||
|
} else {
|
||||||
|
val b = holder.binding as ItemFollowerBinding
|
||||||
|
b.profileUserAvatar.loadImage(user.pfp)
|
||||||
|
b.profileBannerImage.loadImage(user.banner ?: user.pfp)
|
||||||
|
b.profileUserName.text = user.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int = user.size
|
override fun getItemCount(): Int = user.size
|
||||||
|
|
|
@ -37,6 +37,10 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
|
||||||
AnimeSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
|
AnimeSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
|
||||||
MangaSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
|
MangaSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
|
||||||
MangaSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
|
MangaSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
|
||||||
|
CharacterSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
|
||||||
|
StaffSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
|
||||||
|
StudioSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
|
||||||
|
UserSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
|
||||||
NovelSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
|
NovelSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
|
||||||
CommentNotificationInterval(Pref(Location.General, Int::class, 0)),
|
CommentNotificationInterval(Pref(Location.General, Int::class, 0)),
|
||||||
AnilistNotificationInterval(Pref(Location.General, Int::class, 3)),
|
AnilistNotificationInterval(Pref(Location.General, Int::class, 3)),
|
||||||
|
|
|
@ -68,13 +68,18 @@ internal class ExtensionGithubApi {
|
||||||
PrefManager.getVal<Set<String>>(PrefName.AnimeExtensionRepos).toMutableList()
|
PrefManager.getVal<Set<String>>(PrefName.AnimeExtensionRepos).toMutableList()
|
||||||
|
|
||||||
repos.forEach {
|
repos.forEach {
|
||||||
|
val repoUrl = if (it.contains("index.min.json")) {
|
||||||
|
it
|
||||||
|
} else {
|
||||||
|
"$it${if (it.endsWith('/')) "" else "/"}index.min.json"
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
val githubResponse = try {
|
val githubResponse = try {
|
||||||
networkService.client
|
networkService.client
|
||||||
.newCall(GET("${it}/index.min.json"))
|
.newCall(GET(repoUrl))
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Logger.log("Failed to get repo: $it")
|
Logger.log("Failed to get repo: $repoUrl")
|
||||||
Logger.log(e)
|
Logger.log(e)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/colorSurface">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/studioTitle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:fontFamily="@font/poppins_bold"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textColor="@color/bg_opp"
|
|
||||||
android:textSize="20sp"
|
|
||||||
tools:text="@string/name" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginTop="64dp"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/charactersText"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="32dp"
|
|
||||||
android:fontFamily="@font/poppins_bold"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:text="@string/characters"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<ani.dantotsu.FadingEdgeRecyclerView
|
|
||||||
android:id="@+id/charactersRecycler"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:nestedScrollingEnabled="true"
|
|
||||||
android:paddingStart="20dp"
|
|
||||||
android:paddingEnd="20dp"
|
|
||||||
android:requiresFadingEdge="horizontal"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:itemCount="4"
|
|
||||||
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
|
||||||
tools:listitem="@layout/item_media_compact"
|
|
||||||
tools:orientation="horizontal"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/studioRecycler"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
|
||||||
tools:itemCount="2"
|
|
||||||
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:listitem="@layout/item_media_compact"
|
|
||||||
tools:orientation="vertical"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/studioProgressBar"
|
|
||||||
style="?android:attr/progressBarStyle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="32dp"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
|
||||||
tools:visibility="gone" />
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/studioClose"
|
|
||||||
android:layout_width="32dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:layout_gravity="end"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:translationZ="2dp"
|
|
||||||
app:cardBackgroundColor="@color/nav_bg"
|
|
||||||
app:cardCornerRadius="16dp">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
|
||||||
android:layout_width="24dp"
|
|
||||||
android:layout_height="24dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:src="@drawable/ic_round_close_24"
|
|
||||||
tools:ignore="ContentDescription" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
|
@ -104,20 +104,75 @@
|
||||||
android:indeterminate="true"
|
android:indeterminate="true"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
<ani.dantotsu.FadingEdgeRecyclerView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:id="@+id/characterRecyclerView"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
android:padding="16dp"
|
|
||||||
android:requiresFadingEdge="vertical"
|
<LinearLayout
|
||||||
android:visibility="gone"
|
android:layout_width="match_parent"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
android:layout_height="wrap_content"
|
||||||
app:spanCount="2"
|
android:orientation="vertical"
|
||||||
tools:itemCount="10"
|
android:paddingStart="20dp"
|
||||||
tools:layoutManager="GridLayoutManager"
|
android:paddingEnd="20dp"
|
||||||
tools:listitem="@layout/item_media_compact"
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
tools:orientation="vertical" />
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/authorCharacterDesc"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
android:alpha="0.58"
|
||||||
|
tools:ignore="TextContrastCheck"
|
||||||
|
tools:maxLines="10"
|
||||||
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/AuthorCharactersText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:text="@string/characters"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
<ani.dantotsu.FadingEdgeRecyclerView
|
||||||
|
android:id="@+id/authorCharactersRecycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:nestedScrollingEnabled="true"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:itemCount="4"
|
||||||
|
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
|
tools:listitem="@layout/item_media_compact"
|
||||||
|
tools:orientation="horizontal"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ani.dantotsu.FadingEdgeRecyclerView
|
||||||
|
android:id="@+id/characterRecyclerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:requiresFadingEdge="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
app:spanCount="2"
|
||||||
|
tools:itemCount="10"
|
||||||
|
tools:layoutManager="GridLayoutManager"
|
||||||
|
tools:listitem="@layout/item_media_compact"
|
||||||
|
tools:orientation="vertical" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/characterClose"
|
android:id="@+id/characterClose"
|
||||||
|
|
117
app/src/main/res/layout/bottom_sheet_search.xml
Normal file
117
app/src/main/res/layout/bottom_sheet_search.xml
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/animeSearch"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:paddingStart="32dp"
|
||||||
|
android:paddingEnd="32dp"
|
||||||
|
android:text="@string/anime"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="?attr/colorOnBackground"
|
||||||
|
app:cornerRadius="0dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/mangaSearch"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:paddingStart="32dp"
|
||||||
|
android:paddingEnd="32dp"
|
||||||
|
android:text="@string/manga"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="?attr/colorOnBackground"
|
||||||
|
app:cornerRadius="0dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/userSearch"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:paddingStart="32dp"
|
||||||
|
android:paddingEnd="32dp"
|
||||||
|
android:text="@string/users"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="?attr/colorOnBackground"
|
||||||
|
app:cornerRadius="0dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/characterSearch"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:paddingStart="32dp"
|
||||||
|
android:paddingEnd="32dp"
|
||||||
|
android:text="@string/characters"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="?attr/colorOnBackground"
|
||||||
|
app:cornerRadius="0dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/staffSearch"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:paddingStart="32dp"
|
||||||
|
android:paddingEnd="32dp"
|
||||||
|
android:text="@string/staff"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="?attr/colorOnBackground"
|
||||||
|
app:cornerRadius="0dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/studioSearch"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:paddingStart="32dp"
|
||||||
|
android:paddingEnd="32dp"
|
||||||
|
android:text="@string/studios"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="?attr/colorOnBackground"
|
||||||
|
app:cornerRadius="0dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -624,6 +624,9 @@
|
||||||
<string name="age">"__Age:__ "</string>
|
<string name="age">"__Age:__ "</string>
|
||||||
<string name="birthday">\n"__Birthday:__ "</string>
|
<string name="birthday">\n"__Birthday:__ "</string>
|
||||||
<string name="gender">\n"__Gender:__ "</string>"
|
<string name="gender">\n"__Gender:__ "</string>"
|
||||||
|
<string name="hometown">\n"__Home Town:__ "</string>
|
||||||
|
<string name="years_active">\n"__Years Active:__ "</string>
|
||||||
|
<string name="date_of_death">\n"__Date of Death:__ "</string>
|
||||||
|
|
||||||
<string name="male">Male</string>
|
<string name="male">Male</string>
|
||||||
<string name="female">Female</string>
|
<string name="female">Female</string>
|
||||||
|
@ -1097,5 +1100,6 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
|
||||||
<string name="privacy_policy">Privacy Policy</string>
|
<string name="privacy_policy">Privacy Policy</string>
|
||||||
<string name="privacy_policy_desc">Read our privacy policy</string>
|
<string name="privacy_policy_desc">Read our privacy policy</string>
|
||||||
<string name="failed_to_load">Failed to load</string>
|
<string name="failed_to_load">Failed to load</string>
|
||||||
|
<string name="studios">Studios</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue