From 2de8ffd36798536853df066d2f19a82e1540b452 Mon Sep 17 00:00:00 2001 From: aayush262 Date: Sun, 26 May 2024 21:43:12 +0530 Subject: [PATCH] feat: optimize activity page --- .../ani/dantotsu/profile/ProfileActivity.kt | 17 +- .../profile/activity/ActivityFragment.kt | 157 +++++++++++++++ .../dantotsu/profile/activity/FeedActivity.kt | 9 +- .../dantotsu/profile/activity/FeedFragment.kt | 188 ------------------ .../notification/NotificationActivity.kt | 11 +- .../notification/NotificationFragment.kt | 10 +- app/src/main/res/layout/activity_feed.xml | 1 + app/src/main/res/layout/fragment_feed.xml | 25 ++- 8 files changed, 206 insertions(+), 212 deletions(-) create mode 100644 app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt delete mode 100644 app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt diff --git a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt index 5433df6a..aef3f4c3 100644 --- a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt @@ -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) } diff --git a/app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt b/app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt new file mode 100644 index 00000000..364a80d8 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt @@ -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 { + 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 { + 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 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt b/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt index df86fe92..778bd37f 100644 --- a/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt @@ -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 { 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) } } } diff --git a/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt b/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt deleted file mode 100644 index 102caea1..00000000 --- a/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt +++ /dev/null @@ -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 = 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 - } - } -} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt b/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt index 8af2cf24..e0f133eb 100644 --- a/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt @@ -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) } } } diff --git a/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt b/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt index c6494b9d..15a32eff 100644 --- a/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt +++ b/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt @@ -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 + } } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_feed.xml b/app/src/main/res/layout/activity_feed.xml index 6537b261..c0b0f0bc 100644 --- a/app/src/main/res/layout/activity_feed.xml +++ b/app/src/main/res/layout/activity_feed.xml @@ -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" diff --git a/app/src/main/res/layout/fragment_feed.xml b/app/src/main/res/layout/fragment_feed.xml index d1c86904..a8aae5b7 100644 --- a/app/src/main/res/layout/fragment_feed.xml +++ b/app/src/main/res/layout/fragment_feed.xml @@ -12,28 +12,35 @@ android:layout_gravity="center" android:visibility="gone" /> - + android:layout_height="wrap_content" + android:gravity="center_horizontal" + android:orientation="vertical"> + @@ -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" /> - + \ No newline at end of file