feat: notifications page rework
This commit is contained in:
parent
37949c7e8e
commit
2b4c9bf7a9
11 changed files with 487 additions and 324 deletions
|
@ -194,7 +194,7 @@
|
|||
android:label="Inbox Activity"
|
||||
android:parentActivityName=".MainActivity" />
|
||||
<activity
|
||||
android:name=".profile.activity.NotificationActivity"
|
||||
android:name=".profile.notification.NotificationActivity"
|
||||
android:label="Inbox Activity"
|
||||
android:parentActivityName=".MainActivity" />
|
||||
<activity
|
||||
|
|
|
@ -51,7 +51,7 @@ import ani.dantotsu.others.CustomBottomDialog
|
|||
import ani.dantotsu.others.calc.CalcActivity
|
||||
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.settings.ExtensionsActivity
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefManager.asLiveBool
|
||||
|
@ -365,7 +365,6 @@ class MainActivity : AppCompatActivity() {
|
|||
} else if (fragmentToLoad == "NOTIFICATIONS" && activityId != -1) {
|
||||
Logger.log("MainActivity, onCreate: $activityId")
|
||||
val notificationIntent = Intent(this, NotificationActivity::class.java).apply {
|
||||
putExtra("FRAGMENT_TO_LOAD", "NOTIFICATIONS")
|
||||
putExtra("activityId", activityId)
|
||||
}
|
||||
launched = true
|
||||
|
|
|
@ -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<CommentStore>
|
||||
private lateinit var subscriptionStore: List<SubscriptionStore>
|
||||
private var adapter: GroupieAdapter = GroupieAdapter()
|
||||
private var notificationList: List<Notification> = emptyList()
|
||||
val filters = ArrayList<String>()
|
||||
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<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = statusBarHeight
|
||||
}
|
||||
binding.listFrameLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
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<List<CommentStore>>(
|
||||
PrefName.CommentNotificationStore,
|
||||
null
|
||||
) ?: listOf()
|
||||
subscriptionStore = PrefManager.getNullableVal<List<SubscriptionStore>>(
|
||||
PrefName.SubscriptionNotificationStore,
|
||||
null
|
||||
) ?: listOf()
|
||||
|
||||
binding.followFilterButton.setOnClickListener {
|
||||
val dialogView = LayoutInflater.from(currContext()).inflate(R.layout.custom_dialog_layout, null)
|
||||
val checkboxContainer = dialogView.findViewById<LinearLayout>(R.id.checkboxContainer)
|
||||
val tickAllButton = dialogView.findViewById<ImageButton>(R.id.toggleButton)
|
||||
val title = dialogView.findViewById<TextView>(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<String>(PrefName.AnilistUserId).toIntOrNull()
|
||||
?: 0, currentPage, resetNotification = resetNotification
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
val newNotifications: MutableList<Notification> = 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.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 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<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = statusBarHeight
|
||||
}
|
||||
navBar = binding.notificationNavBar
|
||||
binding.root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = navBarHeight
|
||||
}
|
||||
val mediaTab = navBar.createTab(R.drawable.ic_round_movie_filter_24, "Media")
|
||||
val userTab = navBar.createTab(R.drawable.ic_round_person_24, "User")
|
||||
val subscriptionTab = navBar.createTab(R.drawable.ic_round_notifications_active_24, "Subscriptions")
|
||||
val commentTab = navBar.createTab(R.drawable.ic_round_comment_24, "Comments")
|
||||
navBar.addTab(mediaTab)
|
||||
navBar.addTab(userTab)
|
||||
navBar.addTab(subscriptionTab)
|
||||
navBar.addTab(commentTab)
|
||||
binding.notificationBack.setOnClickListener {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
val getOne = intent.getIntExtra("activityId", -1)
|
||||
binding.notificationViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, getOne)
|
||||
binding.notificationViewPager.setOffscreenPageLimit(4)
|
||||
binding.notificationViewPager.setCurrentItem(selected, false)
|
||||
binding.notificationViewPager
|
||||
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() {
|
||||
if (this::navBar.isInitialized) {
|
||||
navBar.selectTabAt(selected)
|
||||
}
|
||||
super.onResume()
|
||||
}
|
||||
private class ViewPagerAdapter(
|
||||
fragmentManager: FragmentManager,
|
||||
lifecycle: Lifecycle,
|
||||
val id: Int = -1
|
||||
) : FragmentStateAdapter(fragmentManager, lifecycle) {
|
||||
override fun getItemCount(): Int = 4
|
||||
|
||||
override fun createFragment(position: Int): Fragment = when (position) {
|
||||
0 -> NotificationFragment(if (id != -1) "getOne" else "media", id)
|
||||
1 -> NotificationFragment("user")
|
||||
2 -> NotificationFragment("subscription")
|
||||
3 -> NotificationFragment("comment")
|
||||
else -> NotificationFragment("media")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
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.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.Notification
|
||||
import ani.dantotsu.databinding.FragmentNotificationsBinding
|
||||
import ani.dantotsu.media.MediaDetailsActivity
|
||||
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.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class NotificationFragment(val type: String, private 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.listRecyclerView.setBaseline(navbar)
|
||||
binding.listRecyclerView.adapter = adapter
|
||||
binding.listRecyclerView.layoutManager = LinearLayoutManager(context)
|
||||
binding.emptyTextView.text = getString(R.string.no_notifications)
|
||||
lifecycleScope.launch {
|
||||
val list = when (type) {
|
||||
"getOne" -> getOne(getID)
|
||||
"media" -> getMediaUpdates()
|
||||
"user" -> getUserUpdates()
|
||||
"subscription" -> getSubscriptions()
|
||||
"comment" -> getComments()
|
||||
else -> listOf()
|
||||
}
|
||||
adapter.addAll(list.map { NotificationItem(it, ::onClick) })
|
||||
if (adapter.itemCount != 0) {
|
||||
binding.listProgressBar.isVisible = false
|
||||
}else{
|
||||
binding.listProgressBar.isVisible = false
|
||||
binding.emptyTextView.isVisible = true
|
||||
}
|
||||
}
|
||||
binding.followSwipeRefresh.setOnRefreshListener {
|
||||
lifecycleScope.launch {
|
||||
adapter.clear()
|
||||
currentPage = 1
|
||||
val list = when (type) {
|
||||
"getOne" -> getOne(getID)
|
||||
"media" -> getMediaUpdates()
|
||||
"user" -> getUserUpdates()
|
||||
"subscription" -> getSubscriptions()
|
||||
"comment" -> getComments()
|
||||
else -> listOf()
|
||||
}
|
||||
adapter.addAll(list.map { NotificationItem(it, ::onClick) })
|
||||
binding.followSwipeRefresh.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 {
|
||||
val list = when (type) {
|
||||
"media" -> getMediaUpdates()
|
||||
"user" -> getUserUpdates()
|
||||
else -> listOf()
|
||||
}
|
||||
binding.followRefresh.visibility = View.VISIBLE
|
||||
adapter.addAll(list.map {
|
||||
NotificationItem(
|
||||
it,
|
||||
::onClick
|
||||
)
|
||||
})
|
||||
binding.followRefresh.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
private suspend fun getNotificationsFiltered(filter: (Notification) -> Boolean): List<Notification> {
|
||||
val userId = Anilist.userid ?: PrefManager.getVal<String>(PrefName.AnilistUserId).toIntOrNull() ?: 0
|
||||
val res = withContext(Dispatchers.IO) {
|
||||
Anilist.query.getNotifications(userId, currentPage)
|
||||
}
|
||||
currentPage = res?.data?.page?.pageInfo?.currentPage?.plus(1) ?: 1
|
||||
hasNextPage = res?.data?.page?.pageInfo?.hasNextPage ?: false
|
||||
return res?.data?.page?.notifications?.filter(filter) ?: listOf()
|
||||
}
|
||||
|
||||
private suspend fun getMediaUpdates(): List<Notification> {
|
||||
return getNotificationsFiltered { it.media != null }
|
||||
}
|
||||
|
||||
private suspend fun getUserUpdates(): List<Notification> {
|
||||
return getNotificationsFiltered { it.media == null }
|
||||
}
|
||||
|
||||
private suspend fun getOne(id: Int): List<Notification> {
|
||||
return getNotificationsFiltered { it.id == id }
|
||||
}
|
||||
|
||||
private fun getSubscriptions(): List<Notification> {
|
||||
val list = PrefManager.getNullableVal<List<SubscriptionStore>>(
|
||||
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<Notification> {
|
||||
val list = PrefManager.getNullableVal<List<CommentStore>>(
|
||||
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.listRecyclerView.layoutManager as LinearLayoutManager
|
||||
val adapter = binding.listRecyclerView.adapter
|
||||
|
||||
return hasNextPage && !binding.followRefresh.isVisible && adapter?.itemCount != 0 &&
|
||||
layoutManager.findLastVisibleItemPosition() == (adapter!!.itemCount - 1) &&
|
||||
!binding.listRecyclerView.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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,15 +103,18 @@
|
|||
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
<LinearLayout
|
||||
android:id="@+id/listFrameLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/followSwipeRefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:clipChildren="false"
|
||||
|
@ -120,7 +123,7 @@
|
|||
<ani.dantotsu.FadingEdgeRecyclerView
|
||||
android:id="@+id/listRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:nestedScrollingEnabled="true"
|
||||
android:requiresFadingEdge="vertical"
|
||||
tools:listitem="@layout/item_follower" />
|
||||
|
@ -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" />
|
||||
</FrameLayout>
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
75
app/src/main/res/layout/activity_notification.xml
Normal file
75
app/src/main/res/layout/activity_notification.xml
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/notificationToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/notificationBack"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginStart="12dp"
|
||||
android:src="@drawable/ic_round_arrow_back_ios_new_24"
|
||||
app:tint="?attr/colorOnBackground"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notificationTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="44dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:gravity="center|start"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
|
||||
android:textColor="?attr/colorOnBackground"
|
||||
android:textSize="18sp"
|
||||
tools:text="Notifications" />
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/notificationViewPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:nestedScrollingEnabled="true"
|
||||
tools:ignore="SpeakableTextPresentCheck" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<nl.joery.animatedbottombar.AnimatedBottomBar
|
||||
android:id="@+id/notificationNavBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal|bottom"
|
||||
android:layout_marginTop="-64dp"
|
||||
android:background="?attr/colorSurface"
|
||||
android:padding="0dp"
|
||||
app:abb_animationInterpolator="@anim/over_shoot"
|
||||
app:abb_indicatorAppearance="round"
|
||||
app:abb_indicatorLocation="top"
|
||||
app:abb_selectedTabType="text"
|
||||
app:abb_textAppearance="@style/NavBarText"
|
||||
app:itemActiveIndicatorStyle="@style/BottomNavBar"
|
||||
app:itemIconTint="@color/tab_layout_icon"
|
||||
app:itemRippleColor="#00000000"
|
||||
app:itemTextAppearanceActive="@style/NavBarText"
|
||||
app:itemTextAppearanceInactive="@style/NavBarText"
|
||||
app:itemTextColor="@color/tab_layout_icon" />
|
||||
</LinearLayout>
|
63
app/src/main/res/layout/fragment_notifications.xml
Normal file
63
app/src/main/res/layout/fragment_notifications.xml
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/listProgressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
tools:visibility="gone">
|
||||
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/listFrameLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal">
|
||||
<TextView
|
||||
android:id="@+id/emptyTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:fontFamily="@font/poppins_semi_bold" />
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/followSwipeRefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
<ani.dantotsu.FadingEdgeRecyclerView
|
||||
android:id="@+id/listRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:nestedScrollingEnabled="true"
|
||||
android:requiresFadingEdge="vertical"
|
||||
tools:listitem="@layout/item_follower" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/followRefresh"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
Loading…
Add table
Add a link
Reference in a new issue