diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt index 86d2a6a9..98e48cda 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt @@ -65,7 +65,7 @@ class AnilistQueries { media.cameFromContinue = false val query = - """{Media(id:${media.id}){id mediaListEntry{id status score(format:POINT_100) progress private notes repeat customLists updatedAt startedAt{year month day}completedAt{year month day}}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 node{id image{medium}name{userPreferred}}}}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 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}}}""" + """{Media(id:${media.id}){id mediaListEntry{id status score(format:POINT_100) progress private notes repeat customLists updatedAt startedAt{year month day}completedAt{year month day}}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 node{id image{medium}name{userPreferred}}}}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}}}""" runBlocking { val anilist = async { var response = executeQuery(query, force = true, show = true) @@ -136,6 +136,29 @@ class AnilistQueries { } } } + if (fetchedMedia.staff != null) { + media.staff = arrayListOf() + fetchedMedia.staff?.edges?.forEach { i -> + i.node?.apply { + media.staff?.add( + Author( + id = id, + name = i.node?.name?.userPreferred, + image = i.node?.image?.medium, + role = when (i.role.toString()) { + "MAIN" -> currContext()?.getString(R.string.main_role) + ?: "MAIN" + + "SUPPORTING" -> currContext()?.getString(R.string.supporting_role) + ?: "SUPPORTING" + + else -> i.role.toString() + } + ) + ) + } + } + } if (fetchedMedia.relations != null) { media.relations = arrayListOf() fetchedMedia.relations?.edges?.forEach { mediaEdge -> @@ -212,8 +235,10 @@ class AnilistQueries { fetchedMedia.staff?.edges?.find { authorRoles.contains(it.role?.trim()) }?.node?.let { media.anime.author = Author( - it.id.toString(), - it.name?.userPreferred ?: "N/A" + it.id, + it.name?.userPreferred ?: "N/A", + it.image?.medium, + "AUTHOR" ) } @@ -232,8 +257,10 @@ class AnilistQueries { } else if (media.manga != null) { fetchedMedia.staff?.edges?.find { authorRoles.contains(it.role?.trim()) }?.node?.let { media.manga.author = Author( - it.id.toString(), - it.name?.userPreferred ?: "N/A" + it.id, + it.name?.userPreferred ?: "N/A", + it.image?.medium, + "AUTHOR" ) } } diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Staff.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Staff.kt index b4742e5b..7b56f693 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Staff.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Staff.kt @@ -15,7 +15,7 @@ data class Staff( @SerialName("languageV2") var languageV2: String?, // The staff images - // @SerialName("image") var image: StaffImage?, + @SerialName("image") var image: StaffImage?, // A general description of the staff member @SerialName("description") var description: String?, @@ -93,7 +93,14 @@ data class StaffConnection( // The pagination information // @SerialName("pageInfo") var pageInfo: PageInfo?, ) +@Serializable +data class StaffImage( + // The character's image of media at its largest size + @SerialName("large") var large: String?, + // The character's image of media at medium size + @SerialName("medium") var medium: String?, +) : java.io.Serializable @Serializable data class StaffEdge( var role: String?, diff --git a/app/src/main/java/ani/dantotsu/media/Author.kt b/app/src/main/java/ani/dantotsu/media/Author.kt index 4c99f710..9ab3c169 100644 --- a/app/src/main/java/ani/dantotsu/media/Author.kt +++ b/app/src/main/java/ani/dantotsu/media/Author.kt @@ -3,7 +3,9 @@ package ani.dantotsu.media import java.io.Serializable data class Author( - val id: String, - val name: String, + val id: Int, + val name: String?, + val image: String?, + val role: String?, var yearMedia: MutableMap>? = null ) : Serializable diff --git a/app/src/main/java/ani/dantotsu/media/AuthorAdapter.kt b/app/src/main/java/ani/dantotsu/media/AuthorAdapter.kt new file mode 100644 index 00000000..35195960 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/media/AuthorAdapter.kt @@ -0,0 +1,60 @@ +package ani.dantotsu.media + +import android.annotation.SuppressLint +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.recyclerview.widget.RecyclerView +import ani.dantotsu.databinding.ItemCharacterBinding +import ani.dantotsu.loadImage +import ani.dantotsu.setAnimation +import java.io.Serializable + +class AuthorAdapter( + private val authorList: ArrayList +) : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AuthorViewHolder { + val binding = + ItemCharacterBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return AuthorViewHolder(binding) + } + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder:AuthorViewHolder, position: Int) { + val binding = holder.binding + setAnimation(binding.root.context, holder.binding.root) + val author = authorList[position] + binding.itemCompactRelation.text = author.role + binding.itemCompactImage.loadImage(author.image) + binding.itemCompactTitle.text = author.name + } + + override fun getItemCount(): Int = authorList.size + inner class AuthorViewHolder(val binding: ItemCharacterBinding) : + RecyclerView.ViewHolder(binding.root) { + init { + itemView.setOnClickListener { + val author = authorList[bindingAdapterPosition] + ContextCompat.startActivity( + itemView.context, + Intent( + itemView.context, + AuthorActivity::class.java + ).putExtra("author", author as Serializable), + ActivityOptionsCompat.makeSceneTransitionAnimation( + itemView.context as Activity, + Pair.create( + binding.itemCompactImage, + ViewCompat.getTransitionName(binding.itemCompactImage)!! + ), + ).toBundle() + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/media/Media.kt b/app/src/main/java/ani/dantotsu/media/Media.kt index ebc4d4f7..ea8273ec 100644 --- a/app/src/main/java/ani/dantotsu/media/Media.kt +++ b/app/src/main/java/ani/dantotsu/media/Media.kt @@ -58,6 +58,7 @@ data class Media( var endDate: FuzzyDate? = null, var characters: ArrayList? = null, + var staff: ArrayList? = null, var prequel: Media? = null, var sequel: Media? = null, var relations: ArrayList? = null, diff --git a/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt b/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt index cafdb4ff..06fb3aa9 100644 --- a/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt @@ -411,23 +411,6 @@ class MediaInfoFragment : Fragment() { parent.addView(bind.root) } - if (!media.characters.isNullOrEmpty() && !offline) { - val bind = ItemTitleRecyclerBinding.inflate( - LayoutInflater.from(context), - parent, - false - ) - bind.itemTitle.setText(R.string.characters) - bind.itemRecycler.adapter = - CharacterAdapter(media.characters!!) - bind.itemRecycler.layoutManager = LinearLayoutManager( - requireContext(), - LinearLayoutManager.HORIZONTAL, - false - ) - parent.addView(bind.root) - } - if (!media.relations.isNullOrEmpty() && !offline) { if (media.sequel != null || media.prequel != null) { val bind = ItemQuelsBinding.inflate( @@ -490,7 +473,38 @@ class MediaInfoFragment : Fragment() { ) parent.addView(bindi.root) } - + if (!media.characters.isNullOrEmpty() && !offline) { + val bind = ItemTitleRecyclerBinding.inflate( + LayoutInflater.from(context), + parent, + false + ) + bind.itemTitle.setText(R.string.characters) + bind.itemRecycler.adapter = + CharacterAdapter(media.characters!!) + bind.itemRecycler.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.HORIZONTAL, + false + ) + parent.addView(bind.root) + } + if (!media.staff.isNullOrEmpty() && !offline) { + val bind = ItemTitleRecyclerBinding.inflate( + LayoutInflater.from(context), + parent, + false + ) + bind.itemTitle.setText(R.string.staff) + bind.itemRecycler.adapter = + AuthorAdapter(media.staff!!) + bind.itemRecycler.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.HORIZONTAL, + false + ) + parent.addView(bind.root) + } if (!media.recommendations.isNullOrEmpty() && !offline) { val bind = ItemTitleRecyclerBinding.inflate( LayoutInflater.from(context), diff --git a/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt b/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt index 50daccab..f7f4f853 100644 --- a/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt +++ b/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt @@ -18,6 +18,8 @@ import ani.dantotsu.connections.anilist.ProfileViewModel import ani.dantotsu.connections.anilist.api.Query import ani.dantotsu.databinding.FragmentProfileBinding import ani.dantotsu.loadImage +import ani.dantotsu.media.Author +import ani.dantotsu.media.AuthorAdapter import ani.dantotsu.media.Character import ani.dantotsu.media.CharacterAdapter import ani.dantotsu.media.Media @@ -145,14 +147,14 @@ class ProfileFragment() : Fragment() { false ) - val favStaff = arrayListOf() + val favStaff = arrayListOf() user.favourites?.staff?.nodes?.forEach { i -> - favStaff.add(Character(i.id, i.name.full, i.image.large, i.image.large, "")) + favStaff.add(Author(i.id, i.name.full, i.image.large , "" )) } if (favStaff.isEmpty()) { binding.profileFavStaffContainer.visibility = View.GONE } - binding.profileFavStaffRecycler.adapter = CharacterAdapter(favStaff) + binding.profileFavStaffRecycler.adapter = AuthorAdapter(favStaff) binding.profileFavStaffRecycler.layoutManager = LinearLayoutManager( requireContext(), LinearLayoutManager.HORIZONTAL, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a9c86ec6..a2014d2c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -123,6 +123,7 @@ Synopsis Characters Relations + Staff Roles Details