diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml index 558fb658..68444cd8 100644 --- a/.github/workflows/beta.yml +++ b/.github/workflows/beta.yml @@ -108,7 +108,7 @@ jobs: #Telegram curl -F "chat_id=${{ secrets.TELEGRAM_CHANNEL_ID }}" \ - -F "document=@app/build/outputs/apk/google/alpha/app-google-universal-alpha.apk" \ + -F "document=@app/build/outputs/apk/google/alpha/app-google-alpha.apk" \ -F "caption=Alpha-Build: ${VERSION}: ${commit_messages}" \ https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendDocument diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 43c9c29f..c42fee0d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -194,7 +194,7 @@ android:label="Inbox Activity" android:parentActivityName=".MainActivity" /> threeDaysAgo }.toList() .sortedByDescending { it.createdAt } val anilistActivities = mutableListOf() diff --git a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt index df0e0939..9c61920b 100644 --- a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt +++ b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt @@ -268,8 +268,9 @@ class AnimePageAdapter : RecyclerView.Adapter - onClick(int) - } - ) - } - ) - } else { - snackString("Failed to load replies") - } + loadData() + } + } + + private suspend fun loadData() { + val response = Anilist.query.getReplies(activityId) + withContext(Dispatchers.Main) { + loading(false) + if (response != null) { + replies.clear() + replies.addAll(response.data.page.activityReplies) + adapter.update( + replies.map { + ActivityReplyItem( + it, + requireActivity(), + clickCallback = { int, _ -> + onClick(int) + } + ) + } + ) + } else { + snackString("Failed to load replies") } } - } private fun onClick(int: Int) { @@ -101,6 +105,15 @@ class RepliesBottomDialog : BottomSheetDialogFragment() { super.onDestroyView() } + override fun onResume() { + super.onResume() + loading(true) + lifecycleScope.launch(Dispatchers.IO) { + delay(2000) + loadData() + } + } + companion object { fun newInstance(activityId: Int): RepliesBottomDialog { return RepliesBottomDialog().apply { diff --git a/app/src/main/java/ani/dantotsu/home/status/Stories.kt b/app/src/main/java/ani/dantotsu/home/status/Stories.kt index 7f2c60ec..9f163857 100644 --- a/app/src/main/java/ani/dantotsu/home/status/Stories.kt +++ b/app/src/main/java/ani/dantotsu/home/status/Stories.kt @@ -74,8 +74,7 @@ class Stories @JvmOverloads constructor( if (context is StoriesCallback) storiesListener = context as StoriesCallback - binding.leftTouchPanel.setOnTouchListener(this) - binding.rightTouchPanel.setOnTouchListener(this) + binding.touchPanel.setOnTouchListener(this) } @@ -264,49 +263,7 @@ class Stories @JvmOverloads constructor( } - private var startClickTime = 0L - private var startX = 0f - private var startY = 0f - private var isLongPress = false - private val swipeThreshold = 100 - override fun onTouch(view: View?, event: MotionEvent?): Boolean { - val maxClickDuration = 200 - when (event?.action) { - MotionEvent.ACTION_DOWN -> { - startX = event.x - startY = event.y - startClickTime = Calendar.getInstance().timeInMillis - pause() - isLongPress = false - } - MotionEvent.ACTION_MOVE -> { - val deltaX = event.x - startX - val deltaY = event.y - startY - if (!isLongPress && (abs(deltaX) > swipeThreshold || abs(deltaY) > swipeThreshold)) { - isLongPress = true - } - } - - MotionEvent.ACTION_UP -> { - val clickDuration = Calendar.getInstance().timeInMillis - startClickTime - if (clickDuration < maxClickDuration && !isLongPress) { - when (view?.id) { - R.id.leftTouchPanel -> leftPanelTouch() - R.id.rightTouchPanel -> rightPanelTouch() - } - } else { - resume() - } - val deltaX = event.x - startX - if (abs(deltaX) > swipeThreshold) { - if (deltaX > 0) onStoriesPrevious() - else onStoriesCompleted() - } - } - } - return true - } private fun rightPanelTouch() { Logger.log("rightPanelTouch: $storyIndex") @@ -359,6 +316,7 @@ class Stories @JvmOverloads constructor( timer.resume() } + @SuppressLint("ClickableViewAccessibility") private fun loadStory(story: Activity) { val key = "activities" val set = PrefManager.getCustomVal>(key, setOf()).plus((story.id)) @@ -374,6 +332,15 @@ class Stories @JvmOverloads constructor( null ) } + + binding.textActivity.setOnTouchListener { v, event -> + onTouchView(v, event, true) + v.onTouchEvent(event) + } + binding.textActivityContainer.setOnTouchListener { v, event -> + onTouchView(v, event, true) + v.onTouchEvent(event) + } fun visible(isList: Boolean) { binding.textActivity.isVisible = !isList binding.textActivityContainer.isVisible = !isList @@ -397,15 +364,15 @@ class Stories @JvmOverloads constructor( } } } ${story.progress ?: story.media?.title?.userPreferred} " + - if ( - story.status?.contains("completed") == false && - !story.status.contains("plans") && - !story.status.contains("repeating") - ) { - "of ${story.media?.title?.userPreferred}" - } else { - "" - } + if ( + story.status?.contains("completed") == false && + !story.status.contains("plans") && + !story.status.contains("repeating") + ) { + "of ${story.media?.title?.userPreferred}" + } else { + "" + } binding.infoText.text = text val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations) blurImage( @@ -502,4 +469,66 @@ class Stories @JvmOverloads constructor( } } } + private var startClickTime = 0L + private var startX = 0f + private var startY = 0f + private var isLongPress = false + private val swipeThreshold = 100 + override fun onTouch(view: View, event: MotionEvent): Boolean { + onTouchView(view, event) + return true + } + private fun onTouchView(view: View, event: MotionEvent, isText: Boolean = false){ + val maxClickDuration = 200 + val screenWidth = view.width + val leftHalf = screenWidth / 2 + val leftQuarter = screenWidth * 0.15 + val rightQuarter = screenWidth * 0.85 + when (event.action) { + MotionEvent.ACTION_DOWN -> { + startX = event.x + startY = event.y + startClickTime = Calendar.getInstance().timeInMillis + pause() + isLongPress = false + } + MotionEvent.ACTION_MOVE -> { + val deltaX = event.x - startX + val deltaY = event.y - startY + if (!isLongPress && (abs(deltaX) > swipeThreshold || abs(deltaY) > swipeThreshold)) { + isLongPress = true + } + } + MotionEvent.ACTION_UP -> { + val clickDuration = Calendar.getInstance().timeInMillis - startClickTime + if (isText) { + if (clickDuration < maxClickDuration && !isLongPress) { + if (event.x < leftQuarter) { + leftPanelTouch() + } else if (event.x > rightQuarter) { + rightPanelTouch() + } + } else { + resume() + } + } else { + if (clickDuration < maxClickDuration && !isLongPress) { + if (event.x < leftHalf) { + leftPanelTouch() + } else { + rightPanelTouch() + } + } else { + resume() + } + } + val deltaX = event.x - startX + if (abs(deltaX) > swipeThreshold) { + if (deltaX > 0) onStoriesPrevious() + else onStoriesCompleted() + } + + } + } + } } \ No newline at end of file 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..cad30291 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt @@ -0,0 +1,159 @@ +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.R +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 + } + binding.emptyTextView.text = getString(R.string.no_notifications) + 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 != true } + ?.filterNot { 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..1b2079b4 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 @@ -10,16 +11,20 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.lifecycle.Lifecycle import androidx.viewpager2.adapter.FragmentStateAdapter +import androidx.viewpager2.widget.ViewPager2 import ani.dantotsu.R import ani.dantotsu.databinding.ActivityFeedBinding +import ani.dantotsu.databinding.ActivityNotificationBinding 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 ani.dantotsu.profile.notification.NotificationActivity import nl.joery.animatedbottombar.AnimatedBottomBar class FeedActivity : AppCompatActivity() { - private lateinit var binding: ActivityFeedBinding + private lateinit var binding: ActivityNotificationBinding private var selected: Int = 0 lateinit var navBar: AnimatedBottomBar @@ -27,28 +32,27 @@ class FeedActivity : AppCompatActivity() { super.onCreate(savedInstanceState) ThemeManager(this).applyTheme() initActivity(this) - binding = ActivityFeedBinding.inflate(layoutInflater) + binding = ActivityNotificationBinding.inflate(layoutInflater) setContentView(binding.root) - navBar = binding.feedNavBar - val navBarMargin = if (resources.configuration.orientation == - Configuration.ORIENTATION_LANDSCAPE - ) 0 else navBarHeight - navBar.updateLayoutParams { bottomMargin = navBarMargin } - val personalTab = navBar.createTab(R.drawable.ic_round_person_24, "Following") - val globalTab = navBar.createTab(R.drawable.ic_globe_24, "Global") - navBar.addTab(personalTab) - navBar.addTab(globalTab) - binding.listTitle.text = getString(R.string.activities) - binding.feedViewPager.updateLayoutParams { - bottomMargin = navBarMargin - topMargin += statusBarHeight + binding.notificationTitle.text = getString(R.string.activities) + binding.notificationToolbar.updateLayoutParams { + topMargin = statusBarHeight } - binding.listToolbar.updateLayoutParams { topMargin += statusBarHeight } - val activityId = intent.getIntExtra("activityId", -1) - binding.feedViewPager.adapter = - ViewPagerAdapter(supportFragmentManager, lifecycle, activityId) - binding.feedViewPager.setCurrentItem(selected, false) - binding.feedViewPager.isUserInputEnabled = false + navBar = binding.notificationNavBar + binding.root.updateLayoutParams { + bottomMargin = navBarHeight + } + val tabs = listOf( + Pair(R.drawable.ic_round_person_24, "Following"), + Pair(R.drawable.ic_globe_24, "Global"), + ) + tabs.forEach { (icon, title) -> navBar.addTab(navBar.createTab(icon, title)) } + binding.notificationBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() } + val getOne = intent.getIntExtra("activityId", -1) + if (getOne != -1) { navBar.visibility = View.GONE } + binding.notificationViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, getOne) + binding.notificationViewPager.setOffscreenPageLimit(4) + binding.notificationViewPager.setCurrentItem(selected, false) navBar.selectTabAt(selected) navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener { override fun onTabSelected( @@ -58,24 +62,15 @@ class FeedActivity : AppCompatActivity() { newTab: AnimatedBottomBar.Tab ) { selected = newIndex - binding.feedViewPager.setCurrentItem(selected, true) + binding.notificationViewPager.setCurrentItem(selected, true) + } + }) + binding.notificationViewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + navBar.selectTabAt(position) } }) - binding.listBack.setOnClickListener { - onBackPressedDispatcher.onBackPressed() - } - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - val margin = - if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) 0 else navBarHeight - val params: ViewGroup.MarginLayoutParams = - binding.feedViewPager.layoutParams as ViewGroup.MarginLayoutParams - val paramsNav: ViewGroup.MarginLayoutParams = - navBar.layoutParams as ViewGroup.MarginLayoutParams - params.updateMargins(bottom = margin) - paramsNav.updateMargins(bottom = margin) } override fun onResume() { @@ -88,12 +83,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/activity/NotificationActivity.kt b/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt deleted file mode 100644 index cf03c59c..00000000 --- a/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt +++ /dev/null @@ -1,309 +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.ViewGroup -import android.widget.CheckBox -import android.widget.ImageButton -import android.widget.LinearLayout -import android.widget.TextView -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.ContextCompat -import androidx.core.view.isVisible -import androidx.core.view.updateLayoutParams -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.LinearLayoutManager -import ani.dantotsu.R -import ani.dantotsu.connections.anilist.Anilist -import ani.dantotsu.connections.anilist.api.Notification -import ani.dantotsu.connections.anilist.api.NotificationType -import ani.dantotsu.connections.anilist.api.NotificationType.Companion.fromFormattedString -import ani.dantotsu.currContext -import ani.dantotsu.databinding.ActivityFollowBinding -import ani.dantotsu.initActivity -import ani.dantotsu.media.MediaDetailsActivity -import ani.dantotsu.navBarHeight -import ani.dantotsu.notifications.comment.CommentStore -import ani.dantotsu.notifications.subscription.SubscriptionStore -import ani.dantotsu.profile.ProfileActivity -import ani.dantotsu.settings.saving.PrefManager -import ani.dantotsu.settings.saving.PrefName -import ani.dantotsu.statusBarHeight -import ani.dantotsu.themes.ThemeManager -import ani.dantotsu.util.Logger -import com.xwray.groupie.GroupieAdapter -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -class NotificationActivity : AppCompatActivity() { - private lateinit var binding: ActivityFollowBinding - private lateinit var commentStore: List - private lateinit var subscriptionStore: List - private var adapter: GroupieAdapter = GroupieAdapter() - private var notificationList: List = emptyList() - val filters = ArrayList() - private var currentPage: Int = 1 - private var hasNextPage: Boolean = true - - @SuppressLint("ClickableViewAccessibility") - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - ThemeManager(this).applyTheme() - initActivity(this) - binding = ActivityFollowBinding.inflate(layoutInflater) - setContentView(binding.root) - binding.listTitle.text = getString(R.string.notifications) - binding.listToolbar.updateLayoutParams { - topMargin = statusBarHeight - } - binding.listFrameLayout.updateLayoutParams { - bottomMargin = navBarHeight - } - binding.listRecyclerView.adapter = adapter - binding.listRecyclerView.layoutManager = - LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) - binding.followerGrid.visibility = ViewGroup.GONE - binding.followerList.visibility = ViewGroup.GONE - binding.listBack.setOnClickListener { - onBackPressedDispatcher.onBackPressed() - } - binding.listProgressBar.visibility = ViewGroup.VISIBLE - commentStore = PrefManager.getNullableVal>( - PrefName.CommentNotificationStore, - null - ) ?: listOf() - subscriptionStore = PrefManager.getNullableVal>( - PrefName.SubscriptionNotificationStore, - null - ) ?: listOf() - - binding.followFilterButton.setOnClickListener { - val dialogView = LayoutInflater.from(currContext()).inflate(R.layout.custom_dialog_layout, null) - val checkboxContainer = dialogView.findViewById(R.id.checkboxContainer) - val tickAllButton = dialogView.findViewById(R.id.toggleButton) - val title = dialogView.findViewById(R.id.scantitle) - title.visibility = ViewGroup.GONE - fun getToggleImageResource(container: ViewGroup): Int { - var allChecked = true - var allUnchecked = true - - for (i in 0 until container.childCount) { - val checkBox = container.getChildAt(i) as CheckBox - if (!checkBox.isChecked) { - allChecked = false - } else { - allUnchecked = false - } - } - return when { - allChecked -> R.drawable.untick_all_boxes - allUnchecked -> R.drawable.tick_all_boxes - else -> R.drawable.invert_all_boxes - } - } - NotificationType.entries.forEach { notificationType -> - val checkBox = CheckBox(currContext()) - checkBox.text = notificationType.toFormattedString() - checkBox.isChecked = !filters.contains(notificationType.value.fromFormattedString()) - checkBox.setOnCheckedChangeListener { _, isChecked -> - if (isChecked) { - filters.remove(notificationType.value.fromFormattedString()) - } else { - filters.add(notificationType.value.fromFormattedString()) - } - tickAllButton.setImageResource(getToggleImageResource(checkboxContainer)) - } - checkboxContainer.addView(checkBox) - } - tickAllButton.setImageResource(getToggleImageResource(checkboxContainer)) - tickAllButton.setOnClickListener { - for (i in 0 until checkboxContainer.childCount) { - val checkBox = checkboxContainer.getChildAt(i) as CheckBox - checkBox.isChecked = !checkBox.isChecked - } - - tickAllButton.setImageResource(getToggleImageResource(checkboxContainer)) - } - val alertD = AlertDialog.Builder(this, R.style.MyPopup) - alertD.setTitle("Filter") - alertD.setView(dialogView) - alertD.setPositiveButton("OK") { _, _ -> - currentPage = 1 - hasNextPage = true - adapter.clear() - adapter.addAll(notificationList.filter { notification -> - !filters.contains(notification.notificationType) - }.map { - NotificationItem( - it, - ::onNotificationClick - ) - }) - loadPage(-1) { - binding.followRefresh.visibility = ViewGroup.GONE - } - } - alertD.setNegativeButton("Cancel") { _, _ -> } - val dialog = alertD.show() - dialog.window?.setDimAmount(0.8f) - } - - val activityId = intent.getIntExtra("activityId", -1) - lifecycleScope.launch { - loadPage(activityId) { - binding.listProgressBar.visibility = ViewGroup.GONE - } - withContext(Dispatchers.Main) { - binding.listProgressBar.visibility = ViewGroup.GONE - binding.listRecyclerView.setOnTouchListener { _, event -> - if (event?.action == MotionEvent.ACTION_UP) { - if (hasNextPage && !binding.listRecyclerView.canScrollVertically(1) && !binding.followRefresh.isVisible - && binding.listRecyclerView.adapter!!.itemCount != 0 && - (binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1) - ) { - binding.followRefresh.visibility = ViewGroup.VISIBLE - loadPage(-1) { - binding.followRefresh.visibility = ViewGroup.GONE - } - } - } - false - } - - binding.followSwipeRefresh.setOnRefreshListener { - currentPage = 1 - hasNextPage = true - adapter.clear() - notificationList = emptyList() - loadPage(-1) { - binding.followSwipeRefresh.isRefreshing = false - } - } - } - } - } - - private fun loadPage(activityId: Int, onFinish: () -> Unit = {}) { - lifecycleScope.launch(Dispatchers.IO) { - val resetNotification = activityId == -1 - val res = Anilist.query.getNotifications( - Anilist.userid ?: PrefManager.getVal(PrefName.AnilistUserId).toIntOrNull() - ?: 0, currentPage, resetNotification = resetNotification - ) - withContext(Dispatchers.Main) { - val newNotifications: MutableList = mutableListOf() - res?.data?.page?.notifications?.let { notifications -> - Logger.log("Notifications: $notifications") - newNotifications += if (activityId != -1) { - notifications.filter { it.id == activityId } - } else { - notifications - }.toMutableList() - } - if (activityId == -1) { - val furthestTime = newNotifications.minOfOrNull { it.createdAt } ?: 0 - commentStore.forEach { - if ((it.time > furthestTime * 1000L || !hasNextPage) && notificationList.none { notification -> - notification.commentId == it.commentId && notification.createdAt == (it.time / 1000L).toInt() - }) { - val notification = Notification( - it.type.toString(), - System.currentTimeMillis().toInt(), - commentId = it.commentId, - notificationType = it.type.toString(), - mediaId = it.mediaId, - context = it.title + "\n" + it.content, - createdAt = (it.time / 1000L).toInt(), - ) - newNotifications += notification - } - } - subscriptionStore.forEach { - if ((it.time > furthestTime * 1000L || !hasNextPage) && notificationList.none { notification -> - notification.mediaId == it.mediaId && notification.createdAt == (it.time / 1000L).toInt() - }) { - val notification = Notification( - it.type, - System.currentTimeMillis().toInt(), - commentId = it.mediaId, - mediaId = it.mediaId, - notificationType = it.type, - context = it.title + ": " + it.content, - createdAt = (it.time / 1000L).toInt(), - image = it.image, - banner = it.banner ?: it.image - ) - newNotifications += notification - } - } - newNotifications.sortByDescending { it.createdAt } - } - - notificationList += newNotifications - adapter.addAll(newNotifications.filter { notification -> - !filters.contains(notification.notificationType) - }.map { - NotificationItem( - it, - ::onNotificationClick - ) - }) - currentPage = res?.data?.page?.pageInfo?.currentPage?.plus(1) ?: 1 - hasNextPage = res?.data?.page?.pageInfo?.hasNextPage ?: false - binding.followSwipeRefresh.isRefreshing = false - onFinish() - } - } - } - - private fun onNotificationClick(id: Int, optional: Int?, type: NotificationClickType) { - when (type) { - NotificationClickType.USER -> { - ContextCompat.startActivity( - this, Intent(this, ProfileActivity::class.java) - .putExtra("userId", id), null - ) - } - - NotificationClickType.MEDIA -> { - ContextCompat.startActivity( - this, Intent(this, MediaDetailsActivity::class.java) - .putExtra("mediaId", id), null - ) - } - - NotificationClickType.ACTIVITY -> { - ContextCompat.startActivity( - this, Intent(this, FeedActivity::class.java) - .putExtra("activityId", id), null - ) - } - - NotificationClickType.COMMENT -> { - ContextCompat.startActivity( - this, Intent(this, MediaDetailsActivity::class.java) - .putExtra("FRAGMENT_TO_LOAD", "COMMENTS") - .putExtra("mediaId", id) - .putExtra("commentId", optional ?: -1), - null - ) - - } - - NotificationClickType.UNDEFINED -> { - // Do nothing - } - } - } - - companion object { - enum class NotificationClickType { - USER, MEDIA, ACTIVITY, COMMENT, UNDEFINED - } - } -} \ 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 new file mode 100644 index 00000000..ea0de673 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt @@ -0,0 +1,94 @@ +package ani.dantotsu.profile.notification + +import android.os.Bundle +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle +import androidx.viewpager2.adapter.FragmentStateAdapter +import androidx.viewpager2.widget.ViewPager2 +import ani.dantotsu.R +import ani.dantotsu.databinding.ActivityNotificationBinding +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() { + private lateinit var binding: ActivityNotificationBinding + private var selected: Int = 0 + lateinit var navBar: AnimatedBottomBar + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + ThemeManager(this).applyTheme() + initActivity(this) + binding = ActivityNotificationBinding.inflate(layoutInflater) + setContentView(binding.root) + binding.notificationTitle.text = getString(R.string.notifications) + binding.notificationToolbar.updateLayoutParams { + topMargin = statusBarHeight + } + navBar = binding.notificationNavBar + binding.root.updateLayoutParams { + bottomMargin = navBarHeight + } + val tabs = listOf( + Pair(R.drawable.ic_round_movie_filter_24, "Media"), + Pair(R.drawable.ic_round_person_24, "User"), + Pair(R.drawable.ic_round_notifications_active_24, "Subs"), + Pair(R.drawable.ic_round_comment_24, "Comments") + ) + tabs.forEach { (icon, title) -> navBar.addTab(navBar.createTab(icon, title)) } + + binding.notificationBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() } + val getOne = intent.getIntExtra("activityId", -1) + if (getOne != -1) navBar.isVisible = false + binding.notificationViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, getOne) + binding.notificationViewPager.setOffscreenPageLimit(4) + binding.notificationViewPager.setCurrentItem(selected, false) + navBar.selectTabAt(selected) + navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener { + override fun onTabSelected( + lastIndex: Int, + lastTab: AnimatedBottomBar.Tab?, + newIndex: Int, + newTab: AnimatedBottomBar.Tab + ) { + selected = newIndex + binding.notificationViewPager.setCurrentItem(selected, true) + } + }) + binding.notificationViewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + navBar.selectTabAt(position) + } + }) + } + override fun onResume() { + super.onResume() + if (this::navBar.isInitialized) { + navBar.selectTabAt(selected) + } + } + private class ViewPagerAdapter( + fragmentManager: FragmentManager, + lifecycle: Lifecycle, + val id: Int = -1 + ) : FragmentStateAdapter(fragmentManager, lifecycle) { + override fun getItemCount(): Int = if (id != -1) 1 else 4 + + override fun createFragment(position: Int): Fragment = when (position) { + 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 new file mode 100644 index 00000000..1c6b827e --- /dev/null +++ b/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt @@ -0,0 +1,228 @@ +package ani.dantotsu.profile.notification + +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 androidx.webkit.internal.ApiFeature.N +import ani.dantotsu.R +import ani.dantotsu.connections.anilist.Anilist +import ani.dantotsu.connections.anilist.api.Notification +import ani.dantotsu.databinding.FragmentNotificationsBinding +import ani.dantotsu.media.MediaDetailsActivity +import ani.dantotsu.navBarHeight +import ani.dantotsu.notifications.comment.CommentStore +import ani.dantotsu.notifications.subscription.SubscriptionStore +import ani.dantotsu.profile.ProfileActivity +import ani.dantotsu.profile.activity.FeedActivity +import ani.dantotsu.setBaseline +import ani.dantotsu.settings.saving.PrefManager +import ani.dantotsu.settings.saving.PrefName +import com.xwray.groupie.GroupieAdapter +import kotlinx.coroutines.launch + + +class NotificationFragment( + val type: NotificationType, + val getID: Int = -1 +) : Fragment() { + private lateinit var binding: FragmentNotificationsBinding + private var adapter: GroupieAdapter = GroupieAdapter() + private var currentPage = 1 + private var hasNextPage = false + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentNotificationsBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val navbar = (activity as NotificationActivity).navBar + binding.notificationRecyclerView.setBaseline(navbar) + binding.notificationRecyclerView.adapter = adapter + binding.notificationRecyclerView.layoutManager = LinearLayoutManager(context) + binding.notificationProgressBar.isVisible = true + binding.notificationRefresh.updateLayoutParams { + bottomMargin = navBarHeight + } + binding.emptyTextView.text = getString(R.string.no_notifications) + lifecycleScope.launch { + getList() + if (adapter.itemCount == 0) { + binding.emptyTextView.isVisible = true + } + binding.notificationProgressBar.isVisible = false + } + binding.notificationSwipeRefresh.setOnRefreshListener { + lifecycleScope.launch { + adapter.clear() + currentPage = 1 + getList() + binding.notificationSwipeRefresh.isRefreshing = false + } + } + binding.notificationRecyclerView.addOnScrollListener(object : + RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (shouldLoadMore()) { + lifecycleScope.launch { + binding.notificationRefresh.isVisible = true + getList() + binding.notificationRefresh.isVisible = false + } + } + } + }) + + } + + private suspend fun getList() { + val list = when (type) { + NotificationType.ONE -> getNotificationsFiltered(false) { it.id == getID } + NotificationType.MEDIA -> getNotificationsFiltered { it.media != null } + NotificationType.USER -> getNotificationsFiltered { it.media == null } + NotificationType.SUBSCRIPTION -> getSubscriptions() + NotificationType.COMMENT -> getComments() + } + adapter.addAll(list.map { NotificationItem(it, ::onClick) }) + } + + private suspend fun getNotificationsFiltered( + reset: Boolean = true, + filter: (Notification) -> Boolean + ): List { + val userId = + Anilist.userid ?: PrefManager.getVal(PrefName.AnilistUserId).toIntOrNull() ?: 0 + val res = Anilist.query.getNotifications(userId, currentPage, reset)?.data?.page + currentPage = res?.pageInfo?.currentPage?.plus(1) ?: 1 + hasNextPage = res?.pageInfo?.hasNextPage ?: false + return res?.notifications?.filter(filter) ?: listOf() + } + + private fun getSubscriptions(): List { + val list = PrefManager.getNullableVal>( + PrefName.SubscriptionNotificationStore, + null + ) ?: listOf() + return list.sortedByDescending { (it.time / 1000L).toInt() } + .filter { it.image != null }.map { + Notification( + it.type, + System.currentTimeMillis().toInt(), + commentId = it.mediaId, + mediaId = it.mediaId, + notificationType = it.type, + context = it.title + ": " + it.content, + createdAt = (it.time / 1000L).toInt(), + image = it.image, + banner = it.banner ?: it.image + ) + } + } + + private fun getComments(): List { + val list = PrefManager.getNullableVal>( + PrefName.CommentNotificationStore, + null + ) ?: listOf() + return list + .sortedByDescending { (it.time / 1000L).toInt() } + .map { + Notification( + it.type.toString(), + System.currentTimeMillis().toInt(), + commentId = it.commentId, + notificationType = it.type.toString(), + mediaId = it.mediaId, + context = it.title + "\n" + it.content, + createdAt = (it.time / 1000L).toInt(), + ) + } + } + + private fun shouldLoadMore(): Boolean { + val layoutManager = + (binding.notificationRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() + val adapter = binding.notificationRecyclerView.adapter + + return hasNextPage && !binding.notificationRefresh.isVisible && adapter?.itemCount != 0 && + layoutManager == (adapter!!.itemCount - 1) && + !binding.notificationRecyclerView.canScrollVertically(1) + } + + fun onClick( + id: Int, + optional: Int?, + type: NotificationClickType + ) { + when (type) { + NotificationClickType.USER -> { + ContextCompat.startActivity( + requireContext(), Intent(requireContext(), ProfileActivity::class.java) + .putExtra("userId", id), null + ) + } + + NotificationClickType.MEDIA -> { + ContextCompat.startActivity( + requireContext(), Intent(requireContext(), MediaDetailsActivity::class.java) + .putExtra("mediaId", id), null + ) + } + + NotificationClickType.ACTIVITY -> { + ContextCompat.startActivity( + requireContext(), Intent(requireContext(), FeedActivity::class.java) + .putExtra("activityId", id), null + ) + } + + NotificationClickType.COMMENT -> { + ContextCompat.startActivity( + requireContext(), Intent(requireContext(), MediaDetailsActivity::class.java) + .putExtra("FRAGMENT_TO_LOAD", "COMMENTS") + .putExtra("mediaId", id) + .putExtra("commentId", optional ?: -1), + null + ) + + } + + NotificationClickType.UNDEFINED -> { + // Do nothing + } + } + } + + override fun onResume() { + super.onResume() + if (this::binding.isInitialized) { + binding.root.requestLayout() + binding.root.setBaseline((activity as NotificationActivity).navBar) + } + } + + companion object { + 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/java/ani/dantotsu/profile/activity/NotificationItem.kt b/app/src/main/java/ani/dantotsu/profile/notification/NotificationItem.kt similarity index 98% rename from app/src/main/java/ani/dantotsu/profile/activity/NotificationItem.kt rename to app/src/main/java/ani/dantotsu/profile/notification/NotificationItem.kt index d0e932d9..f51225ea 100644 --- a/app/src/main/java/ani/dantotsu/profile/activity/NotificationItem.kt +++ b/app/src/main/java/ani/dantotsu/profile/notification/NotificationItem.kt @@ -1,4 +1,4 @@ -package ani.dantotsu.profile.activity +package ani.dantotsu.profile.notification import android.view.View import android.view.ViewGroup @@ -8,7 +8,8 @@ import ani.dantotsu.connections.anilist.api.Notification import ani.dantotsu.connections.anilist.api.NotificationType import ani.dantotsu.databinding.ItemNotificationBinding import ani.dantotsu.loadImage -import ani.dantotsu.profile.activity.NotificationActivity.Companion.NotificationClickType +import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationClickType +import ani.dantotsu.profile.activity.ActivityItemBuilder import ani.dantotsu.setAnimation import ani.dantotsu.toPx import com.xwray.groupie.viewbinding.BindableItem diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt b/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt index 70da38d4..1072d742 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt @@ -1,6 +1,5 @@ package ani.dantotsu.settings -import android.app.AlertDialog import android.content.Intent import android.graphics.Color import android.os.Bundle @@ -16,7 +15,6 @@ import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.databinding.BottomSheetSettingsBinding import ani.dantotsu.download.anime.OfflineAnimeFragment import ani.dantotsu.download.manga.OfflineMangaFragment -import ani.dantotsu.getAppString import ani.dantotsu.getThemeColor import ani.dantotsu.home.AnimeFragment import ani.dantotsu.home.HomeFragment @@ -28,7 +26,7 @@ import ani.dantotsu.loadImage import ani.dantotsu.offline.OfflineFragment import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.activity.FeedActivity -import ani.dantotsu.profile.activity.NotificationActivity +import ani.dantotsu.profile.notification.NotificationActivity import ani.dantotsu.setSafeOnClickListener import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName diff --git a/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt b/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt index d2ab6601..14e71432 100644 --- a/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt +++ b/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt @@ -135,6 +135,7 @@ class AlertDialogBuilder(private val context: Context) { } else if (checkedItems != null && onItemsSelected != null) { builder.setMultiChoiceItems(items, checkedItems) { _, which, isChecked -> checkedItems?.set(which, isChecked) + onItemsSelected?.invoke(checkedItems!!) } } } 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/activity_follow.xml b/app/src/main/res/layout/activity_follow.xml index 9ec387c4..ded5b53f 100644 --- a/app/src/main/res/layout/activity_follow.xml +++ b/app/src/main/res/layout/activity_follow.xml @@ -103,15 +103,18 @@ - + android:layout_height="match_parent" + android:orientation="vertical" + android:gravity="center_horizontal"> @@ -132,8 +135,8 @@ style="?android:attr/progressBarStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="bottom|center_horizontal" android:layout_marginBottom="32dp" - android:visibility="gone" /> - + android:visibility="gone" + android:layout_gravity="center_horizontal" /> + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_notification.xml b/app/src/main/res/layout/activity_notification.xml new file mode 100644 index 00000000..bfdda606 --- /dev/null +++ b/app/src/main/res/layout/activity_notification.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/bottom_sheet_settings.xml b/app/src/main/res/layout/bottom_sheet_settings.xml index 2d919ff3..66b041b7 100644 --- a/app/src/main/res/layout/bottom_sheet_settings.xml +++ b/app/src/main/res/layout/bottom_sheet_settings.xml @@ -85,7 +85,7 @@ diff --git a/app/src/main/res/layout/fragment_feed.xml b/app/src/main/res/layout/fragment_feed.xml index d1c86904..e341d0a9 100644 --- a/app/src/main/res/layout/fragment_feed.xml +++ b/app/src/main/res/layout/fragment_feed.xml @@ -4,50 +4,62 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - + android:layout_height="match_parent" + android:gravity="center" + tools:visibility="gone"> + + + + + + + - - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_notifications.xml b/app/src/main/res/layout/fragment_notifications.xml new file mode 100644 index 00000000..21a2cb73 --- /dev/null +++ b/app/src/main/res/layout/fragment_notifications.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_status.xml b/app/src/main/res/layout/fragment_status.xml index 0453c04f..9cb56109 100644 --- a/app/src/main/res/layout/fragment_status.xml +++ b/app/src/main/res/layout/fragment_status.xml @@ -28,45 +28,36 @@ android:src="@drawable/linear_gradient_bg" tools:ignore="ContentDescription" /> + + + android:layout_marginHorizontal="16dp" + android:layout_marginVertical="94dp" + android:clipToPadding="false" + android:scrollbars="none"> - - - -