This commit is contained in:
rebelonion 2024-05-27 05:11:48 -05:00
commit 0b32636c1b
25 changed files with 852 additions and 679 deletions

View file

@ -108,7 +108,7 @@ jobs:
#Telegram
curl -F "chat_id=${{ secrets.TELEGRAM_CHANNEL_ID }}" \
-F "document=@app/build/outputs/apk/google/alpha/app-google-universal-alpha.apk" \
-F "document=@app/build/outputs/apk/google/alpha/app-google-alpha.apk" \
-F "caption=Alpha-Build: ${VERSION}: ${commit_messages}" \
https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendDocument

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

@ -435,7 +435,7 @@ class AnilistQueries {
response.data.page2.activities
).asSequence().flatten()
.filter { it.typename != "MessageActivity" }
.filter { if (Anilist.adult) true else it.media?.isAdult == false }
.filter { if (Anilist.adult) true else it.media?.isAdult != true }
.filter { it.createdAt * 1000L > threeDaysAgo }.toList()
.sortedByDescending { it.createdAt }
val anilistActivities = mutableListOf<User>()

View file

@ -268,8 +268,9 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
LinearLayoutManager.HORIZONTAL,
false
)
MediaListViewActivity.passedMedia = media.toCollection(ArrayList())
more.setOnClickListener {
MediaListViewActivity.passedMedia = media.toCollection(ArrayList())
ContextCompat.startActivity(
it.context, Intent(it.context, MediaListViewActivity::class.java)
.putExtra("title", string),

View file

@ -19,6 +19,7 @@ import ani.dantotsu.snackString
import ani.dantotsu.util.MarkdownCreatorActivity
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -58,29 +59,32 @@ class RepliesBottomDialog : BottomSheetDialogFragment() {
activityId = requireArguments().getInt("activityId")
loading(true)
lifecycleScope.launch(Dispatchers.IO) {
val response = Anilist.query.getReplies(activityId)
withContext(Dispatchers.Main) {
loading(false)
if (response != null) {
replies.clear()
replies.addAll(response.data.page.activityReplies)
adapter.update(
replies.map {
ActivityReplyItem(
it,
requireActivity(),
clickCallback = { int, _ ->
onClick(int)
}
)
}
)
} else {
snackString("Failed to load replies")
}
loadData()
}
}
private suspend fun loadData() {
val response = Anilist.query.getReplies(activityId)
withContext(Dispatchers.Main) {
loading(false)
if (response != null) {
replies.clear()
replies.addAll(response.data.page.activityReplies)
adapter.update(
replies.map {
ActivityReplyItem(
it,
requireActivity(),
clickCallback = { int, _ ->
onClick(int)
}
)
}
)
} else {
snackString("Failed to load replies")
}
}
}
private fun onClick(int: Int) {
@ -101,6 +105,15 @@ class RepliesBottomDialog : BottomSheetDialogFragment() {
super.onDestroyView()
}
override fun onResume() {
super.onResume()
loading(true)
lifecycleScope.launch(Dispatchers.IO) {
delay(2000)
loadData()
}
}
companion object {
fun newInstance(activityId: Int): RepliesBottomDialog {
return RepliesBottomDialog().apply {

View file

@ -74,8 +74,7 @@ class Stories @JvmOverloads constructor(
if (context is StoriesCallback) storiesListener = context as StoriesCallback
binding.leftTouchPanel.setOnTouchListener(this)
binding.rightTouchPanel.setOnTouchListener(this)
binding.touchPanel.setOnTouchListener(this)
}
@ -264,49 +263,7 @@ class Stories @JvmOverloads constructor(
}
private var startClickTime = 0L
private var startX = 0f
private var startY = 0f
private var isLongPress = false
private val swipeThreshold = 100
override fun onTouch(view: View?, event: MotionEvent?): Boolean {
val maxClickDuration = 200
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
startX = event.x
startY = event.y
startClickTime = Calendar.getInstance().timeInMillis
pause()
isLongPress = false
}
MotionEvent.ACTION_MOVE -> {
val deltaX = event.x - startX
val deltaY = event.y - startY
if (!isLongPress && (abs(deltaX) > swipeThreshold || abs(deltaY) > swipeThreshold)) {
isLongPress = true
}
}
MotionEvent.ACTION_UP -> {
val clickDuration = Calendar.getInstance().timeInMillis - startClickTime
if (clickDuration < maxClickDuration && !isLongPress) {
when (view?.id) {
R.id.leftTouchPanel -> leftPanelTouch()
R.id.rightTouchPanel -> rightPanelTouch()
}
} else {
resume()
}
val deltaX = event.x - startX
if (abs(deltaX) > swipeThreshold) {
if (deltaX > 0) onStoriesPrevious()
else onStoriesCompleted()
}
}
}
return true
}
private fun rightPanelTouch() {
Logger.log("rightPanelTouch: $storyIndex")
@ -359,6 +316,7 @@ class Stories @JvmOverloads constructor(
timer.resume()
}
@SuppressLint("ClickableViewAccessibility")
private fun loadStory(story: Activity) {
val key = "activities"
val set = PrefManager.getCustomVal<Set<Int>>(key, setOf()).plus((story.id))
@ -374,6 +332,15 @@ class Stories @JvmOverloads constructor(
null
)
}
binding.textActivity.setOnTouchListener { v, event ->
onTouchView(v, event, true)
v.onTouchEvent(event)
}
binding.textActivityContainer.setOnTouchListener { v, event ->
onTouchView(v, event, true)
v.onTouchEvent(event)
}
fun visible(isList: Boolean) {
binding.textActivity.isVisible = !isList
binding.textActivityContainer.isVisible = !isList
@ -397,15 +364,15 @@ class Stories @JvmOverloads constructor(
}
}
} ${story.progress ?: story.media?.title?.userPreferred} " +
if (
story.status?.contains("completed") == false &&
!story.status.contains("plans") &&
!story.status.contains("repeating")
) {
"of ${story.media?.title?.userPreferred}"
} else {
""
}
if (
story.status?.contains("completed") == false &&
!story.status.contains("plans") &&
!story.status.contains("repeating")
) {
"of ${story.media?.title?.userPreferred}"
} else {
""
}
binding.infoText.text = text
val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations)
blurImage(
@ -502,4 +469,66 @@ class Stories @JvmOverloads constructor(
}
}
}
private var startClickTime = 0L
private var startX = 0f
private var startY = 0f
private var isLongPress = false
private val swipeThreshold = 100
override fun onTouch(view: View, event: MotionEvent): Boolean {
onTouchView(view, event)
return true
}
private fun onTouchView(view: View, event: MotionEvent, isText: Boolean = false){
val maxClickDuration = 200
val screenWidth = view.width
val leftHalf = screenWidth / 2
val leftQuarter = screenWidth * 0.15
val rightQuarter = screenWidth * 0.85
when (event.action) {
MotionEvent.ACTION_DOWN -> {
startX = event.x
startY = event.y
startClickTime = Calendar.getInstance().timeInMillis
pause()
isLongPress = false
}
MotionEvent.ACTION_MOVE -> {
val deltaX = event.x - startX
val deltaY = event.y - startY
if (!isLongPress && (abs(deltaX) > swipeThreshold || abs(deltaY) > swipeThreshold)) {
isLongPress = true
}
}
MotionEvent.ACTION_UP -> {
val clickDuration = Calendar.getInstance().timeInMillis - startClickTime
if (isText) {
if (clickDuration < maxClickDuration && !isLongPress) {
if (event.x < leftQuarter) {
leftPanelTouch()
} else if (event.x > rightQuarter) {
rightPanelTouch()
}
} else {
resume()
}
} else {
if (clickDuration < maxClickDuration && !isLongPress) {
if (event.x < leftHalf) {
leftPanelTouch()
} else {
rightPanelTouch()
}
} else {
resume()
}
}
val deltaX = event.x - startX
if (abs(deltaX) > swipeThreshold) {
if (deltaX > 0) onStoriesPrevious()
else onStoriesCompleted()
}
}
}
}
}

View file

@ -30,8 +30,8 @@ import ani.dantotsu.media.user.ListActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.openImage
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.profile.activity.FeedFragment
import ani.dantotsu.profile.activity.ActivityFragment
import ani.dantotsu.profile.activity.ActivityFragment.Companion.ActivityType
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
@ -156,6 +156,7 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
openLinkInBrowser("https://anilist.co/user/${user.name}")
true
}
R.id.action_create_new_activity -> {
ContextCompat.startActivity(
context,
@ -165,6 +166,7 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
)
true
}
else -> false
}
}
@ -177,7 +179,8 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
user.avatar?.medium ?: ""
)
profileUserName.text = user.name
val bannerAnimations: ImageView= if (PrefManager.getVal(PrefName.BannerAnimations)) profileBannerImage else profileBannerImageNoKen
val bannerAnimations: ImageView =
if (PrefManager.getVal(PrefName.BannerAnimations)) profileBannerImage else profileBannerImageNoKen
blurImage(
bannerAnimations,
@ -199,7 +202,8 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
profileAppBar.addOnOffsetChangedListener(context)
profileFollowerCount.text = (respond.data.followerPage?.pageInfo?.total ?: 0).toString()
profileFollowerCount.text =
(respond.data.followerPage?.pageInfo?.total ?: 0).toString()
profileFollowerCountContainer.setOnClickListener {
ContextCompat.startActivity(
context,
@ -209,7 +213,8 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
null
)
}
profileFollowingCount.text = (respond.data.followingPage?.pageInfo?.total ?: 0).toString()
profileFollowingCount.text =
(respond.data.followingPage?.pageInfo?.total ?: 0).toString()
profileFollowingCountContainer.setOnClickListener {
ContextCompat.startActivity(
context,
@ -320,7 +325,7 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment = when (position) {
0 -> ProfileFragment.newInstance(user)
1 -> FeedFragment.newInstance(user.id, false, -1)
1 -> ActivityFragment(ActivityType.OTHER_USER, user.id)
2 -> StatsFragment.newInstance(user)
else -> ProfileFragment.newInstance(user)
}

View file

@ -0,0 +1,159 @@
package ani.dantotsu.profile.activity
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.Activity
import ani.dantotsu.databinding.FragmentFeedBinding
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.setBaseline
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.launch
class ActivityFragment(
var type: ActivityType,
val userId: Int? = null,
var activityId: Int? = null,
) : Fragment() {
private lateinit var binding: FragmentFeedBinding
private var adapter: GroupieAdapter = GroupieAdapter()
private var page: Int = 1
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentFeedBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val navBar = if (userId != null) {
(activity as ProfileActivity).navBar
} else {
(activity as FeedActivity).navBar
}
binding.listRecyclerView.setBaseline(navBar)
binding.listRecyclerView.adapter = adapter
binding.listRecyclerView.layoutManager = LinearLayoutManager(context)
binding.listProgressBar.isVisible = true
binding.feedRefresh.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = navBarHeight
}
binding.emptyTextView.text = getString(R.string.no_notifications)
lifecycleScope.launch {
getList()
if (adapter.itemCount == 0) {
binding.emptyTextView.isVisible = true
}
binding.listProgressBar.isVisible = false
}
binding.feedSwipeRefresh.setOnRefreshListener {
lifecycleScope.launch {
adapter.clear()
page = 1
getList()
binding.feedSwipeRefresh.isRefreshing = false
}
}
binding.listRecyclerView.addOnScrollListener(object :
RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (shouldLoadMore()) {
lifecycleScope.launch {
binding.feedRefresh.isVisible = true
getList()
binding.feedRefresh.isVisible = false
}
}
}
})
}
private suspend fun getList() {
val list = when (type) {
ActivityType.GLOBAL -> getActivities(true)
ActivityType.USER -> getActivities()
ActivityType.OTHER_USER -> getActivities(userId = userId)
ActivityType.ONE -> getActivities(activityId = activityId)
}
adapter.addAll(list.map { ActivityItem(it, ::onActivityClick, requireActivity()) })
}
private suspend fun getActivities(
global: Boolean = false,
userId: Int? = null,
activityId: Int? = null,
): List<Activity> {
val res = Anilist.query.getFeed(userId, global, page, activityId)?.data?.page?.activities
page += 1
return res
?.filter { if (Anilist.adult) true else it.media?.isAdult != true }
?.filterNot { it.recipient?.id != null && it.recipient.id != Anilist.userid }
?: emptyList()
}
private fun shouldLoadMore(): Boolean {
val layoutManager =
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition()
val adapter = binding.listRecyclerView.adapter
return !binding.listRecyclerView.canScrollVertically(1) &&
!binding.feedRefresh.isVisible && adapter?.itemCount != 0 &&
layoutManager == (adapter!!.itemCount - 1)
}
private fun onActivityClick(id: Int, type: String) {
when (type) {
"USER" -> {
ContextCompat.startActivity(
requireContext(), Intent(requireContext(), ProfileActivity::class.java)
.putExtra("userId", id), null
)
}
"MEDIA" -> {
ContextCompat.startActivity(
requireContext(), Intent(requireContext(), MediaDetailsActivity::class.java)
.putExtra("mediaId", id), null
)
}
}
}
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
binding.root.requestLayout()
val navBar = if (userId != null) {
(activity as ProfileActivity).navBar
} else {
(activity as FeedActivity).navBar
}
binding.listRecyclerView.setBaseline(navBar)
}
}
companion object {
enum class ActivityType {
GLOBAL, USER, OTHER_USER, ONE
}
}
}

