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">
-
-
-
-