From 3af7926d20c6340d31a16bcbb4b35b0e433becd2 Mon Sep 17 00:00:00 2001 From: rebelonion <87634197+rebelonion@users.noreply.github.com> Date: Fri, 8 Mar 2024 22:04:25 -0600 Subject: [PATCH 1/6] feat: text activity --- .../dantotsu/profile/activity/ActivityItem.kt | 33 ++++++++---- app/src/main/res/layout/item_activity.xml | 50 +++++++++++++++++-- app/src/main/res/values/strings.xml | 10 +++- 3 files changed, 80 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/profile/activity/ActivityItem.kt b/app/src/main/java/ani/dantotsu/profile/activity/ActivityItem.kt index 7860d93e..ee7481c6 100644 --- a/app/src/main/java/ani/dantotsu/profile/activity/ActivityItem.kt +++ b/app/src/main/java/ani/dantotsu/profile/activity/ActivityItem.kt @@ -4,6 +4,7 @@ import android.content.Context import android.view.View import androidx.core.content.ContextCompat import ani.dantotsu.R +import ani.dantotsu.buildMarkwon import ani.dantotsu.connections.anilist.api.Activity import ani.dantotsu.databinding.ItemActivityBinding import ani.dantotsu.loadImage @@ -21,21 +22,27 @@ class ActivityItem( private lateinit var binding: ItemActivityBinding override fun bind(viewBinding: ItemActivityBinding, position: Int) { binding = viewBinding + + binding.activityUserName.text = activity.user?.name + binding.activityUserAvatar.loadImage(activity.user?.avatar?.medium) + binding.activityTime.text = ActivityItemBuilder.getDateTime(activity.createdAt) + val color = if (activity.isLiked == true) + ContextCompat.getColor(binding.root.context, R.color.yt_red) + else + ContextCompat.getColor(binding.root.context, R.color.bg_opp) + binding.activityFavorite.setColorFilter(color) + binding.commentRepliesContainer.visibility = if (activity.replyCount > 0) View.VISIBLE else View.GONE + + val context = binding.root.context + when (activity.typename) { "ListActivity" ->{ - binding.activityUserName.text = activity.user?.name - binding.activityUserAvatar.loadImage(activity.user?.avatar?.medium) - binding.activityTime.text = ActivityItemBuilder.getDateTime(activity.createdAt) - val color = if (activity.isLiked == true) - ContextCompat.getColor(binding.root.context, R.color.yt_red) - else - ContextCompat.getColor(binding.root.context, R.color.bg_opp) - binding.activityFavorite.setColorFilter(color) + binding.activityContent.visibility = View.GONE + binding.activityBannerContainer.visibility = View.VISIBLE binding.activityMediaName.text = activity.media?.title?.userPreferred binding.activityText.text = "${activity.user!!.name} ${activity.status} ${activity.media!!.title!!.userPreferred}" binding.activityCover.loadImage(activity.media.coverImage?.medium) - val context = binding.root.context val banner = activity.media.bannerImage if (banner != null) { if (!(context as android.app.Activity).isDestroyed) @@ -48,6 +55,14 @@ class ActivityItem( binding.activityBannerImage.setImageResource(R.drawable.linear_gradient_bg) } } + "TextActivity" -> { + binding.activityBannerContainer.visibility = View.GONE + binding.activityContent.visibility = View.VISIBLE + if (!(context as android.app.Activity).isDestroyed) { + val markwon = buildMarkwon(context, false) + markwon.setMarkdown(binding.activityContent, activity.text ?: "") + } + } } } diff --git a/app/src/main/res/layout/item_activity.xml b/app/src/main/res/layout/item_activity.xml index eb63b17a..2220d60e 100644 --- a/app/src/main/res/layout/item_activity.xml +++ b/app/src/main/res/layout/item_activity.xml @@ -5,6 +5,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="24dp" + android:background="?attr/colorSurface" android:orientation="vertical"> + + + tools:ignore="ContentDescription" + tools:srcCompat="@tools:sample/backgrounds/scenic" /> + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 89cc8d51..5a16848f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -684,5 +684,13 @@ Manga Mean Score About me Follow - + + +Lorem ipsum dolor sit amet. Est consectetur sint qui internos optio nam Quis excepturi qui voluptatem animi. Qui labore quasi vel suscipit deleniti et doloremque omnis in velit suscipit et quasi eaque. Et doloribus recusandae id laudantium Quis qui natus velit in voluptatem voluptatem! + +Et magni quasi vel nemo omnis et voluptate quisquam vel corporis fuga ut consequatur voluptas At quis veniam. At assumenda illum id minus veritatis id consequatur ipsum est quod corrupti quo adipisci sint. Quo explicabo doloribus et excepturi internos est reiciendis laboriosam ut explicabo veniam ut culpa mollitia in ducimus sint aut autem voluptatem! In consectetur laudantium non omnis saepe nam sint fuga. + +Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exercitationem adipisci quo nemo aliquam ea numquam beatae. Eum Quis dolore aut quia accusantium sed vero autem vel quaerat eaque et beatae dicta non delectus galisum non ullam nulla. + + From 98f4d4f30b71c043c232464decdfd2dbfff96fb0 Mon Sep 17 00:00:00 2001 From: rebelonion <87634197+rebelonion@users.noreply.github.com> Date: Sat, 9 Mar 2024 04:33:06 -0600 Subject: [PATCH 2/6] feat: global/personal feed | like posts | pagination --- app/src/main/AndroidManifest.xml | 2 +- app/src/main/java/ani/dantotsu/Functions.kt | 4 +- .../connections/anilist/AnilistQueries.kt | 21 +++- .../dantotsu/connections/anilist/api/Feed.kt | 32 +++-- .../ani/dantotsu/profile/FollowActivity.kt | 2 + .../ani/dantotsu/profile/ProfileActivity.kt | 26 ++-- .../ani/dantotsu/profile/ProfileFragment.kt | 2 +- .../profile/activity/ActivityActivity.kt | 60 ---------- .../dantotsu/profile/activity/ActivityItem.kt | 57 +++++++-- .../dantotsu/profile/activity/FeedActivity.kt | 81 +++++++++++++ .../dantotsu/profile/activity/FeedFragment.kt | 111 ++++++++++++++++++ .../profile/activity/NotificationActivity.kt | 33 +++++- .../settings/SettingsDialogFragment.kt | 6 + app/src/main/res/drawable/ic_globe_24.xml | 10 ++ ...ctivity_activity.xml => activity_feed.xml} | 50 +++++--- app/src/main/res/layout/activity_follow.xml | 27 ++++- app/src/main/res/layout/activity_profile.xml | 10 +- app/src/main/res/layout/fragment_feed.xml | 44 ++++++- app/src/main/res/layout/item_activity.xml | 4 +- 19 files changed, 438 insertions(+), 144 deletions(-) delete mode 100644 app/src/main/java/ani/dantotsu/profile/activity/ActivityActivity.kt create mode 100644 app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt create mode 100644 app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt create mode 100644 app/src/main/res/drawable/ic_globe_24.xml rename app/src/main/res/layout/{activity_activity.xml => activity_feed.xml} (50%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8590ff4d..12e7eccc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -115,7 +115,7 @@ android:windowSoftInputMode="adjustResize|stateHidden" android:parentActivityName=".MainActivity" /> diff --git a/app/src/main/java/ani/dantotsu/Functions.kt b/app/src/main/java/ani/dantotsu/Functions.kt index 683263a7..67af749c 100644 --- a/app/src/main/java/ani/dantotsu/Functions.kt +++ b/app/src/main/java/ani/dantotsu/Functions.kt @@ -28,6 +28,7 @@ import android.telephony.TelephonyManager import android.text.InputFilter import android.text.Spanned import android.util.AttributeSet +import android.util.Log import android.util.TypedValue import android.view.* import android.view.ViewGroup.LayoutParams.WRAP_CONTENT @@ -129,7 +130,8 @@ var loadIsMAL = false fun logger(e: Any?, print: Boolean = true) { if (print) - println(e) + //println(e) + Log.d("Logger", e.toString()) } diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt index 91244488..e1b52dba 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt @@ -8,10 +8,10 @@ import ani.dantotsu.connections.anilist.Anilist.authorRoles import ani.dantotsu.connections.anilist.Anilist.executeQuery import ani.dantotsu.connections.anilist.api.FeedResponse import ani.dantotsu.connections.anilist.api.FuzzyDate -import ani.dantotsu.connections.anilist.api.Notification import ani.dantotsu.connections.anilist.api.NotificationResponse import ani.dantotsu.connections.anilist.api.Page import ani.dantotsu.connections.anilist.api.Query +import ani.dantotsu.connections.anilist.api.ToggleLike import ani.dantotsu.currContext import ani.dantotsu.isOnline import ani.dantotsu.logError @@ -1265,10 +1265,15 @@ Page(page:$page,perPage:50) { } suspend fun toggleFollow(id: Int): Query.ToggleFollow? { - val response = executeQuery( + return executeQuery( """mutation{ToggleFollow(userId:$id){id, isFollowing, isFollower}}""" ) - return response + } + + suspend fun toggleLike(id: Int, type: String): ToggleLike? { + return executeQuery( + """mutation Like{ToggleLikeV2(id:$id,type:$type){__typename}}""" + ) } suspend fun getUserProfile(id: Int): Query.UserProfileResponse? { @@ -1342,8 +1347,8 @@ Page(page:$page,perPage:50) { return default } - suspend fun getNotifications(id: Int): NotificationResponse? { - val res = executeQuery("""{User(id:$id){unreadNotificationCount}Page{notifications(resetNotificationCount:true){__typename...on AiringNotification{id,type,animeId,episode,contexts,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}},}...on FollowingNotification{id,userId,type,context,createdAt,user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMessageNotification{id,userId,type,activityId,context,createdAt,message{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMentionNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplySubscribedNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentMentionNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentReplyNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentSubscribedNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentLikeNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadLikeNotification{id,userId,type,threadId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on RelatedMediaAdditionNotification{id,type,context,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDataChangeNotification{id,type,mediaId,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaMergeNotification{id,type,mediaId,deletedMediaTitles,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDeletionNotification{id,type,deletedMediaTitle,context,reason,createdAt,}}}}""", force = true) + suspend fun getNotifications(id: Int, page: Int = 1): NotificationResponse? { + val res = executeQuery("""{User(id:$id){unreadNotificationCount}Page(page:$page,perPage:$ITEMS_PER_PAGE){notifications(resetNotificationCount:true){__typename...on AiringNotification{id,type,animeId,episode,contexts,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}},}...on FollowingNotification{id,userId,type,context,createdAt,user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMessageNotification{id,userId,type,activityId,context,createdAt,message{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMentionNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplySubscribedNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentMentionNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentReplyNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentSubscribedNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentLikeNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadLikeNotification{id,userId,type,threadId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on RelatedMediaAdditionNotification{id,type,context,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDataChangeNotification{id,type,mediaId,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaMergeNotification{id,type,mediaId,deletedMediaTitles,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDeletionNotification{id,type,deletedMediaTitle,context,reason,createdAt,}}}}""", force = true) if (res != null) { Anilist.unreadNotificationCount = 0 } @@ -1354,7 +1359,11 @@ Page(page:$page,perPage:50) { val filter = if (userId != null) "userId:$userId," else if (global) "isFollowing:false," else "isFollowing:true," - val res = executeQuery("""{Page(page:$page,perPage:25){activities(${filter}sort:ID_DESC){__typename ... on TextActivity{id userId type replyCount text(asHtml:true)siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on ListActivity{id userId type replyCount status progress siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}media{id title{english romaji native userPreferred}bannerImage coverImage{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on MessageActivity{id recipientId messengerId type replyCount message(asHtml:true)isLocked isSubscribed isLiked isPrivate siteUrl createdAt recipient{id name bannerImage avatar{medium large}}messenger{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}}}}""") + val res = executeQuery("""{Page(page:$page,perPage:$ITEMS_PER_PAGE){activities(${filter}sort:ID_DESC){__typename ... on TextActivity{id userId type replyCount text(asHtml:true)siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on ListActivity{id userId type replyCount status progress siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}media{id title{english romaji native userPreferred}bannerImage coverImage{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on MessageActivity{id recipientId messengerId type replyCount message(asHtml:true)isLocked isSubscribed isLiked isPrivate siteUrl createdAt recipient{id name bannerImage avatar{medium large}}messenger{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}}}}""") return res } + + companion object { + const val ITEMS_PER_PAGE = 25 + } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Feed.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Feed.kt index ca31d6f9..b8cc5cc9 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Feed.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Feed.kt @@ -7,19 +7,19 @@ import kotlinx.serialization.Serializable data class FeedResponse( @SerialName("data") val data: Data -) { +) : java.io.Serializable { @Serializable data class Data( @SerialName("Page") val page: ActivityPage - ) + ) : java.io.Serializable } @Serializable data class ActivityPage( @SerialName("activities") val activities: List -) +) : java.io.Serializable @Serializable data class Activity( @@ -52,9 +52,9 @@ data class Activity( @SerialName("isSubscribed") val isSubscribed: Boolean, @SerialName("likeCount") - val likeCount: Int?, + var likeCount: Int?, @SerialName("isLiked") - val isLiked: Boolean?, + var isLiked: Boolean?, @SerialName("isPinned") val isPinned: Boolean?, @SerialName("isPrivate") @@ -69,7 +69,7 @@ data class Activity( val replies: List?, @SerialName("likes") val likes: List?, -) +) : java.io.Serializable @Serializable data class Reply( @@ -89,4 +89,22 @@ data class Reply( val user: User, @SerialName("likes") val likes: List?, -) \ No newline at end of file +) : java.io.Serializable + +@Serializable +data class ToggleLike( + @SerialName("data") + val data: Data +) : java.io.Serializable { + @Serializable + data class Data( + @SerialName("ToggleLikeV2") + val toggleLike: LikeData + ) : java.io.Serializable +} + +@Serializable +data class LikeData( + @SerialName("__typename") + val typename: String +) : java.io.Serializable \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/profile/FollowActivity.kt b/app/src/main/java/ani/dantotsu/profile/FollowActivity.kt index fa30f6a3..daa984bc 100644 --- a/app/src/main/java/ani/dantotsu/profile/FollowActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/FollowActivity.kt @@ -14,6 +14,7 @@ import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.api.User import ani.dantotsu.databinding.ActivityFollowBinding import ani.dantotsu.initActivity +import ani.dantotsu.navBarHeight import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.statusBarHeight @@ -36,6 +37,7 @@ class FollowActivity : AppCompatActivity(){ initActivity(this) binding = ActivityFollowBinding.inflate(layoutInflater) binding.listToolbar.updateLayoutParams { topMargin = statusBarHeight } + binding.listFrameLayout.updateLayoutParams { bottomMargin = navBarHeight } setContentView(binding.root) val layoutType = PrefManager.getVal(PrefName.FollowerLayout) selected = getSelected(layoutType) diff --git a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt index b180a833..a7f8216d 100644 --- a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt @@ -22,7 +22,7 @@ import ani.dantotsu.initActivity import ani.dantotsu.loadImage import ani.dantotsu.navBarHeight import ani.dantotsu.others.ImageViewDialog -import ani.dantotsu.profile.activity.ActivityActivity +import ani.dantotsu.profile.activity.FeedFragment import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.snackString @@ -36,8 +36,8 @@ import nl.joery.animatedbottombar.AnimatedBottomBar class ProfileActivity : AppCompatActivity() { - private lateinit var binding: ActivityProfileBinding - private var selected: Int = 0 + lateinit var binding: ActivityProfileBinding + private var selected: Int = 1 private lateinit var navBar: AnimatedBottomBar @SuppressLint("SetTextI18n") @@ -49,8 +49,10 @@ class ProfileActivity : AppCompatActivity() { setContentView(binding.root) navBar = binding.profileNavBar navBar.updateLayoutParams { bottomMargin = navBarHeight } + val feedTab = navBar.createTab(R.drawable.ic_round_filter_24, "Feed") val profileTab = navBar.createTab(R.drawable.ic_round_person_24, "Profile") val statsTab = navBar.createTab(R.drawable.ic_stats_24, "Stats") + navBar.addTab(feedTab) navBar.addTab(profileTab) navBar.addTab(statsTab) navBar.visibility = View.GONE @@ -70,6 +72,7 @@ class ProfileActivity : AppCompatActivity() { } binding.profileViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, user) + binding.profileViewPager.setCurrentItem(selected, false) navBar.visibility = View.VISIBLE navBar.selectTabAt(selected) navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener { @@ -106,15 +109,6 @@ class ProfileActivity : AppCompatActivity() { } binding.profileProgressBar.visibility = View.GONE binding.profileTopContainer.visibility = View.VISIBLE - binding.profileActivityButton.setOnClickListener { - ContextCompat.startActivity( - this@ProfileActivity, - Intent(this@ProfileActivity, ActivityActivity::class.java) - .putExtra("userId", user.id) - .putExtra("username", user.name), - null - ) - } binding.profileMenuButton.setOnClickListener { val popup = PopupMenu(this@ProfileActivity, binding.profileMenuButton) popup.menuInflater.inflate(R.menu.menu_profile, popup.menu) @@ -161,7 +155,6 @@ class ProfileActivity : AppCompatActivity() { binding.profileBannerImage.updateLayoutParams { height += statusBarHeight } binding.profileBannerGradient.updateLayoutParams { height += statusBarHeight } binding.profileMenuButton.updateLayoutParams { topMargin += statusBarHeight } - binding.profileActivityButton.updateLayoutParams { topMargin += statusBarHeight } binding.profileBannerImage.setOnLongClickListener { ImageViewDialog.newInstance( this@ProfileActivity, @@ -188,10 +181,11 @@ class ProfileActivity : AppCompatActivity() { ) : FragmentStateAdapter(fragmentManager, lifecycle) { - override fun getItemCount(): Int = 2 + override fun getItemCount(): Int = 3 override fun createFragment(position: Int): Fragment = when (position) { - 0 -> ProfileFragment.newInstance(user) - 1 -> StatsFragment.newInstance(user) + 0 -> FeedFragment.newInstance(user.id, false) + 1 -> ProfileFragment.newInstance(user) + 2 -> StatsFragment.newInstance(user) else -> ProfileFragment.newInstance(user) } } diff --git a/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt b/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt index afb62d82..340c415f 100644 --- a/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt +++ b/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt @@ -32,7 +32,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -class ProfileFragment() : Fragment() { +class ProfileFragment : Fragment() { lateinit var binding: FragmentProfileBinding private lateinit var activity: ProfileActivity private lateinit var user: Query.UserProfile diff --git a/app/src/main/java/ani/dantotsu/profile/activity/ActivityActivity.kt b/app/src/main/java/ani/dantotsu/profile/activity/ActivityActivity.kt deleted file mode 100644 index a9482820..00000000 --- a/app/src/main/java/ani/dantotsu/profile/activity/ActivityActivity.kt +++ /dev/null @@ -1,60 +0,0 @@ -package ani.dantotsu.profile.activity - -import android.annotation.SuppressLint -import android.os.Bundle -import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.updateLayoutParams -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.LinearLayoutManager -import ani.dantotsu.connections.anilist.Anilist -import ani.dantotsu.connections.anilist.api.Activity -import ani.dantotsu.databinding.ActivityFollowBinding -import ani.dantotsu.initActivity -import ani.dantotsu.statusBarHeight -import ani.dantotsu.themes.ThemeManager -import com.xwray.groupie.GroupieAdapter -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -class ActivityActivity : AppCompatActivity() { - private lateinit var binding: ActivityFollowBinding - private var adapter: GroupieAdapter = GroupieAdapter() - private var activityList: List = emptyList() - - @SuppressLint("SetTextI18n") - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - ThemeManager(this).applyTheme() - initActivity(this) - binding = ActivityFollowBinding.inflate(layoutInflater) - setContentView(binding.root) - - binding.listTitle.text = "Activity" - binding.listToolbar.updateLayoutParams { topMargin = statusBarHeight } - 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 { - onBackPressed() - } - binding.listProgressBar.visibility = ViewGroup.VISIBLE - var userId: Int? = intent.getIntExtra("userId", -1) - if (userId == -1) userId = null - val global = intent.getBooleanExtra("global", false) - - lifecycleScope.launch(Dispatchers.IO) { - val res = Anilist.query.getFeed(userId, global) - - withContext(Dispatchers.Main){ - res?.data?.page?.activities?.let { activities -> - activityList = activities - adapter.update(activityList.map { ActivityItem(it){} }) - } - binding.listProgressBar.visibility = ViewGroup.GONE - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/profile/activity/ActivityItem.kt b/app/src/main/java/ani/dantotsu/profile/activity/ActivityItem.kt index 5744c6c6..c69d5950 100644 --- a/app/src/main/java/ani/dantotsu/profile/activity/ActivityItem.kt +++ b/app/src/main/java/ani/dantotsu/profile/activity/ActivityItem.kt @@ -6,21 +6,29 @@ import android.view.View import androidx.core.content.ContextCompat import ani.dantotsu.R import ani.dantotsu.buildMarkwon +import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.api.Activity import ani.dantotsu.databinding.ItemActivityBinding import ani.dantotsu.loadImage +import ani.dantotsu.snackString import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.request.RequestOptions import com.xwray.groupie.viewbinding.BindableItem import jp.wasabeef.glide.transformations.BlurTransformation +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class ActivityItem( private val activity: Activity, - val clickCallback: (Int) -> Unit -): BindableItem() { + val clickCallback: (Int, type: String) -> Unit +) : BindableItem() { private lateinit var binding: ItemActivityBinding + @SuppressLint("SetTextI18n") override fun bind(viewBinding: ItemActivityBinding, position: Int) { binding = viewBinding @@ -28,24 +36,48 @@ class ActivityItem( binding.activityUserName.text = activity.user?.name binding.activityUserAvatar.loadImage(activity.user?.avatar?.medium) binding.activityTime.text = ActivityItemBuilder.getDateTime(activity.createdAt) - val color = if (activity.isLiked == true) - ContextCompat.getColor(binding.root.context, R.color.yt_red) - else - ContextCompat.getColor(binding.root.context, R.color.bg_opp) - binding.activityFavorite.setColorFilter(color) - binding.commentRepliesContainer.visibility = if (activity.replyCount > 0) View.VISIBLE else View.GONE + val likeColor = ContextCompat.getColor(binding.root.context, R.color.yt_red) + val notLikeColor = ContextCompat.getColor(binding.root.context, R.color.bg_opp) + binding.activityLike.setColorFilter(if (activity.isLiked == true) likeColor else notLikeColor) + binding.commentRepliesContainer.visibility = + if (activity.replyCount > 0) View.VISIBLE else View.GONE + binding.activityLikeCount.text = activity.likeCount.toString() + + binding.activityLike.setOnClickListener { + val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) + scope.launch { + val res = Anilist.query.toggleLike(activity.id, "ACTIVITY") + withContext(Dispatchers.Main) { + if (res != null) { + + if (activity.isLiked == true) { + activity.likeCount = activity.likeCount?.minus(1) + } else { + activity.likeCount = activity.likeCount?.plus(1) + } + binding.activityLikeCount.text = activity.likeCount.toString() + activity.isLiked = !activity.isLiked!! + binding.activityLike.setColorFilter(if (activity.isLiked == true) likeColor else notLikeColor) + + } else { + snackString("Failed to like activity") + } + } + } + } val context = binding.root.context when (activity.typename) { - "ListActivity" ->{ + "ListActivity" -> { binding.activityContent.visibility = View.GONE binding.activityBannerContainer.visibility = View.VISIBLE binding.activityMediaName.text = activity.media?.title?.userPreferred - binding.activityText.text = "${activity.user!!.name} ${activity.status} ${activity.media!!.title!!.userPreferred}" - binding.activityCover.loadImage(activity.media.coverImage?.medium) - val banner = activity.media.bannerImage + binding.activityText.text = + """${activity.user!!.name} ${activity.status} ${activity.progress ?: ""}""" + binding.activityCover.loadImage(activity.media?.coverImage?.medium) + val banner = activity.media?.bannerImage if (banner != null) { if (!(context as android.app.Activity).isDestroyed) { Glide.with(context as Context) @@ -58,6 +90,7 @@ class ActivityItem( binding.activityBannerImage.setImageResource(R.drawable.linear_gradient_bg) } } + "TextActivity" -> { binding.activityBannerContainer.visibility = View.GONE binding.activityContent.visibility = View.VISIBLE diff --git a/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt b/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt new file mode 100644 index 00000000..8513ceb2 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt @@ -0,0 +1,81 @@ +package ani.dantotsu.profile.activity + +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 ani.dantotsu.R +import ani.dantotsu.databinding.ActivityFeedBinding +import ani.dantotsu.initActivity +import ani.dantotsu.navBarHeight +import ani.dantotsu.statusBarHeight +import ani.dantotsu.themes.ThemeManager +import nl.joery.animatedbottombar.AnimatedBottomBar + +class FeedActivity: AppCompatActivity() { + private lateinit var binding: ActivityFeedBinding + private var selected: Int = 0 + private lateinit var navBar: AnimatedBottomBar + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + ThemeManager(this).applyTheme() + initActivity(this) + binding = ActivityFeedBinding.inflate(layoutInflater) + setContentView(binding.root) + navBar = binding.feedNavBar + navBar.updateLayoutParams { bottomMargin += navBarHeight } + val personalTab = navBar.createTab(R.drawable.ic_round_person_24, "Personal") + val globalTab = navBar.createTab(R.drawable.ic_globe_24, "Global") + navBar.addTab(personalTab) + navBar.addTab(globalTab) + binding.listTitle.text = "Activities" + binding.feedViewPager.updateLayoutParams { + bottomMargin += navBarHeight + topMargin += statusBarHeight + } + binding.listToolbar.updateLayoutParams { topMargin += statusBarHeight } + binding.feedViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle) + binding.feedViewPager.setCurrentItem(selected, false) + binding.feedViewPager.isUserInputEnabled = 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.feedViewPager.setCurrentItem(selected, true) + } + }) + binding.listBack.setOnClickListener { + onBackPressed() + } + } + + override fun onResume() { + super.onResume() + navBar.selectTabAt(selected) + } + + + private class ViewPagerAdapter( + fragmentManager: FragmentManager, + lifecycle: Lifecycle + ) : FragmentStateAdapter(fragmentManager, lifecycle) { + override fun getItemCount(): Int = 2 + + override fun createFragment(position: Int): Fragment { + return when (position) { + 0 -> FeedFragment.newInstance(null, false) + else -> FeedFragment.newInstance(null, true) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt b/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt new file mode 100644 index 00000000..f6751d53 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt @@ -0,0 +1,111 @@ +package ani.dantotsu.profile.activity + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +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.logger +import ani.dantotsu.profile.ProfileActivity +import ani.dantotsu.snackString +import com.xwray.groupie.GroupieAdapter +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class FeedFragment : Fragment() { + private lateinit var binding: FragmentFeedBinding + private var adapter: GroupieAdapter = GroupieAdapter() + private var activityList: List = emptyList() + private lateinit var activity: androidx.activity.ComponentActivity + private var page: Int = 1 + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentFeedBinding.inflate(inflater, container, false) + return binding.root + } + + @SuppressLint("ClickableViewAccessibility") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + activity = requireActivity() + binding.listRecyclerView.adapter = adapter + binding.listRecyclerView.layoutManager = + LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) + binding.listProgressBar.visibility = ViewGroup.VISIBLE + var userId: Int? = arguments?.getInt("userId", -1) + if (userId == -1) userId = null + val global = arguments?.getBoolean("global", false) ?: false + + activity.lifecycleScope.launch(Dispatchers.IO) { + val res = Anilist.query.getFeed(userId, global) + withContext(Dispatchers.Main) { + res?.data?.page?.activities?.let { activities -> + activityList = activities + adapter.update(activityList.map { ActivityItem(it) { _, _ -> } }) + } + binding.listProgressBar.visibility = ViewGroup.GONE + val scrollView = if (activity is ProfileActivity) { + (activity as ProfileActivity).binding.profileScrollView + } else { + binding.listRecyclerView + } + binding.listRecyclerView.setOnTouchListener { _, event -> + if (event?.action == MotionEvent.ACTION_UP) { + if (adapter.itemCount % AnilistQueries.ITEMS_PER_PAGE != 0) { + snackString("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 + activity.lifecycleScope.launch(Dispatchers.IO) { + val res = Anilist.query.getFeed(userId, global, page) + withContext(Dispatchers.Main) { + res?.data?.page?.activities?.let { activities -> + activityList += activities + adapter.addAll(activities.map { ActivityItem(it) { _, _ -> } }) + } + binding.feedRefresh.visibility = ViewGroup.GONE + } + } + } + } + false + } + } + } + } + + override fun onResume() { + super.onResume() + if (this::binding.isInitialized) { + binding.root.requestLayout() + } + } + + companion object { + fun newInstance(userId: Int?, global: Boolean): FeedFragment { + val fragment = FeedFragment() + val args = Bundle() + args.putInt("userId", userId ?: -1) + args.putBoolean("global", global) + fragment.arguments = args + return fragment + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt b/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt index f699b274..881c9975 100644 --- a/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt @@ -3,18 +3,23 @@ package ani.dantotsu.profile.activity import android.annotation.SuppressLint import android.content.Intent import android.os.Bundle +import android.view.MotionEvent import android.view.ViewGroup 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.connections.anilist.Anilist +import ani.dantotsu.connections.anilist.AnilistQueries import ani.dantotsu.connections.anilist.api.Notification import ani.dantotsu.databinding.ActivityFollowBinding import ani.dantotsu.initActivity import ani.dantotsu.media.MediaDetailsActivity +import ani.dantotsu.navBarHeight import ani.dantotsu.profile.ProfileActivity +import ani.dantotsu.snackString import ani.dantotsu.statusBarHeight import ani.dantotsu.themes.ThemeManager import com.xwray.groupie.GroupieAdapter @@ -26,8 +31,9 @@ class NotificationActivity : AppCompatActivity() { private lateinit var binding: ActivityFollowBinding private var adapter: GroupieAdapter = GroupieAdapter() private var notificationList: List = emptyList() + private var page: Int = 1 - @SuppressLint("SetTextI18n") + @SuppressLint("SetTextI18n", "ClickableViewAccessibility") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ThemeManager(this).applyTheme() @@ -36,6 +42,7 @@ class NotificationActivity : AppCompatActivity() { setContentView(binding.root) binding.listTitle.text = "Notifications" binding.listToolbar.updateLayoutParams { topMargin = statusBarHeight } + binding.listFrameLayout.updateLayoutParams { bottomMargin = navBarHeight } binding.listRecyclerView.adapter = adapter binding.listRecyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) binding.followerGrid.visibility = ViewGroup.GONE @@ -52,6 +59,30 @@ class NotificationActivity : AppCompatActivity() { } withContext(Dispatchers.Main){ binding.listProgressBar.visibility = ViewGroup.GONE + binding.listRecyclerView.setOnTouchListener { _, event -> + if (event?.action == MotionEvent.ACTION_UP) { + if (adapter.itemCount % AnilistQueries.ITEMS_PER_PAGE != 0) { + snackString("No more notifications") + } else if (!binding.listRecyclerView.canScrollVertically(1) && !binding.followRefresh.isVisible + && binding.listRecyclerView.adapter!!.itemCount != 0 && + (binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1) + ) { + page++ + binding.followRefresh.visibility = ViewGroup.VISIBLE + lifecycleScope.launch(Dispatchers.IO) { + val res = Anilist.query.getNotifications(Anilist.userid?:0, page) + withContext(Dispatchers.Main) { + res?.data?.page?.notifications?.let { notifications -> + notificationList += notifications + adapter.addAll(notifications.map { NotificationItem(it, ::onNotificationClick) }) + } + binding.followRefresh.visibility = ViewGroup.GONE + } + } + } + } + false + } } } } diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt b/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt index 6638b151..b2aa43e6 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt @@ -25,6 +25,7 @@ import ani.dantotsu.incognitoNotification import ani.dantotsu.loadImage import ani.dantotsu.profile.activity.NotificationActivity import ani.dantotsu.offline.OfflineFragment +import ani.dantotsu.profile.activity.FeedActivity import ani.dantotsu.setSafeOnClickListener import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName @@ -108,6 +109,11 @@ class SettingsDialogFragment : BottomSheetDialogFragment() { dismiss() } + binding.settingsActivity.setSafeOnClickListener { + startActivity(Intent(activity, FeedActivity::class.java)) + dismiss() + } + binding.settingsNotification.setOnClickListener { startActivity(Intent(activity, NotificationActivity::class.java)) dismiss() diff --git a/app/src/main/res/drawable/ic_globe_24.xml b/app/src/main/res/drawable/ic_globe_24.xml new file mode 100644 index 00000000..ff6e71b0 --- /dev/null +++ b/app/src/main/res/drawable/ic_globe_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/activity_activity.xml b/app/src/main/res/layout/activity_feed.xml similarity index 50% rename from app/src/main/res/layout/activity_activity.xml rename to app/src/main/res/layout/activity_feed.xml index 584c5588..295a8a10 100644 --- a/app/src/main/res/layout/activity_activity.xml +++ b/app/src/main/res/layout/activity_feed.xml @@ -1,19 +1,13 @@ - + - - @@ -38,18 +32,38 @@ android:fontFamily="@font/poppins_bold" android:gravity="center|start" android:singleLine="true" - android:text="Activity" android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title" android:textColor="?attr/colorOnBackground" android:textSize="18sp" - tools:ignore="HardcodedText" /> + tools:text="Activities" /> + - - \ No newline at end of file + android:layout_height="match_parent" + android:layout_marginBottom="72dp" + android:layout_marginTop="48dp" + tools:ignore="SpeakableTextPresentCheck" /> + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_follow.xml b/app/src/main/res/layout/activity_follow.xml index 59358670..9dfe9846 100644 --- a/app/src/main/res/layout/activity_follow.xml +++ b/app/src/main/res/layout/activity_follow.xml @@ -93,11 +93,26 @@ - + android:layout_height="wrap_content"> + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_profile.xml b/app/src/main/res/layout/activity_profile.xml index 1583039d..409e6860 100644 --- a/app/src/main/res/layout/activity_profile.xml +++ b/app/src/main/res/layout/activity_profile.xml @@ -20,6 +20,7 @@ - - - + android:layout_height="match_parent" + android:orientation="vertical"> - \ No newline at end of file + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_activity.xml b/app/src/main/res/layout/item_activity.xml index 361df9d2..a21d7e05 100644 --- a/app/src/main/res/layout/item_activity.xml +++ b/app/src/main/res/layout/item_activity.xml @@ -70,14 +70,14 @@ android:orientation="vertical"> Date: Sat, 9 Mar 2024 15:58:15 -0500 Subject: [PATCH 3/6] fix: swap chapter names and nav on RTL (#230) * fix: swap chapter names and nav on RTL * fix: swipe RTL no longer needs invert --- .../manga/mangareader/MangaReaderActivity.kt | 59 ++++++++++++------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt index ef8725e1..a6ce9bd3 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt @@ -285,16 +285,26 @@ class MangaReaderActivity : AppCompatActivity() { binding.mangaReaderNextChapter.performClick() } binding.mangaReaderNextChapter.setOnClickListener { - if (chaptersArr.size > currentChapterIndex + 1) progress { change(currentChapterIndex + 1) } - else snackString(getString(R.string.next_chapter_not_found)) + if (defaultSettings.direction == RIGHT_TO_LEFT) { + if (currentChapterIndex > 0) change(currentChapterIndex - 1) + else snackString(getString(R.string.first_chapter)) + } else { + if (chaptersArr.size > currentChapterIndex + 1) progress { change(currentChapterIndex + 1) } + else snackString(getString(R.string.next_chapter_not_found)) + } } //Prev Chapter binding.mangaReaderPrevChap.setOnClickListener { binding.mangaReaderPreviousChapter.performClick() } binding.mangaReaderPreviousChapter.setOnClickListener { - if (currentChapterIndex > 0) change(currentChapterIndex - 1) - else snackString(getString(R.string.first_chapter)) + if (defaultSettings.direction == RIGHT_TO_LEFT) { + if (chaptersArr.size > currentChapterIndex + 1) progress { change(currentChapterIndex + 1) } + else snackString(getString(R.string.next_chapter_not_found)) + } else { + if (currentChapterIndex > 0) change(currentChapterIndex - 1) + else snackString(getString(R.string.first_chapter)) + } } model.getMangaChapter().observe(this) { chap -> @@ -305,10 +315,17 @@ class MangaReaderActivity : AppCompatActivity() { PrefManager.setCustomVal("${media.id}_current_chp", chap.number) currentChapterIndex = chaptersArr.indexOf(chap.number) binding.mangaReaderChapterSelect.setSelection(currentChapterIndex) - binding.mangaReaderNextChap.text = - chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: "" - binding.mangaReaderPrevChap.text = - chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: "" + if (defaultSettings.direction == RIGHT_TO_LEFT) { + binding.mangaReaderNextChap.text = + chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: "" + binding.mangaReaderPrevChap.text = + chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: "" + } else { + binding.mangaReaderNextChap.text = + chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: "" + binding.mangaReaderPrevChap.text = + chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: "" + } applySettings() val context = this val offline: Boolean = PrefManager.getVal(PrefName.OfflineMode) @@ -459,27 +476,26 @@ class MangaReaderActivity : AppCompatActivity() { } else { binding.mangaReaderSwipy.vertical = false if (defaultSettings.direction == RIGHT_TO_LEFT) { + binding.mangaReaderNextChap.text = + chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: "" + binding.mangaReaderPrevChap.text = + chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: "" binding.LeftSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: getString(R.string.no_chapter) binding.RightSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: getString(R.string.no_chapter) - binding.mangaReaderSwipy.onLeftSwiped = { - binding.mangaReaderNextChapter.performClick() - } - binding.mangaReaderSwipy.onRightSwiped = { - binding.mangaReaderPreviousChapter.performClick() - } } else { + binding.mangaReaderNextChap.text = + chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: "" + binding.mangaReaderPrevChap.text = + chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: "" binding.LeftSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: getString(R.string.no_chapter) binding.RightSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: getString(R.string.no_chapter) - binding.mangaReaderSwipy.onLeftSwiped = { - binding.mangaReaderPreviousChapter.performClick() - } - binding.mangaReaderSwipy.onRightSwiped = { - binding.mangaReaderNextChapter.performClick() - } + } + binding.mangaReaderSwipy.onLeftSwiped = { + binding.mangaReaderPreviousChapter.performClick() } binding.mangaReaderSwipy.leftBeingSwiped = { value -> binding.LeftSwipeContainer.apply { @@ -487,6 +503,9 @@ class MangaReaderActivity : AppCompatActivity() { translationX = -width.dp * (1 - min(value, 1f)) } } + binding.mangaReaderSwipy.onRightSwiped = { + binding.mangaReaderNextChapter.performClick() + } binding.mangaReaderSwipy.rightBeingSwiped = { value -> binding.RightSwipeContainer.apply { alpha = value From 7504bb9081356e75d18cab064d510dded33bbce5 Mon Sep 17 00:00:00 2001 From: TwistedUmbrellaX <1173913+AbandonedCart@users.noreply.github.com> Date: Sat, 9 Mar 2024 16:00:00 -0500 Subject: [PATCH 4/6] fix: optimize querying download uri (#232) --- .../java/ani/dantotsu/others/AppUpdater.kt | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/app/src/google/java/ani/dantotsu/others/AppUpdater.kt b/app/src/google/java/ani/dantotsu/others/AppUpdater.kt index 153cf970..595aaab4 100644 --- a/app/src/google/java/ani/dantotsu/others/AppUpdater.kt +++ b/app/src/google/java/ani/dantotsu/others/AppUpdater.kt @@ -161,21 +161,8 @@ object AppUpdater { DownloadManager.EXTRA_DOWNLOAD_ID, id ) ?: id - val query = DownloadManager.Query() - query.setFilterById(downloadId) - val c = downloadManager.query(query) - - if (c.moveToFirst()) { - val columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS) - if (DownloadManager.STATUS_SUCCESSFUL == c - .getInt(columnIndex) - ) { - c.getColumnIndex(DownloadManager.COLUMN_MEDIAPROVIDER_URI) - val uri = Uri.parse( - c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)) - ) - openApk(this@downloadUpdate, uri) - } + downloadManager.getUriForDownloadedFile(downloadId)?.let { + openApk(this@downloadUpdate, it) } } catch (e: Exception) { logError(e) @@ -190,16 +177,11 @@ object AppUpdater { private fun openApk(context: Context, uri: Uri) { try { uri.path?.let { - val contentUri = FileProvider.getUriForFile( - context, - BuildConfig.APPLICATION_ID + ".provider", - File(it) - ) val installIntent = Intent(Intent.ACTION_VIEW).apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true) - data = contentUri + data = uri } context.startActivity(installIntent) } From 7f36eba7093fe67f2b13779943fd8d6d84568ca7 Mon Sep 17 00:00:00 2001 From: ibo <41344259+sneazy-ibo@users.noreply.github.com> Date: Sat, 9 Mar 2024 22:02:23 +0100 Subject: [PATCH 5/6] feat: longclicklistener for AL profile now accessible everywhere (#228) * feat: added longclicklistener for AL profile in AnimePageAdapter and MangaPageAdapter * feat: add delete to smaller media bottom sheet --- .../ani/dantotsu/home/AnimePageAdapter.kt | 10 ++++ .../java/ani/dantotsu/home/HomeFragment.kt | 2 +- .../ani/dantotsu/home/MangaPageAdapter.kt | 9 ++++ .../media/MediaListDialogSmallFragment.kt | 26 ++++++++++ .../layout/bottom_sheet_media_list_small.xml | 47 ++++++++++++++----- 5 files changed, 80 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt index fc480ae3..7665ace2 100644 --- a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt +++ b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt @@ -26,6 +26,7 @@ import ani.dantotsu.media.CalendarActivity import ani.dantotsu.media.GenreActivity import ani.dantotsu.media.MediaAdaptor import ani.dantotsu.media.SearchActivity +import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.px import ani.dantotsu.setSafeOnClickListener import ani.dantotsu.setSlideIn @@ -94,6 +95,15 @@ class AnimePageAdapter : RecyclerView.Adapter + ContextCompat.startActivity( + view.context, + Intent(view.context, ProfileActivity::class.java) + .putExtra("userId", Anilist.userid),null + ) + false + } + binding.animeNotificationCount.visibility = if (Anilist.unreadNotificationCount > 0) View.VISIBLE else View.GONE binding.animeNotificationCount.text = Anilist.unreadNotificationCount.toString() diff --git a/app/src/main/java/ani/dantotsu/home/HomeFragment.kt b/app/src/main/java/ani/dantotsu/home/HomeFragment.kt index 9652c43b..e0839e90 100644 --- a/app/src/main/java/ani/dantotsu/home/HomeFragment.kt +++ b/app/src/main/java/ani/dantotsu/home/HomeFragment.kt @@ -124,7 +124,7 @@ class HomeFragment : Fragment() { binding.homeUserAvatarContainer.setOnLongClickListener { ContextCompat.startActivity( requireContext(), Intent(requireContext(), ProfileActivity::class.java) - .putExtra("userId", Anilist.userid), null + .putExtra("userId", Anilist.userid),null ) false } diff --git a/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt b/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt index 1f714260..ca0a6e59 100644 --- a/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt +++ b/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt @@ -25,6 +25,7 @@ import ani.dantotsu.loadImage import ani.dantotsu.media.GenreActivity import ani.dantotsu.media.MediaAdaptor import ani.dantotsu.media.SearchActivity +import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.px import ani.dantotsu.setSafeOnClickListener import ani.dantotsu.setSlideIn @@ -90,6 +91,14 @@ class MangaPageAdapter : RecyclerView.Adapter + ContextCompat.startActivity( + view.context, + Intent(view.context, ProfileActivity::class.java) + .putExtra("userId", Anilist.userid),null + ) + false + } binding.mangaSearchBar.setEndIconOnClickListener { binding.mangaSearchBarText.performClick() diff --git a/app/src/main/java/ani/dantotsu/media/MediaListDialogSmallFragment.kt b/app/src/main/java/ani/dantotsu/media/MediaListDialogSmallFragment.kt index cb7390ce..185e4916 100644 --- a/app/src/main/java/ani/dantotsu/media/MediaListDialogSmallFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/MediaListDialogSmallFragment.kt @@ -58,6 +58,32 @@ class MediaListDialogSmallFragment : BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { binding.mediaListContainer.updateLayoutParams { bottomMargin += navBarHeight } val scope = viewLifecycleOwner.lifecycleScope + binding.mediaListDelete.setOnClickListener { + val id = media.userListId + if (id != null) { + viewLifecycleOwner.lifecycleScope.launch { + withContext(Dispatchers.IO) { + try { + Anilist.mutation.deleteList(id) + MAL.query.deleteList(media.anime != null, media.idMAL) + } catch (e: Exception) { + withContext(Dispatchers.Main) { + snackString("Failed to delete because of... ${e.message}") + } + return@withContext + } + } + withContext(Dispatchers.Main) { + Refresh.all() + snackString(getString(R.string.deleted_from_list)) + dismissAllowingStateLoss() + } + } + } else { + snackString(getString(R.string.no_list_id)) + Refresh.all() + } + } binding.mediaListProgressBar.visibility = View.GONE binding.mediaListLayout.visibility = View.VISIBLE diff --git a/app/src/main/res/layout/bottom_sheet_media_list_small.xml b/app/src/main/res/layout/bottom_sheet_media_list_small.xml index ecab76ba..1cb457c2 100644 --- a/app/src/main/res/layout/bottom_sheet_media_list_small.xml +++ b/app/src/main/res/layout/bottom_sheet_media_list_small.xml @@ -178,22 +178,43 @@ android:fontFamily="@font/poppins_bold" android:text="@string/list_private" /> -