View file

@ -2,6 +2,7 @@ package ani.dantotsu.profile.activity
import android.content.res.Configuration
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.updateLayoutParams
@ -10,16 +11,20 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.R
import ani.dantotsu.databinding.ActivityFeedBinding
import ani.dantotsu.databinding.ActivityNotificationBinding
import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.profile.activity.ActivityFragment.Companion.ActivityType
import ani.dantotsu.profile.notification.NotificationActivity
import nl.joery.animatedbottombar.AnimatedBottomBar
class FeedActivity : AppCompatActivity() {
private lateinit var binding: ActivityFeedBinding
private lateinit var binding: ActivityNotificationBinding
private var selected: Int = 0
lateinit var navBar: AnimatedBottomBar
@ -27,28 +32,27 @@ class FeedActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
ThemeManager(this).applyTheme()
initActivity(this)
binding = ActivityFeedBinding.inflate(layoutInflater)
binding = ActivityNotificationBinding.inflate(layoutInflater)
setContentView(binding.root)
navBar = binding.feedNavBar
val navBarMargin = if (resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE
) 0 else navBarHeight
navBar.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin = navBarMargin }
val personalTab = navBar.createTab(R.drawable.ic_round_person_24, "Following")
val globalTab = navBar.createTab(R.drawable.ic_globe_24, "Global")
navBar.addTab(personalTab)
navBar.addTab(globalTab)
binding.listTitle.text = getString(R.string.activities)
binding.feedViewPager.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = navBarMargin
topMargin += statusBarHeight
binding.notificationTitle.text = getString(R.string.activities)
binding.notificationToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = statusBarHeight
}
binding.listToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
val activityId = intent.getIntExtra("activityId", -1)
binding.feedViewPager.adapter =
ViewPagerAdapter(supportFragmentManager, lifecycle, activityId)
binding.feedViewPager.setCurrentItem(selected, false)
binding.feedViewPager.isUserInputEnabled = false
navBar = binding.notificationNavBar
binding.root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = navBarHeight
}
val tabs = listOf(
Pair(R.drawable.ic_round_person_24, "Following"),
Pair(R.drawable.ic_globe_24, "Global"),
)
tabs.forEach { (icon, title) -> navBar.addTab(navBar.createTab(icon, title)) }
binding.notificationBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
val getOne = intent.getIntExtra("activityId", -1)
if (getOne != -1) { navBar.visibility = View.GONE }
binding.notificationViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, getOne)
binding.notificationViewPager.setOffscreenPageLimit(4)
binding.notificationViewPager.setCurrentItem(selected, false)
navBar.selectTabAt(selected)
navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener {
override fun onTabSelected(
@ -58,24 +62,15 @@ class FeedActivity : AppCompatActivity() {
newTab: AnimatedBottomBar.Tab
) {
selected = newIndex
binding.feedViewPager.setCurrentItem(selected, true)
binding.notificationViewPager.setCurrentItem(selected, true)
}
})
binding.notificationViewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
navBar.selectTabAt(position)
}
})
binding.listBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
val margin =
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) 0 else navBarHeight
val params: ViewGroup.MarginLayoutParams =
binding.feedViewPager.layoutParams as ViewGroup.MarginLayoutParams
val paramsNav: ViewGroup.MarginLayoutParams =
navBar.layoutParams as ViewGroup.MarginLayoutParams
params.updateMargins(bottom = margin)
paramsNav.updateMargins(bottom = margin)
}
override fun onResume() {
@ -88,12 +83,12 @@ class FeedActivity : AppCompatActivity() {
lifecycle: Lifecycle,
private val activityId: Int
) : FragmentStateAdapter(fragmentManager, lifecycle) {
override fun getItemCount(): Int = 2
override fun getItemCount(): Int = if (activityId != -1) 1 else 2
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> FeedFragment.newInstance(null, false, activityId)
else -> FeedFragment.newInstance(null, true, -1)
0 -> ActivityFragment(if (activityId != -1) ActivityType.ONE else ActivityType.USER, activityId = activityId)
else -> ActivityFragment(ActivityType.GLOBAL)
}
}
}

