feat: optimize activity page

This commit is contained in:
aayush262 2024-05-26 21:43:12 +05:30
parent f3f0daf7e7
commit 2de8ffd367
8 changed files with 206 additions and 212 deletions

View file

@ -30,8 +30,8 @@ import ani.dantotsu.media.user.ListActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.openImage
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.profile.activity.FeedFragment
import ani.dantotsu.profile.activity.ActivityFragment
import ani.dantotsu.profile.activity.ActivityFragment.Companion.ActivityType
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
@ -156,6 +156,7 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
openLinkInBrowser("https://anilist.co/user/${user.name}")
true
}
R.id.action_create_new_activity -> {
ContextCompat.startActivity(
context,
@ -165,6 +166,7 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
)
true
}
else -> false
}
}
@ -177,7 +179,8 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
user.avatar?.medium ?: ""
)
profileUserName.text = user.name
val bannerAnimations: ImageView= if (PrefManager.getVal(PrefName.BannerAnimations)) profileBannerImage else profileBannerImageNoKen
val bannerAnimations: ImageView =
if (PrefManager.getVal(PrefName.BannerAnimations)) profileBannerImage else profileBannerImageNoKen
blurImage(
bannerAnimations,
@ -199,7 +202,8 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
profileAppBar.addOnOffsetChangedListener(context)
profileFollowerCount.text = (respond.data.followerPage?.pageInfo?.total ?: 0).toString()
profileFollowerCount.text =
(respond.data.followerPage?.pageInfo?.total ?: 0).toString()
profileFollowerCountContainer.setOnClickListener {
ContextCompat.startActivity(
context,
@ -209,7 +213,8 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
null
)
}
profileFollowingCount.text = (respond.data.followingPage?.pageInfo?.total ?: 0).toString()
profileFollowingCount.text =
(respond.data.followingPage?.pageInfo?.total ?: 0).toString()
profileFollowingCountContainer.setOnClickListener {
ContextCompat.startActivity(
context,
@ -320,7 +325,7 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment = when (position) {
0 -> ProfileFragment.newInstance(user)
1 -> FeedFragment.newInstance(user.id, false, -1)
1 -> ActivityFragment(ActivityType.OTHER_USER, user.id)
2 -> StatsFragment.newInstance(user)
else -> ProfileFragment.newInstance(user)
}

View file

@ -0,0 +1,157 @@
package ani.dantotsu.profile.activity
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 androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.Activity
import ani.dantotsu.databinding.FragmentFeedBinding
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.setBaseline
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.launch
class ActivityFragment(
var type: ActivityType,
val userId: Int? = null,
var activityId: Int? = null,
) : Fragment() {
private lateinit var binding: FragmentFeedBinding
private var adapter: GroupieAdapter = GroupieAdapter()
private var page: Int = 1
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentFeedBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val navBar = if (userId != null) {
(activity as ProfileActivity).navBar
} else {
(activity as FeedActivity).navBar
}
binding.listRecyclerView.setBaseline(navBar)
binding.listRecyclerView.adapter = adapter
binding.listRecyclerView.layoutManager = LinearLayoutManager(context)
binding.listProgressBar.isVisible = true
binding.feedRefresh.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = navBarHeight
}
lifecycleScope.launch {
getList()
if (adapter.itemCount == 0) {
binding.emptyTextView.isVisible = true
}
binding.listProgressBar.isVisible = false
}
binding.feedSwipeRefresh.setOnRefreshListener {
lifecycleScope.launch {
adapter.clear()
page = 1
getList()
binding.feedSwipeRefresh.isRefreshing = false
}
}
binding.listRecyclerView.addOnScrollListener(object :
RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (shouldLoadMore()) {
lifecycleScope.launch {
binding.feedRefresh.isVisible = true
getList()
binding.feedRefresh.isVisible = false
}
}
}
})
}
private suspend fun getList() {
val list = when (type) {
ActivityType.GLOBAL -> getActivities(true)
ActivityType.USER -> getActivities()
ActivityType.OTHER_USER -> getActivities(userId = userId)
ActivityType.ONE -> getActivities(activityId = activityId)
}
adapter.addAll(list.map { ActivityItem(it, ::onActivityClick, requireActivity()) })
}
private suspend fun getActivities(
global: Boolean = false,
userId: Int? = null,
activityId: Int? = null,
): List<Activity> {
val res = Anilist.query.getFeed(userId, global, page, activityId)?.data?.page?.activities
page += 1
return res?.filter {
if (Anilist.adult) true else it.media?.isAdult == false &&
(it.recipient?.id == null || it.recipient.id == Anilist.userid)
} ?: emptyList()
}
private fun shouldLoadMore(): Boolean {
val layoutManager =
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition()
val adapter = binding.listRecyclerView.adapter
return !binding.listRecyclerView.canScrollVertically(1) &&
!binding.feedRefresh.isVisible && adapter?.itemCount != 0 &&
layoutManager == (adapter!!.itemCount - 1)
}
private fun onActivityClick(id: Int, type: String) {
when (type) {
"USER" -> {
ContextCompat.startActivity(
requireContext(), Intent(requireContext(), ProfileActivity::class.java)
.putExtra("userId", id), null
)
}
"MEDIA" -> {
ContextCompat.startActivity(
requireContext(), Intent(requireContext(), MediaDetailsActivity::class.java)
.putExtra("mediaId", id), null
)
}
}
}
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
binding.root.requestLayout()
val navBar = if (userId != null) {
(activity as ProfileActivity).navBar
} else {
(activity as FeedActivity).navBar
}
binding.listRecyclerView.setBaseline(navBar)
}
}
companion object {
enum class ActivityType {
GLOBAL, USER, OTHER_USER, ONE
}
}
}

