feat: following / followers page

This commit is contained in:
rebelonion 2024-03-05 19:33:42 -06:00
parent 8da0092561
commit 31afbd547e
17 changed files with 546 additions and 215 deletions

View file

@ -1278,12 +1278,19 @@ Page(page:$page,perPage:50) {
} }
return responseArray return responseArray
} }
private fun userFavMediaQuery(anime: Boolean, page: Int, id: Int): String { private fun userFavMediaQuery(anime: Boolean, page: Int, id: Int): 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){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}}}}}}"""
} }
suspend fun userFollowing(id: Int): Query.Following?{ suspend fun userFollowing(id: Int): Query.Following?{
return executeQuery<Query.Following>("""{Following:Page {following(userId:${id},sort:[USERNAME]){id name avatar{large medium}bannerImage}}}""", force = true) return executeQuery<Query.Following>("""{Page {following(userId:${id},sort:[USERNAME]){id name avatar{large medium}bannerImage}}}""", force = true)
} }
suspend fun userFollowers(id: Int): Query.Follower?{
return executeQuery<Query.Follower>("""{Page {followers(userId:${id},sort:[USERNAME]){id name avatar{large medium}bannerImage}}}""", force = true)
}
private suspend fun userBannerImage(type: String,id: Int?): String? { private suspend fun userBannerImage(type: String,id: Int?): String? {
val response = val response =
executeQuery<Query.MediaListCollection>("""{ MediaListCollection(userId: ${id}, type: $type, chunk:1,perChunk:25, sort: [SCORE_DESC,UPDATED_TIME_DESC]) { lists { entries{ media { id bannerImage } } } } } """) executeQuery<Query.MediaListCollection>("""{ MediaListCollection(userId: ${id}, type: $type, chunk:1,perChunk:25, sort: [SCORE_DESC,UPDATED_TIME_DESC]) { lists { entries{ media { id bannerImage } } } } } """)

View file

@ -143,73 +143,97 @@ class Query {
data class ToggleFollow( data class ToggleFollow(
@SerialName("data") @SerialName("data")
val data: Data? val data: Data?
) { ) : java.io.Serializable {
@Serializable @Serializable
data class Data( data class Data(
@SerialName("ToggleFollow") @SerialName("ToggleFollow")
val toggleFollow: FollowData val toggleFollow: FollowData
) : java.io.Serializable
)
} }
@Serializable @Serializable
data class GenreCollection( data class GenreCollection(
@SerialName("data") @SerialName("data")
val data: Data val data: Data
) { ) : java.io.Serializable {
@Serializable @Serializable
data class Data( data class Data(
@SerialName("GenreCollection") @SerialName("GenreCollection")
val genreCollection: List<String>? val genreCollection: List<String>?
) ) : java.io.Serializable
} }
@Serializable @Serializable
data class MediaTagCollection( data class MediaTagCollection(
@SerialName("data") @SerialName("data")
val data: Data val data: Data
) { ) : java.io.Serializable {
@Serializable @Serializable
data class Data( data class Data(
@SerialName("MediaTagCollection") @SerialName("MediaTagCollection")
val mediaTagCollection: List<MediaTag>? val mediaTagCollection: List<MediaTag>?
) ) : java.io.Serializable
} }
@Serializable @Serializable
data class User( data class User(
@SerialName("data") @SerialName("data")
val data: Data val data: Data
) { ) : java.io.Serializable {
@Serializable @Serializable
data class Data( data class Data(
@SerialName("User") @SerialName("User")
val user: ani.dantotsu.connections.anilist.api.User? val user: ani.dantotsu.connections.anilist.api.User?
) ) : java.io.Serializable
} }
@Serializable @Serializable
data class UserProfileResponse( data class UserProfileResponse(
@SerialName("data") @SerialName("data")
val data: Data val data: Data
) { ) : java.io.Serializable {
@Serializable @Serializable
data class Data( data class Data(
@SerialName("user") @SerialName("user")
val user: UserProfile? val user: UserProfile?
) ) : java.io.Serializable
} }
@Serializable @Serializable
data class Following( data class Following(
@SerialName("data") @SerialName("data")
val data: Data val data: Data
) { ) : java.io.Serializable {
@Serializable @Serializable
data class Data( data class Data(
@SerialName("following") @SerialName("Page")
val following: ani.dantotsu.connections.anilist.api.User? val page: FollowingPage?
) ) : java.io.Serializable
} }
@Serializable
data class Follower(
@SerialName("data")
val data: Data
) : java.io.Serializable {
@Serializable
data class Data(
@SerialName("Page")
val page: FollowerPage?
) : java.io.Serializable
}
@Serializable
data class FollowerPage(
@SerialName("followers")
val followers: List<ani.dantotsu.connections.anilist.api.User>?
) : java.io.Serializable
@Serializable
data class FollowingPage(
@SerialName("following")
val following: List<ani.dantotsu.connections.anilist.api.User>?
) : java.io.Serializable
@Serializable @Serializable
data class UserProfile( data class UserProfile(
@SerialName("id") @SerialName("id")
@ -342,7 +366,7 @@ class Query {
val mediaListOptions: MediaListOptions, val mediaListOptions: MediaListOptions,
@SerialName("statistics") @SerialName("statistics")
val statistics: StatisticsTypes val statistics: StatisticsTypes
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsTypes( data class StatisticsTypes(
@ -350,7 +374,7 @@ class Query {
val anime: Statistics, val anime: Statistics,
@SerialName("manga") @SerialName("manga")
val manga: Statistics val manga: Statistics
) ) : java.io.Serializable
@Serializable @Serializable
data class Statistics( data class Statistics(
@ -392,7 +416,7 @@ class Query {
val staff: List<StatisticsStaff>, val staff: List<StatisticsStaff>,
@SerialName("studios") @SerialName("studios")
val studios: List<StatisticsStudio> val studios: List<StatisticsStudio>
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsFormat( data class StatisticsFormat(
@ -408,7 +432,7 @@ class Query {
val mediaIds: List<Int>, val mediaIds: List<Int>,
@SerialName("format") @SerialName("format")
val format: String val format: String
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsStatus( data class StatisticsStatus(
@ -424,7 +448,7 @@ class Query {
val mediaIds: List<Int>, val mediaIds: List<Int>,
@SerialName("status") @SerialName("status")
val status: String val status: String
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsScore( data class StatisticsScore(
@ -440,7 +464,7 @@ class Query {
val mediaIds: List<Int>, val mediaIds: List<Int>,
@SerialName("score") @SerialName("score")
val score: Int val score: Int
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsLength( data class StatisticsLength(
@ -456,7 +480,7 @@ class Query {
val mediaIds: List<Int>, val mediaIds: List<Int>,
@SerialName("length") @SerialName("length")
val length: String? //can be null for manga val length: String? //can be null for manga
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsReleaseYear( data class StatisticsReleaseYear(
@ -472,7 +496,7 @@ class Query {
val mediaIds: List<Int>, val mediaIds: List<Int>,
@SerialName("releaseYear") @SerialName("releaseYear")
val releaseYear: Int val releaseYear: Int
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsStartYear( data class StatisticsStartYear(
@ -488,7 +512,7 @@ class Query {
val mediaIds: List<Int>, val mediaIds: List<Int>,
@SerialName("startYear") @SerialName("startYear")
val startYear: Int val startYear: Int
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsGenre( data class StatisticsGenre(
@ -504,7 +528,7 @@ class Query {
val mediaIds: List<Int>, val mediaIds: List<Int>,
@SerialName("genre") @SerialName("genre")
val genre: String val genre: String
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsTag( data class StatisticsTag(
@ -520,7 +544,7 @@ class Query {
val mediaIds: List<Int>, val mediaIds: List<Int>,
@SerialName("tag") @SerialName("tag")
val tag: Tag val tag: Tag
) ) : java.io.Serializable
@Serializable @Serializable
data class Tag( data class Tag(
@ -528,7 +552,7 @@ class Query {
val id: Int, val id: Int,
@SerialName("name") @SerialName("name")
val name: String val name: String
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsCountry( data class StatisticsCountry(
@ -544,7 +568,7 @@ class Query {
val mediaIds: List<Int>, val mediaIds: List<Int>,
@SerialName("country") @SerialName("country")
val country: String val country: String
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsVoiceActor( data class StatisticsVoiceActor(
@ -562,7 +586,7 @@ class Query {
val voiceActor: VoiceActor, val voiceActor: VoiceActor,
@SerialName("characterIds") @SerialName("characterIds")
val characterIds: List<Int> val characterIds: List<Int>
) ) : java.io.Serializable
@Serializable @Serializable
data class VoiceActor( data class VoiceActor(
@ -570,7 +594,7 @@ class Query {
val id: Int, val id: Int,
@SerialName("name") @SerialName("name")
val name: StaffName val name: StaffName
) ) : java.io.Serializable
@Serializable @Serializable
data class StaffName( data class StaffName(
@ -588,7 +612,7 @@ class Query {
val alternative: List<String>?, val alternative: List<String>?,
@SerialName("userPreferred") @SerialName("userPreferred")
val userPreferred: String? val userPreferred: String?
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsStaff( data class StatisticsStaff(
@ -604,7 +628,7 @@ class Query {
val mediaIds: List<Int>, val mediaIds: List<Int>,
@SerialName("staff") @SerialName("staff")
val staff: VoiceActor val staff: VoiceActor
) ) : java.io.Serializable
@Serializable @Serializable
data class StatisticsStudio( data class StatisticsStudio(
@ -620,7 +644,7 @@ class Query {
val mediaIds: List<Int>, val mediaIds: List<Int>,
@SerialName("studio") @SerialName("studio")
val studio: StatStudio val studio: StatStudio
) ) : java.io.Serializable
@Serializable @Serializable
data class StatStudio( data class StatStudio(
@ -630,7 +654,7 @@ class Query {
val name: String, val name: String,
@SerialName("isAnimationStudio") @SerialName("isAnimationStudio")
val isAnimationStudio: Boolean val isAnimationStudio: Boolean
) ) : java.io.Serializable
} }

View file

@ -1,12 +1,24 @@
package ani.dantotsu.profile package ani.dantotsu.profile
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.ViewGroup.MarginLayoutParams
import android.widget.ImageButton
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.User
import ani.dantotsu.databinding.ActivityFollowBinding import ani.dantotsu.databinding.ActivityFollowBinding
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -14,19 +26,98 @@ import kotlinx.coroutines.withContext
class FollowActivity : AppCompatActivity(){ class FollowActivity : AppCompatActivity(){
private lateinit var binding: ActivityFollowBinding private lateinit var binding: ActivityFollowBinding
val adapter = GroupieAdapter()
var users: List<User>? = null
private lateinit var selected: ImageButton
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
ThemeManager(this).applyTheme() ThemeManager(this).applyTheme()
initActivity(this) initActivity(this)
binding = ActivityFollowBinding.inflate(layoutInflater) binding = ActivityFollowBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
binding.listTitle.text = intent.getStringExtra("title")
val layoutType = PrefManager.getVal<Int>(PrefName.FollowerLayout)
selected = getSelected(layoutType)
binding.followerGrid.alpha = 0.33f
binding.followerList.alpha = 0.33f
selected(selected)
binding.root.updateLayoutParams<MarginLayoutParams> { topMargin += navBarHeight }
binding.listRecyclerView.layoutManager = LinearLayoutManager(
this,
LinearLayoutManager.VERTICAL,
false
)
binding.listRecyclerView.adapter = adapter
binding.listBack.setOnClickListener { finish() }
val title = intent.getStringExtra("title")
binding.listTitle.text = title
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val respond = Anilist.query.userFollowing(intent.getIntExtra("userId", 0)) val respond = when (title) {
val user = respond?.data?.following "Following" -> Anilist.query.userFollowing(intent.getIntExtra("userId", 0))?.data?.page?.following
"Followers" -> Anilist.query.userFollowers(intent.getIntExtra("userId", 0))?.data?.page?.followers
else -> null
}
users = respond
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
user?.id fillList()
}
}
binding.followerList.setOnClickListener {
selected(it as ImageButton)
PrefManager.setVal(PrefName.FollowerLayout, 0)
fillList()
}
binding.followerGrid.setOnClickListener {
selected(it as ImageButton)
PrefManager.setVal(PrefName.FollowerLayout, 1)
fillList()
}
}
private fun fillList() {
adapter.clear()
binding.listRecyclerView.layoutManager = when (getLayoutType(selected)) {
0 -> LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
1 -> GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false)
else -> LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
}
users?.forEach { user ->
if (getLayoutType(selected) == 0) {
adapter.add(FollowerItem(user.id, user.name ?: "Unknown", user.avatar?.medium, user.bannerImage) { onUserClick(it) })
} else {
adapter.add(GridFollowerItem(user.id, user.name ?: "Unknown", user.avatar?.medium) { onUserClick(it) })
} }
} }
} }
fun selected(it: ImageButton) {
selected.alpha = 0.33f
selected = it
selected.alpha = 1f
}
private fun getSelected(pos: Int): ImageButton {
return when (pos) {
0 -> binding.followerList
1 -> binding.followerGrid
else -> binding.followerList
}
}
private fun getLayoutType(it: ImageButton): Int {
return when (it) {
binding.followerList -> 0
binding.followerGrid -> 1
else -> 0
}
}
private fun onUserClick(id: Int) {
val intent = Intent(this, ProfileActivity::class.java)
intent.putExtra("userId", id)
startActivity(intent)
}
} }

View file

@ -0,0 +1,33 @@
package ani.dantotsu.profile
import android.view.View
import ani.dantotsu.R
import ani.dantotsu.databinding.ItemFollowerBinding
import ani.dantotsu.loadImage
import com.xwray.groupie.viewbinding.BindableItem
class FollowerItem(
private val id: Int,
private val name: String,
private val avatar: String?,
private val banner: String?,
val clickCallback: (Int) -> Unit
): BindableItem<ItemFollowerBinding>() {
private lateinit var binding: ItemFollowerBinding
override fun bind(viewBinding: ItemFollowerBinding, position: Int) {
binding = viewBinding
binding.profileUserName.text = name
avatar?.let { binding.profileUserAvatar.loadImage(it) }
banner?.let { binding.profileBannerImage.loadImage(it) }
binding.root.setOnClickListener { clickCallback(id) }
}
override fun getLayout(): Int {
return R.layout.item_follower
}
override fun initializeViewBinding(view: View): ItemFollowerBinding {
return ItemFollowerBinding.bind(view)
}
}

View file

@ -0,0 +1,31 @@
package ani.dantotsu.profile
import android.view.View
import ani.dantotsu.R
import ani.dantotsu.databinding.ItemFollowerGridBinding
import ani.dantotsu.loadImage
import com.xwray.groupie.viewbinding.BindableItem
class GridFollowerItem (
private val id: Int,
private val name: String,
private val avatar: String?,
val clickCallback: (Int) -> Unit
): BindableItem<ItemFollowerGridBinding>() {
private lateinit var binding: ItemFollowerGridBinding
override fun bind(viewBinding: ItemFollowerGridBinding, position: Int) {
binding = viewBinding
binding.profileUserName.text = name
avatar?.let { binding.profileUserAvatar.loadImage(it) }
binding.root.setOnClickListener { clickCallback(id) }
}
override fun getLayout(): Int {
return R.layout.item_follower_grid
}
override fun initializeViewBinding(view: View): ItemFollowerGridBinding {
return ItemFollowerGridBinding.bind(view)
}
}

View file

@ -5,6 +5,7 @@ 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 android.widget.PopupMenu
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
@ -82,8 +83,10 @@ class ProfileActivity : AppCompatActivity() {
} }
}) })
val userLevel = intent.getStringExtra("username") ?: "" val userLevel = intent.getStringExtra("username") ?: ""
binding.followButton.visibility = if (user.id == Anilist.userid || Anilist.userid == null) View.GONE else View.VISIBLE binding.followButton.visibility =
binding.followButton.text = if (user.isFollowing) "Unfollow" else if(user.isFollower) "Follows you" else "Follow" if (user.id == Anilist.userid || Anilist.userid == null) View.GONE else View.VISIBLE
binding.followButton.text =
if (user.isFollowing) "Unfollow" else if (user.isFollower) "Follows you" else "Follow"
if (user.isFollowing && user.isFollower) binding.followButton.text = "Mutual" if (user.isFollowing && user.isFollower) binding.followButton.text = "Mutual"
binding.followButton.setOnClickListener { binding.followButton.setOnClickListener {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
@ -92,8 +95,10 @@ class ProfileActivity : AppCompatActivity() {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
snackString("Success") snackString("Success")
user.isFollowing = res.data.toggleFollow.isFollowing user.isFollowing = res.data.toggleFollow.isFollowing
binding.followButton.text = if (user.isFollowing) "Unfollow" else if(user.isFollower) "Follows you" else "Follow" binding.followButton.text =
if (user.isFollowing && user.isFollower) binding.followButton.text = "Mutual" if (user.isFollowing) "Unfollow" else if (user.isFollower) "Follows you" else "Follow"
if (user.isFollowing && user.isFollower) binding.followButton.text =
"Mutual"
} }
} }
} }
@ -101,12 +106,35 @@ class ProfileActivity : AppCompatActivity() {
binding.profileProgressBar.visibility = View.GONE binding.profileProgressBar.visibility = View.GONE
binding.profileTopContainer.visibility = View.VISIBLE binding.profileTopContainer.visibility = View.VISIBLE
binding.temp.setOnClickListener { binding.profileMenuButton.setOnClickListener {
ContextCompat.startActivity( val popup = PopupMenu(this@ProfileActivity, binding.profileMenuButton)
this@ProfileActivity, Intent(this@ProfileActivity, FollowActivity::class.java) popup.menuInflater.inflate(R.menu.menu_profile, popup.menu)
.putExtra("title", "Following") popup.setOnMenuItemClickListener { item ->
.putExtra("userId", user.id), null when (item.itemId) {
) R.id.action_view_following -> {
ContextCompat.startActivity(
this@ProfileActivity,
Intent(this@ProfileActivity, FollowActivity::class.java)
.putExtra("title", "Following")
.putExtra("userId", user.id),
null
)
true
}
R.id.action_view_followers -> {
ContextCompat.startActivity(
this@ProfileActivity,
Intent(this@ProfileActivity, FollowActivity::class.java)
.putExtra("title", "Followers")
.putExtra("userId", user.id),
null
)
true
}
else -> false
}
}
popup.show()
} }
binding.profileUserAvatar.loadImage(user.avatar?.medium) binding.profileUserAvatar.loadImage(user.avatar?.medium)
@ -122,6 +150,10 @@ class ProfileActivity : AppCompatActivity() {
if (!(PrefManager.getVal(PrefName.BannerAnimations) as Boolean)) binding.profileBannerImage.pause() if (!(PrefManager.getVal(PrefName.BannerAnimations) as Boolean)) binding.profileBannerImage.pause()
binding.profileBannerImage.loadImage(user.bannerImage) binding.profileBannerImage.loadImage(user.bannerImage)
binding.profileBannerImage.updateLayoutParams { height += statusBarHeight } binding.profileBannerImage.updateLayoutParams { height += statusBarHeight }
binding.profileBannerGradient.updateLayoutParams { height += statusBarHeight }
binding.profileMenuButton.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin += statusBarHeight
}
binding.profileBannerImage.setOnLongClickListener { binding.profileBannerImage.setOnLongClickListener {
ImageViewDialog.newInstance( ImageViewDialog.newInstance(
this@ProfileActivity, this@ProfileActivity,
@ -132,7 +164,6 @@ class ProfileActivity : AppCompatActivity() {
} }
} }
} }
override fun onResume() { override fun onResume() {

View file

@ -47,11 +47,11 @@ class ProfileFragment() : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
activity = requireActivity() as ProfileActivity activity = requireActivity() as ProfileActivity
user = arguments?.getSerializable("user") as Query.UserProfile
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
model.setData(user.id) model.setData(user.id)
} }
user = arguments?.getSerializable("user") as Query.UserProfile
val backGroundColorTypedValue = TypedValue() val backGroundColorTypedValue = TypedValue()
val textColorTypedValue = TypedValue() val textColorTypedValue = TypedValue()
activity.theme.resolveAttribute( activity.theme.resolveAttribute(

View file

@ -66,6 +66,7 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
AnimeListSortOrder(Pref(Location.UI, String::class, "score")), AnimeListSortOrder(Pref(Location.UI, String::class, "score")),
MangaListSortOrder(Pref(Location.UI, String::class, "score")), MangaListSortOrder(Pref(Location.UI, String::class, "score")),
CommentSortOrder(Pref(Location.UI, String::class, "newest")), CommentSortOrder(Pref(Location.UI, String::class, "newest")),
FollowerLayout(Pref(Location.UI, Int::class, 0)),
//Player //Player
DefaultSpeed(Pref(Location.Player, Int::class, 5)), DefaultSpeed(Pref(Location.Player, Int::class, 5)),

View file

@ -1,38 +1,98 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<ProgressBar <ProgressBar
android:id="@+id/listProgressBar" android:id="@+id/listProgressBar"
style="?android:attr/progressBarStyle" style="?android:attr/progressBarStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:visibility="gone"/> android:visibility="gone" />
<TextView <FrameLayout
android:id="@+id/listTitle" android:layout_marginTop="16dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="wrap_content"
android:layout_marginStart="32dp" android:orientation="horizontal">
android:ellipsize="end"
android:fontFamily="@font/poppins_bold" <ImageView
android:gravity="center|start" android:id="@+id/listBack"
android:singleLine="true" android:layout_width="wrap_content"
android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title" android:layout_height="wrap_content"
android:textColor="?attr/colorOnBackground" android:layout_gravity="start|center_vertical"
android:textSize="16sp" android:layout_marginStart="12dp"
tools:text="xyz" /> android:src="@drawable/ic_round_arrow_back_ios_new_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/listTitle"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:layout_marginStart="44dp"
android:ellipsize="end"
android:fontFamily="@font/poppins_bold"
android:gravity="center|start"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
android:textColor="?attr/colorOnBackground"
android:textSize="18sp"
tools:text="xyz" />
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="8dp"
app:cardBackgroundColor="@color/nav_bg_inv"
app:cardCornerRadius="16dp"
app:cardElevation="0dp">
<ImageButton
android:id="@+id/followerList"
android:layout_width="36dp"
android:layout_height="36dp"
android:alpha="0.33"
android:background="?android:attr/selectableItemBackground"
android:scaleX="-1"
app:srcCompat="@drawable/ic_round_view_list_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,SpeakableTextPresentCheck,ImageContrastCheck" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="46dp"
app:cardBackgroundColor="@color/nav_bg_inv"
app:cardCornerRadius="16dp"
app:cardElevation="0dp">
<ImageButton
android:id="@+id/followerGrid"
android:layout_width="36dp"
android:layout_height="36dp"
android:alpha="0.33"
android:background="?android:attr/selectableItemBackground"
app:srcCompat="@drawable/ic_round_grid_view_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,SpeakableTextPresentCheck,ImageContrastCheck" />
</androidx.cardview.widget.CardView>
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/listRecyclerView" android:id="@+id/listRecyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
tools:listitem="@layout/item_follow"/> android:layout_marginEnd="16dp"
tools:listitem="@layout/item_follower" />
</LinearLayout> </LinearLayout>

View file

@ -22,102 +22,104 @@
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:scrollbars="none" android:layout_marginBottom="72dp"
android:layout_marginBottom="72dp"> android:scrollbars="none">
<LinearLayout <LinearLayout
android:id="@+id/profileTopContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:visibility="gone"
tools:visibility="visible"
android:orientation="vertical"> android:orientation="vertical">
<com.flaviofaria.kenburnsview.KenBurnsView
android:id="@+id/profileBannerImage" <FrameLayout
android:id="@+id/profileTopContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
tools:ignore="ContentDescription"
tools:src="@tools:sample/backgrounds/scenic" />
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="-200dp"
android:src="@drawable/linear_gradient_bg"
tools:ignore="ContentDescription" />
<LinearLayout
android:id="@+id/profileUserDataContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="-171dp"
android:layout_gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible"> tools:visibility="visible">
<com.google.android.material.card.MaterialCardView <com.flaviofaria.kenburnsview.KenBurnsView
android:id="@+id/profileUserAvatarContainer" android:id="@+id/profileBannerImage"
android:layout_width="82dp" android:layout_width="match_parent"
android:layout_height="82dp" android:layout_height="200dp"
android:layout_gravity="center" android:scaleType="centerCrop"
android:backgroundTint="@color/transparent" tools:ignore="ContentDescription"
app:cardCornerRadius="64dp"> tools:src="@tools:sample/backgrounds/scenic" />
<com.google.android.material.imageview.ShapeableImageView <ImageView
android:id="@+id/profileUserAvatar" android:id="@+id/profileBannerGradient"
android:layout_width="match_parent"
android:layout_height="200dp"
android:src="@drawable/linear_gradient_bg"
tools:ignore="ContentDescription" />
<LinearLayout
android:id="@+id/profileUserDataContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:orientation="vertical"
tools:visibility="visible">
<com.google.android.material.card.MaterialCardView
android:id="@+id/profileUserAvatarContainer"
android:layout_width="82dp" android:layout_width="82dp"
android:layout_height="82dp" android:layout_height="82dp"
android:layout_gravity="center" android:layout_gravity="center"
app:srcCompat="@drawable/ic_round_add_circle_24" android:backgroundTint="@color/transparent"
tools:tint="@color/transparent" app:cardCornerRadius="64dp">
tools:ignore="ContentDescription,ImageContrastCheck" />
</com.google.android.material.card.MaterialCardView> <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/profileUserAvatar"
android:layout_width="82dp"
android:layout_height="82dp"
android:layout_gravity="center"
app:srcCompat="@drawable/ic_round_add_circle_24"
tools:ignore="ContentDescription,ImageContrastCheck"
tools:tint="@color/transparent" />
<TextView </com.google.android.material.card.MaterialCardView>
android:id="@+id/profileUserName"
<TextView
android:id="@+id/profileUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/username"
android:textColor="?attr/colorPrimary"
android:textSize="18sp" />
<Button
android:id="@+id/followButton"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:backgroundTint="?attr/colorPrimaryContainer"
android:enabled="true"
android:fontFamily="@font/poppins_bold"
android:text="Follow"
android:textColor="@color/bg_opp"
android:textSize="14sp"
app:cornerRadius="8dp"
app:strokeColor="?attr/colorPrimaryContainer"
tools:ignore="SpeakableTextPresentCheck" />
</LinearLayout>
<ImageView
android:id="@+id/profileMenuButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_gravity="end"
android:layout_gravity="center" android:layout_marginTop="20dp"
android:textColor="?attr/colorPrimary" android:layout_marginEnd="16dp"
android:fontFamily="@font/poppins_semi_bold" android:contentDescription="@string/menu"
android:text="@string/username" android:src="@drawable/ic_round_dots_vertical_24" />
android:textSize="18sp" /> </FrameLayout>
<Button
android:id="@+id/followButton"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="true"
android:layout_gravity="center"
android:fontFamily="@font/poppins_bold"
android:textColor="@color/bg_opp"
android:text="Follow"
android:textSize="14sp"
android:backgroundTint="?attr/colorPrimaryContainer"
app:cornerRadius="8dp"
app:strokeColor="?attr/colorPrimaryContainer"
tools:ignore="SpeakableTextPresentCheck" />
<Button
android:id="@+id/temp"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="true"
android:layout_gravity="center"
android:fontFamily="@font/poppins_bold"
android:textColor="@color/bg_opp"
android:text="temp"
android:textSize="14sp"
android:backgroundTint="?attr/colorPrimaryContainer"
app:cornerRadius="8dp"
app:strokeColor="?attr/colorPrimaryContainer"
tools:ignore="SpeakableTextPresentCheck" />
</LinearLayout>
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/profileViewPager" android:id="@+id/profileViewPager"
@ -126,6 +128,7 @@
tools:ignore="SpeakableTextPresentCheck" /> tools:ignore="SpeakableTextPresentCheck" />
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>

View file

@ -141,12 +141,12 @@
android:id="@+id/profileUserStatsTitle" android:id="@+id/profileUserStatsTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="32dp" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="32dp" android:layout_marginEnd="32dp"
android:fontFamily="@font/poppins_bold" android:fontFamily="@font/poppins_bold"
android:text="Stats" android:text="Stats"
android:textSize="16sp" android:textSize="18sp"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
<TableLayout <TableLayout
@ -341,18 +341,19 @@
android:id="@+id/profileUserBioTitle" android:id="@+id/profileUserBioTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="32dp" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="32dp" android:layout_marginEnd="32dp"
android:fontFamily="@font/poppins_bold" android:fontFamily="@font/poppins_bold"
android:text="Bio" android:text="Bio"
android:textSize="16sp" android:textSize="18sp"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
<WebView <WebView
android:id="@+id/profileUserBio" android:id="@+id/profileUserBio"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:textAlignment="textStart" android:textAlignment="textStart"
@ -373,11 +374,11 @@
android:id="@+id/profileFavAnime" android:id="@+id/profileFavAnime"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="24dp" android:layout_marginStart="8dp"
android:fontFamily="@font/poppins_bold" android:fontFamily="@font/poppins_bold"
android:padding="8dp" android:padding="8dp"
android:text="@string/fav_anime" android:text="@string/fav_anime"
android:textSize="16sp" /> android:textSize="18sp" />
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -447,11 +448,11 @@
android:id="@+id/profileFavManga" android:id="@+id/profileFavManga"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="24dp" android:layout_marginStart="8dp"
android:fontFamily="@font/poppins_bold" android:fontFamily="@font/poppins_bold"
android:padding="8dp" android:padding="8dp"
android:text="@string/fav_manga" android:text="@string/fav_manga"
android:textSize="16sp" /> android:textSize="18sp" />
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -517,12 +518,12 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="32dp" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="32dp" android:layout_marginEnd="32dp"
android:fontFamily="@font/poppins_bold" android:fontFamily="@font/poppins_bold"
android:text="Favorite Characters" android:text="Favorite Characters"
android:textSize="16sp" /> android:textSize="18sp" />
<ani.dantotsu.FadingEdgeRecyclerView <ani.dantotsu.FadingEdgeRecyclerView
android:id="@+id/profileFavCharactersRecycler" android:id="@+id/profileFavCharactersRecycler"
@ -548,12 +549,12 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="32dp" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="32dp" android:layout_marginEnd="32dp"
android:fontFamily="@font/poppins_bold" android:fontFamily="@font/poppins_bold"
android:text="Favorite Staff" android:text="Favorite Staff"
android:textSize="16sp" /> android:textSize="18sp" />
<ani.dantotsu.FadingEdgeRecyclerView <ani.dantotsu.FadingEdgeRecyclerView
android:id="@+id/profileFavStaffRecycler" android:id="@+id/profileFavStaffRecycler"

View file

@ -1,55 +0,0 @@
<androidx.cardview.widget.CardView 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="80dp"
app:cardCornerRadius="16dp"
android:layout_marginBottom="16dp">
<ImageView
android:id="@+id/followBanner"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="ContentDescription,ImageContrastCheck" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_gravity="center"
app:cardCornerRadius="32dp">
<ImageView
android:id="@+id/followProfile"
android:layout_width="64dp"
android:layout_height="64dp"
tools:ignore="ContentDescription,ImageContrastCheck"
tools:srcCompat="@tools:sample/avatars" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/followName"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="10dp"
android:gravity="center_vertical"
android:fontFamily="@font/poppins_bold"
android:textColor="?attr/colorSecondary"
tools:text="Username"
tools:ignore="RtlSymmetry" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_gravity="bottom"
android:layout_height="4dp"
android:layout_marginStart="-16dp"
android:layout_marginEnd="-16dp"
android:background="?android:attr/listDivider" />
</androidx.cardview.widget.CardView>

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="120dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_marginBottom="8dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/profileBannerImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/linear_gradient_bg"
tools:ignore="ContentDescription" />
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_black_50"
tools:ignore="ContentDescription" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/profileUserAvatarContainer"
android:layout_width="82dp"
android:layout_height="82dp"
android:layout_gravity="start|center_vertical"
android:layout_marginStart="16dp"
android:backgroundTint="@color/bg_white"
app:cardCornerRadius="64dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/profileUserAvatar"
android:layout_width="82dp"
android:layout_height="82dp"
android:layout_gravity="center"
app:srcCompat="@drawable/ic_round_add_circle_24"
tools:tint="@color/transparent"
tools:ignore="ContentDescription,ImageContrastCheck" />
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/profileUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:layout_marginStart="120dp"
android:text="Username"
android:textColor="@color/bg_white"
android:textSize="18sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
</FrameLayout>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:padding="8dp"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<ImageView
android:id="@+id/profileUserAvatar"
android:layout_width="112dp"
android:layout_height="112dp"
android:layout_gravity="center"
tools:tint="@color/transparent"
tools:ignore="ContentDescription,ImageContrastCheck" />
<TextView
android:id="@+id/profileUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|center_vertical"
android:text="Username"
android:textColor="?attr/colorOnBackground"
android:textSize="12sp"
android:ellipsize="end"
android:textStyle="bold"
tools:ignore="HardcodedText" />
</LinearLayout>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_view_following"
android:title="@string/view_following"
app:showAsAction="never" />
<item
android:id="@+id/action_view_followers"
android:title="@string/view_followers"
app:showAsAction="never" />
</menu>

View file

@ -8,6 +8,7 @@
<color name="bg">@color/bg_white</color> <color name="bg">@color/bg_white</color>
<color name="bg_opp">@color/bg_black</color> <color name="bg_opp">@color/bg_black</color>
<color name="fg">#A8000000</color> <color name="fg">#A8000000</color>
<color name="bg_black_50" alpha="128">#80000000</color>
<color name="nav_bg">#fff</color> <color name="nav_bg">#fff</color>
<color name="nav_bg_inv">#00FFFFFF</color> <color name="nav_bg_inv">#00FFFFFF</color>
<color name="nav_tab">#40000000</color> <color name="nav_tab">#40000000</color>

View file

@ -666,6 +666,9 @@
<string name="highest_rated">Highest rated</string> <string name="highest_rated">Highest rated</string>
<string name="lowest_rated">Lowest rated</string> <string name="lowest_rated">Lowest rated</string>
<string name="compare"><u>Compare</u></string> <string name="compare"><u>Compare</u></string>
<string name="view_following">View Following</string>
<string name="menu">menu</string>
<string name="view_followers">View Followers</string>
</resources> </resources>