View file

@ -1,188 +0,0 @@
package ani.dantotsu.profile.activity
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistQueries
import ani.dantotsu.connections.anilist.api.Activity
import ani.dantotsu.databinding.FragmentFeedBinding
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.setBaseline
import ani.dantotsu.util.Logger
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class FeedFragment : Fragment() {
private lateinit var binding: FragmentFeedBinding
private var adapter: GroupieAdapter = GroupieAdapter()
private var activityList: List<Activity> = emptyList()
private lateinit var activity: androidx.activity.ComponentActivity
private var page: Int = 1
private var loadedFirstTime = false
private var userId: Int? = null
private var global: Boolean = false
private var activityId: Int = -1
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentFeedBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity = requireActivity()
userId = arguments?.getInt("userId", -1)
activityId = arguments?.getInt("activityId", -1) ?: -1
if (userId == -1) userId = null
global = arguments?.getBoolean("global", false) ?: false
val navBar = if (userId != null) {
(activity as ProfileActivity).navBar
} else {
(activity as FeedActivity).navBar
}
binding.listRecyclerView.setBaseline(navBar)
binding.listRecyclerView.adapter = adapter
binding.listRecyclerView.layoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
binding.listProgressBar.visibility = ViewGroup.VISIBLE
}
@SuppressLint("ClickableViewAccessibility")
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
binding.root.requestLayout()
val navBar = if (userId != null) {
(activity as ProfileActivity).navBar
} else {
(activity as FeedActivity).navBar
}
binding.listRecyclerView.setBaseline(navBar)
if (!loadedFirstTime) {
activity.lifecycleScope.launch(Dispatchers.IO) {
val nulledId = if (activityId == -1) null else activityId
val res = Anilist.query.getFeed(userId, global, activityId = nulledId)
withContext(Dispatchers.Main) {
res?.data?.page?.activities?.let { activities ->
activityList = activities
val filtered =
activityList
.filter { if (Anilist.adult) true else it.media?.isAdult == false }
.filterNot { //filter out messages that are not directed to the user
it.recipient?.id != null && it.recipient.id != Anilist.userid
}
adapter.update(filtered.map {
ActivityItem(
it,
::onActivityClick,
requireActivity()
)
})
}
binding.listProgressBar.visibility = ViewGroup.GONE
val scrollView = binding.listRecyclerView
binding.listRecyclerView.setOnTouchListener { _, event ->
if (event?.action == MotionEvent.ACTION_UP) {
if (activityList.size % AnilistQueries.ITEMS_PER_PAGE != 0 && !global) {
//snackString("No more activities") fix spam?
Logger.log("No more activities")
} else if (!scrollView.canScrollVertically(1) && !binding.feedRefresh.isVisible
&& binding.listRecyclerView.adapter!!.itemCount != 0 &&
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)
) {
page++
binding.feedRefresh.visibility = ViewGroup.VISIBLE
loadPage {
binding.feedRefresh.visibility = ViewGroup.GONE
}
}
}
false
}
binding.feedSwipeRefresh.setOnRefreshListener {
page = 1
adapter.clear()
activityList = emptyList()
loadPage()
}
}
}
loadedFirstTime = true
}
}
}
private fun loadPage(onFinish: () -> Unit = {}) {
activity.lifecycleScope.launch(Dispatchers.IO) {
val newRes = Anilist.query.getFeed(userId, global, page)
withContext(Dispatchers.Main) {
newRes?.data?.page?.activities?.let { activities ->
activityList += activities
val filtered = activities.filterNot {
it.recipient?.id != null && it.recipient.id != Anilist.userid
}
adapter.addAll(filtered.map {
ActivityItem(
it,
::onActivityClick,
requireActivity()
)
})
}
binding.feedSwipeRefresh.isRefreshing = false
onFinish()
}
}
}
private fun onActivityClick(id: Int, type: String) {
when (type) {
"USER" -> {
ContextCompat.startActivity(
activity, Intent(activity, ProfileActivity::class.java)
.putExtra("userId", id), null
)
}
"MEDIA" -> {
ContextCompat.startActivity(
activity, Intent(activity, MediaDetailsActivity::class.java)
.putExtra("mediaId", id), null
)
}
}
}
companion object {
fun newInstance(userId: Int?, global: Boolean, activityId: Int): FeedFragment {
val fragment = FeedFragment()
val args = Bundle()
args.putInt("userId", userId ?: -1)
args.putBoolean("global", global)
args.putInt("activityId", activityId)
fragment.arguments = args
return fragment
}
}
}

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.isVisible
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.R
import ani.dantotsu.databinding.ActivityNotificationBinding
import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType
import nl.joery.animatedbottombar.AnimatedBottomBar
class NotificationActivity : AppCompatActivity() {
private lateinit var binding: ActivityNotificationBinding
private var selected: Int = 0
lateinit var navBar: AnimatedBottomBar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ThemeManager(this).applyTheme()
initActivity(this)
binding = ActivityNotificationBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.notificationTitle.text = getString(R.string.notifications)
binding.notificationToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = statusBarHeight
}
navBar = binding.notificationNavBar
binding.root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = navBarHeight
}
val tabs = listOf(
Pair(R.drawable.ic_round_movie_filter_24, "Media"),
Pair(R.drawable.ic_round_person_24, "User"),
Pair(R.drawable.ic_round_notifications_active_24, "Subs"),
Pair(R.drawable.ic_round_comment_24, "Comments")
)
tabs.forEach { (icon, title) -> navBar.addTab(navBar.createTab(icon, title)) }
binding.notificationBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
val getOne = intent.getIntExtra("activityId", -1)
if (getOne != -1) navBar.isVisible = false
binding.notificationViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, getOne)
binding.notificationViewPager.setOffscreenPageLimit(4)
binding.notificationViewPager.setCurrentItem(selected, false)
navBar.selectTabAt(selected)
navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener {
override fun onTabSelected(
lastIndex: Int,
lastTab: AnimatedBottomBar.Tab?,
newIndex: Int,
newTab: AnimatedBottomBar.Tab
) {
selected = newIndex
binding.notificationViewPager.setCurrentItem(selected, true)
}
})
binding.notificationViewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
navBar.selectTabAt(position)
}
})
}
override fun onResume() {
super.onResume()
if (this::navBar.isInitialized) {
navBar.selectTabAt(selected)
}
}
private class ViewPagerAdapter(
fragmentManager: FragmentManager,
lifecycle: Lifecycle,
val id: Int = -1
) : FragmentStateAdapter(fragmentManager, lifecycle) {
override fun getItemCount(): Int = if (id != -1) 1 else 4
override fun createFragment(position: Int): Fragment = when (position) {
0 -> NotificationFragment(if (id != -1) NotificationType.ONE else NotificationType.MEDIA, id)
1 -> NotificationFragment(NotificationType.USER)
2 -> NotificationFragment(NotificationType.SUBSCRIPTION)
3 -> NotificationFragment(NotificationType.COMMENT)
else -> NotificationFragment(NotificationType.MEDIA)
}
}
}