View file

@ -2,6 +2,7 @@ package ani.dantotsu.profile.activity
import android.content.res.Configuration
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.updateLayoutParams
@ -16,6 +17,7 @@ import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.profile.activity.ActivityFragment.Companion.ActivityType
import nl.joery.animatedbottombar.AnimatedBottomBar
class FeedActivity : AppCompatActivity() {
@ -45,6 +47,7 @@ class FeedActivity : AppCompatActivity() {
}
binding.listToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
val activityId = intent.getIntExtra("activityId", -1)
if (activityId != -1) { navBar.visibility = View.GONE }
binding.feedViewPager.adapter =
ViewPagerAdapter(supportFragmentManager, lifecycle, activityId)
binding.feedViewPager.setCurrentItem(selected, false)
@ -88,12 +91,12 @@ class FeedActivity : AppCompatActivity() {
lifecycle: Lifecycle,
private val activityId: Int
) : FragmentStateAdapter(fragmentManager, lifecycle) {
override fun getItemCount(): Int = 2
override fun getItemCount(): Int = if (activityId != -1) 1 else 2
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> FeedFragment.newInstance(null, false, activityId)
else -> FeedFragment.newInstance(null, true, -1)
0 -> ActivityFragment(if (activityId != -1) ActivityType.ONE else ActivityType.USER, activityId = activityId)
else -> ActivityFragment(ActivityType.GLOBAL)
}
}
}

View file

@ -1,188 +0,0 @@
package ani.dantotsu.profile.activity
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistQueries
import ani.dantotsu.connections.anilist.api.Activity
import ani.dantotsu.databinding.FragmentFeedBinding
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.setBaseline
import ani.dantotsu.util.Logger
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class FeedFragment : Fragment() {
private lateinit var binding: FragmentFeedBinding
private var adapter: GroupieAdapter = GroupieAdapter()
private var activityList: List<Activity> = emptyList()
private lateinit var activity: androidx.activity.ComponentActivity
private var page: Int = 1
private var loadedFirstTime = false
private var userId: Int? = null
private var global: Boolean = false
private var activityId: Int = -1
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentFeedBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity = requireActivity()
userId = arguments?.getInt("userId", -1)
activityId = arguments?.getInt("activityId", -1) ?: -1
if (userId == -1) userId = null
global = arguments?.getBoolean("global", false) ?: false
val navBar = if (userId != null) {
(activity as ProfileActivity).navBar
} else {
(activity as FeedActivity).navBar
}
binding.listRecyclerView.setBaseline(navBar)
binding.listRecyclerView.adapter = adapter
binding.listRecyclerView.layoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
binding.listProgressBar.visibility = ViewGroup.VISIBLE
}
@SuppressLint("ClickableViewAccessibility")
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
binding.root.requestLayout()
val navBar = if (userId != null) {
(activity as ProfileActivity).navBar
} else {
(activity as FeedActivity).navBar
}
binding.listRecyclerView.setBaseline(navBar)
if (!loadedFirstTime) {
activity.lifecycleScope.launch(Dispatchers.IO) {
val nulledId = if (activityId == -1) null else activityId
val res = Anilist.query.getFeed(userId, global, activityId = nulledId)
withContext(Dispatchers.Main) {
res?.data?.page?.activities?.let { activities ->
activityList = activities
val filtered =
activityList
.filter { if (Anilist.adult) true else it.media?.isAdult == false }
.filterNot { //filter out messages that are not directed to the user
it.recipient?.id != null && it.recipient.id != Anilist.userid
}
adapter.update(filtered.map {
ActivityItem(
it,
::onActivityClick,
requireActivity()
)
})
}
binding.listProgressBar.visibility = ViewGroup.GONE
val scrollView = binding.listRecyclerView
binding.listRecyclerView.setOnTouchListener { _, event ->
if (event?.action == MotionEvent.ACTION_UP) {
if (activityList.size % AnilistQueries.ITEMS_PER_PAGE != 0 && !global) {
//snackString("No more activities") fix spam?
Logger.log("No more activities")
} else if (!scrollView.canScrollVertically(1) && !binding.feedRefresh.isVisible
&& binding.listRecyclerView.adapter!!.itemCount != 0 &&
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)
) {
page++
binding.feedRefresh.visibility = ViewGroup.VISIBLE
loadPage {
binding.feedRefresh.visibility = ViewGroup.GONE
}
}
}
false
}
binding.feedSwipeRefresh.setOnRefreshListener {
page = 1
adapter.clear()
activityList = emptyList()
loadPage()
}
}
}
loadedFirstTime = true
}
}
}
private fun loadPage(onFinish: () -> Unit = {}) {
activity.lifecycleScope.launch(Dispatchers.IO) {
val newRes = Anilist.query.getFeed(userId, global, page)
withContext(Dispatchers.Main) {
newRes?.data?.page?.activities?.let { activities ->
activityList += activities
val filtered = activities.filterNot {
it.recipient?.id != null && it.recipient.id != Anilist.userid
}
adapter.addAll(filtered.map {
ActivityItem(
it,
::onActivityClick,
requireActivity()
)
})
}
binding.feedSwipeRefresh.isRefreshing = false
onFinish()
}
}
}
private fun onActivityClick(id: Int, type: String) {
when (type) {
"USER" -> {
ContextCompat.startActivity(
activity, Intent(activity, ProfileActivity::class.java)
.putExtra("userId", id), null
)
}
"MEDIA" -> {
ContextCompat.startActivity(
activity, Intent(activity, MediaDetailsActivity::class.java)
.putExtra("mediaId", id), null
)
}
}
}
companion object {
fun newInstance(userId: Int?, global: Boolean, activityId: Int): FeedFragment {
val fragment = FeedFragment()
val args = Bundle()
args.putInt("userId", userId ?: -1)
args.putBoolean("global", global)
args.putInt("activityId", activityId)
fragment.arguments = args
return fragment
}
}
}

