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:label="Inbox Activity"
|
||||||
android:parentActivityName=".MainActivity" />
|
android:parentActivityName=".MainActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".profile.activity.NotificationActivity"
|
android:name=".profile.notification.NotificationActivity"
|
||||||
android:label="Inbox Activity"
|
android:label="Inbox Activity"
|
||||||
android:parentActivityName=".MainActivity" />
|
android:parentActivityName=".MainActivity" />
|
||||||
<activity
|
<activity
|
||||||
|
|
|
@ -51,7 +51,7 @@ import ani.dantotsu.others.CustomBottomDialog
|
||||||
import ani.dantotsu.others.calc.CalcActivity
|
import ani.dantotsu.others.calc.CalcActivity
|
||||||
import ani.dantotsu.profile.ProfileActivity
|
import ani.dantotsu.profile.ProfileActivity
|
||||||
import ani.dantotsu.profile.activity.FeedActivity
|
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.ExtensionsActivity
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefManager.asLiveBool
|
import ani.dantotsu.settings.saving.PrefManager.asLiveBool
|
||||||
|
@ -365,7 +365,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
} else if (fragmentToLoad == "NOTIFICATIONS" && activityId != -1) {
|
} else if (fragmentToLoad == "NOTIFICATIONS" && activityId != -1) {
|
||||||
Logger.log("MainActivity, onCreate: $activityId")
|
Logger.log("MainActivity, onCreate: $activityId")
|
||||||
val notificationIntent = Intent(this, NotificationActivity::class.java).apply {
|
val notificationIntent = Intent(this, NotificationActivity::class.java).apply {
|
||||||
putExtra("FRAGMENT_TO_LOAD", "NOTIFICATIONS")
|
|
||||||
putExtra("activityId", activityId)
|
putExtra("activityId", activityId)
|
||||||
}
|
}
|
||||||
launched = true
|
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.View
|
||||||
import android.view.ViewGroup
|
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.connections.anilist.api.NotificationType
|
||||||
import ani.dantotsu.databinding.ItemNotificationBinding
|
import ani.dantotsu.databinding.ItemNotificationBinding
|
||||||
import ani.dantotsu.loadImage
|
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.setAnimation
|
||||||
import ani.dantotsu.toPx
|
import ani.dantotsu.toPx
|
||||||
import com.xwray.groupie.viewbinding.BindableItem
|
import com.xwray.groupie.viewbinding.BindableItem
|
|
@ -1,6 +1,5 @@
|
||||||
package ani.dantotsu.settings
|
package ani.dantotsu.settings
|
||||||
|
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -16,7 +15,6 @@ import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.databinding.BottomSheetSettingsBinding
|
import ani.dantotsu.databinding.BottomSheetSettingsBinding
|
||||||
import ani.dantotsu.download.anime.OfflineAnimeFragment
|
import ani.dantotsu.download.anime.OfflineAnimeFragment
|
||||||
import ani.dantotsu.download.manga.OfflineMangaFragment
|
import ani.dantotsu.download.manga.OfflineMangaFragment
|
||||||
import ani.dantotsu.getAppString
|
|
||||||
import ani.dantotsu.getThemeColor
|
import ani.dantotsu.getThemeColor
|
||||||
import ani.dantotsu.home.AnimeFragment
|
import ani.dantotsu.home.AnimeFragment
|
||||||
import ani.dantotsu.home.HomeFragment
|
import ani.dantotsu.home.HomeFragment
|
||||||
|
@ -28,7 +26,7 @@ import ani.dantotsu.loadImage
|
||||||
import ani.dantotsu.offline.OfflineFragment
|
import ani.dantotsu.offline.OfflineFragment
|
||||||
import ani.dantotsu.profile.ProfileActivity
|
import ani.dantotsu.profile.ProfileActivity
|
||||||
import ani.dantotsu.profile.activity.FeedActivity
|
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.setSafeOnClickListener
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
|
|
|
@ -135,6 +135,7 @@ class AlertDialogBuilder(private val context: Context) {
|
||||||
} else if (checkedItems != null && onItemsSelected != null) {
|
} else if (checkedItems != null && onItemsSelected != null) {
|
||||||
builder.setMultiChoiceItems(items, checkedItems) { _, which, isChecked ->
|
builder.setMultiChoiceItems(items, checkedItems) { _, which, isChecked ->
|
||||||
checkedItems?.set(which, isChecked)
|
checkedItems?.set(which, isChecked)
|
||||||
|
onItemsSelected?.invoke(checkedItems!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,15 +103,18 @@
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<LinearLayout
|
||||||
android:id="@+id/listFrameLayout"
|
android:id="@+id/listFrameLayout"
|
||||||
android:layout_width="match_parent"
|
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
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
android:id="@+id/followSwipeRefresh"
|
android:id="@+id/followSwipeRefresh"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
|
@ -120,7 +123,7 @@
|
||||||
<ani.dantotsu.FadingEdgeRecyclerView
|
<ani.dantotsu.FadingEdgeRecyclerView
|
||||||
android:id="@+id/listRecyclerView"
|
android:id="@+id/listRecyclerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:nestedScrollingEnabled="true"
|
android:nestedScrollingEnabled="true"
|
||||||
android:requiresFadingEdge="vertical"
|
android:requiresFadingEdge="vertical"
|
||||||
tools:listitem="@layout/item_follower" />
|
tools:listitem="@layout/item_follower" />
|
||||||
|
@ -132,8 +135,8 @@
|
||||||
style="?android:attr/progressBarStyle"
|
style="?android:attr/progressBarStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|center_horizontal"
|
|
||||||
android:layout_marginBottom="32dp"
|
android:layout_marginBottom="32dp"
|
||||||
android:visibility="gone" />
|
android:visibility="gone"
|
||||||
</FrameLayout>
|
android:layout_gravity="center_horizontal" />
|
||||||
|
</LinearLayout>
|
||||||
</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