View file

@ -0,0 +1,228 @@
package ani.dantotsu.profile.notification
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.webkit.internal.ApiFeature.N
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.Notification
import ani.dantotsu.databinding.FragmentNotificationsBinding
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.notifications.comment.CommentStore
import ani.dantotsu.notifications.subscription.SubscriptionStore
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.profile.activity.FeedActivity
import ani.dantotsu.setBaseline
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.launch
class NotificationFragment(
val type: NotificationType,
val getID: Int = -1
) : Fragment() {
private lateinit var binding: FragmentNotificationsBinding
private var adapter: GroupieAdapter = GroupieAdapter()
private var currentPage = 1
private var hasNextPage = false
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentNotificationsBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val navbar = (activity as NotificationActivity).navBar
binding.notificationRecyclerView.setBaseline(navbar)
binding.notificationRecyclerView.adapter = adapter
binding.notificationRecyclerView.layoutManager = LinearLayoutManager(context)
binding.notificationProgressBar.isVisible = true
binding.notificationRefresh.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = navBarHeight
}
binding.emptyTextView.text = getString(R.string.no_notifications)
lifecycleScope.launch {
getList()
if (adapter.itemCount == 0) {
binding.emptyTextView.isVisible = true
}
binding.notificationProgressBar.isVisible = false
}
binding.notificationSwipeRefresh.setOnRefreshListener {
lifecycleScope.launch {
adapter.clear()
currentPage = 1
getList()
binding.notificationSwipeRefresh.isRefreshing = false
}
}
binding.notificationRecyclerView.addOnScrollListener(object :
RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (shouldLoadMore()) {
lifecycleScope.launch {
binding.notificationRefresh.isVisible = true
getList()
binding.notificationRefresh.isVisible = false
}
}
}
})
}
private suspend fun getList() {
val list = when (type) {
NotificationType.ONE -> getNotificationsFiltered(false) { it.id == getID }
NotificationType.MEDIA -> getNotificationsFiltered { it.media != null }
NotificationType.USER -> getNotificationsFiltered { it.media == null }
NotificationType.SUBSCRIPTION -> getSubscriptions()
NotificationType.COMMENT -> getComments()
}
adapter.addAll(list.map { NotificationItem(it, ::onClick) })
}
private suspend fun getNotificationsFiltered(
reset: Boolean = true,
filter: (Notification) -> Boolean
): List<Notification> {
val userId =
Anilist.userid ?: PrefManager.getVal<String>(PrefName.AnilistUserId).toIntOrNull() ?: 0
val res = Anilist.query.getNotifications(userId, currentPage, reset)?.data?.page
currentPage = res?.pageInfo?.currentPage?.plus(1) ?: 1
hasNextPage = res?.pageInfo?.hasNextPage ?: false
return res?.notifications?.filter(filter) ?: listOf()
}
private fun getSubscriptions(): List<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.notificationRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition()
val adapter = binding.notificationRecyclerView.adapter
return hasNextPage && !binding.notificationRefresh.isVisible && adapter?.itemCount != 0 &&
layoutManager == (adapter!!.itemCount - 1) &&
!binding.notificationRecyclerView.canScrollVertically(1)
}
fun onClick(
id: Int,
optional: Int?,
type: NotificationClickType
) {
when (type) {
NotificationClickType.USER -> {
ContextCompat.startActivity(
requireContext(), Intent(requireContext(), ProfileActivity::class.java)
.putExtra("userId", id), null
)
}
NotificationClickType.MEDIA -> {
ContextCompat.startActivity(
requireContext(), Intent(requireContext(), MediaDetailsActivity::class.java)
.putExtra("mediaId", id), null
)
}
NotificationClickType.ACTIVITY -> {
ContextCompat.startActivity(
requireContext(), Intent(requireContext(), FeedActivity::class.java)
.putExtra("activityId", id), null
)
}
NotificationClickType.COMMENT -> {
ContextCompat.startActivity(
requireContext(), Intent(requireContext(), MediaDetailsActivity::class.java)
.putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
.putExtra("mediaId", id)
.putExtra("commentId", optional ?: -1),
null
)
}
NotificationClickType.UNDEFINED -> {
// Do nothing
}
}
}
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
binding.root.requestLayout()
binding.root.setBaseline((activity as NotificationActivity).navBar)
}
}
companion object {
enum class NotificationClickType {
USER, MEDIA, ACTIVITY, COMMENT, UNDEFINED
}
enum class NotificationType {
MEDIA, USER, SUBSCRIPTION, COMMENT, ONE
}
}
}

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