View file

@ -15,6 +15,7 @@ import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType
import nl.joery.animatedbottombar.AnimatedBottomBar
class NotificationActivity : AppCompatActivity() {
@ -84,11 +85,11 @@ class NotificationActivity : AppCompatActivity() {
override fun getItemCount(): Int = 4
override fun createFragment(position: Int): Fragment = when (position) {
0 -> NotificationFragment(if (id != -1) "getOne" else "media", id)
1 -> NotificationFragment("user")
2 -> NotificationFragment("subscription")
3 -> NotificationFragment("comment")
else -> NotificationFragment("media")
0 -> NotificationFragment(if (id != -1) NotificationType.ONE else NotificationType.MEDIA, id)
1 -> NotificationFragment(NotificationType.USER)
2 -> NotificationFragment(NotificationType.SUBSCRIPTION)
3 -> NotificationFragment(NotificationType.COMMENT)
else -> NotificationFragment(NotificationType.MEDIA)
}
}
}

View file

@ -11,6 +11,7 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.webkit.internal.ApiFeature.N
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.Notification
@ -28,7 +29,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class NotificationFragment(val type: String, private val getID: Int = -1) : Fragment() {
class NotificationFragment(
val type: NotificationType,
val getID: Int = -1
) : Fragment() {
private lateinit var binding: FragmentNotificationsBinding
private var adapter: GroupieAdapter = GroupieAdapter()
private var currentPage = 1
@ -233,6 +237,10 @@ class NotificationFragment(val type: String, private val getID: Int = -1) : Frag
enum class NotificationClickType {
USER, MEDIA, ACTIVITY, COMMENT, UNDEFINED
}
enum class NotificationType {
MEDIA, USER, SUBSCRIPTION, COMMENT, ONE
}
}
}

View file

@ -56,6 +56,7 @@
app:abb_animationInterpolator="@anim/over_shoot"
app:abb_indicatorAppearance="round"
app:abb_indicatorLocation="top"
app:abb_indicatorMargin="28dp"
app:abb_selectedTabType="text"
app:abb_textAppearance="@style/NavBarText"
app:itemActiveIndicatorStyle="@style/BottomNavBar"

View file

@ -12,28 +12,35 @@
android:layout_gravity="center"
android:visibility="gone" />
<FrameLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/emptyTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fontFamily="@font/poppins_semi_bold"
android:gravity="center"
android:visibility="gone" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/feedSwipeRefresh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_weight="1"
android:clipChildren="false"
android:clipToPadding="false">
<ani.dantotsu.FadingEdgeRecyclerView
android:id="@+id/listRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_height="match_parent"
android:nestedScrollingEnabled="true"
android:requiresFadingEdge="vertical"
android:visibility="visible"
tools:listitem="@layout/item_activity" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
@ -44,10 +51,10 @@
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="32dp"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
</LinearLayout>