feat: notifications page rework

This commit is contained in:
aayush262 2024-05-26 00:40:46 +05:30
parent 37949c7e8e
commit 2b4c9bf7a9
11 changed files with 487 additions and 324 deletions

View file

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

View file

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

View file

@ -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
}
}
}

View file

@ -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")
}
}
}

View file

@ -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
}
}
}

View file

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

View file

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

View file

@ -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!!)
}
}
}

View file

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

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

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