@ -56,6 +56,7 @@
app:abb_animationInterpolator="@anim/over_shoot"
app:abb_indicatorAppearance="round"
app:abb_indicatorLocation="top"
app:abb_indicatorMargin="28dp"
app:abb_selectedTabType="text"
app:abb_textAppearance="@style/NavBarText"
app:itemActiveIndicatorStyle="@style/BottomNavBar"

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,76 @@
<?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_indicatorMargin="28dp"
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

@ -85,7 +85,7 @@
<com.google.android.material.card.MaterialCardView
android:layout_width="52dp"
android:layout_height="52dp"
app:strokeColor="@color/transparent"
android:backgroundTint="@color/nav_bg_inv"
app:cardCornerRadius="26dp">

View file

@ -4,50 +4,62 @@
android:layout_height="match_parent"
android:orientation="vertical">
<ProgressBar
<LinearLayout
android:id="@+id/listProgressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
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:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/emptyTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fontFamily="@font/poppins_semi_bold"
android:gravity="center"
android:visibility="gone" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/feedSwipeRefresh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_weight="1"
android:clipChildren="false"
android:clipToPadding="false">
<ani.dantotsu.FadingEdgeRecyclerView
android:id="@+id/listRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_height="match_parent"
android:nestedScrollingEnabled="true"
android:requiresFadingEdge="vertical"
android:visibility="visible"
tools:listitem="@layout/item_activity" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<ProgressBar
android:id="@+id/feedRefresh"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="32dp"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/notificationProgressBar"
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:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/emptyTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fontFamily="@font/poppins_semi_bold"
android:gravity="center"
android:visibility="gone" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/notificationSwipeRefresh"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_weight="1"
android:clipChildren="false"
android:clipToPadding="false">
<ani.dantotsu.FadingEdgeRecyclerView
android:id="@+id/notificationRecyclerView"
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/notificationRefresh"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="32dp"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>

View file

@ -28,45 +28,36 @@
android:src="@drawable/linear_gradient_bg"
tools:ignore="ContentDescription" />
<FrameLayout
android:id="@+id/touchPanel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true" />
<androidx.core.widget.NestedScrollView
android:id="@+id/textActivityContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="94dp"
android:clipToPadding="false"
android:scrollbars="none">
<TextView
android:id="@+id/textActivity"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:gravity="center"
android:paddingHorizontal="12dp"
android:text="Play"
android:text="test"
android:textAlignment="center"
android:textColor="@color/bg_white"
android:textSize="18sp"
android:visibility="gone"
tools:ignore="HardcodedText" />
</androidx.core.widget.NestedScrollView>
<FrameLayout
android:id="@+id/leftTouchPanel"
android:layout_width="0dp"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintWidth_percent="0.5" />
<FrameLayout
android:id="@+id/rightTouchPanel"
android:layout_width="0dp"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintWidth_percent="0.5" />
<LinearLayout
android:layout_width="match_parent"

View file

@ -90,14 +90,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:backgroundTint="?attr/colorSecondaryContainer"
android:backgroundTint="?attr/colorOnPrimary"
android:enabled="true"
android:fontFamily="@font/poppins_bold"
android:text="@string/follow"
android:textColor="@color/bg_opp"
android:textSize="14sp"
app:cornerRadius="8dp"
app:strokeColor="?attr/colorSecondaryContainer"
app:strokeColor="?attr/colorOnPrimary"
tools:ignore="SpeakableTextPresentCheck" />
</LinearLayout>