From 4d2a08c2581bdbf918ccdfe2cb535bb740a47300 Mon Sep 17 00:00:00 2001 From: rebelonion <87634197+rebelonion@users.noreply.github.com> Date: Thu, 14 Mar 2024 06:00:48 -0500 Subject: [PATCH] feat: anilist notifications (real) --- app/src/main/java/ani/dantotsu/App.kt | 56 ++++- .../connections/anilist/AnilistQueries.kt | 8 +- .../dantotsu/connections/anilist/api/Data.kt | 2 +- .../dantotsu/media/comments/CommentItem.kt | 8 +- .../media/comments/CommentsFragment.kt | 8 +- ...Worker.kt => CommentNotificationWorker.kt} | 8 +- .../anilist/AnilistNotificationWorker.kt | 98 +++++++++ .../ani/dantotsu/settings/SettingsActivity.kt | 73 ++++++- .../dantotsu/settings/saving/Preferences.kt | 5 +- .../data/notification/Notifications.kt | 16 +- app/src/main/res/layout/activity_settings.xml | 200 ++++++++++++------ app/src/main/res/values/strings.xml | 3 + 12 files changed, 390 insertions(+), 95 deletions(-) rename app/src/main/java/ani/dantotsu/notifications/{NotificationWorker.kt => CommentNotificationWorker.kt} (96%) create mode 100644 app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationWorker.kt diff --git a/app/src/main/java/ani/dantotsu/App.kt b/app/src/main/java/ani/dantotsu/App.kt index 51336438..bb1a7cb8 100644 --- a/app/src/main/java/ani/dantotsu/App.kt +++ b/app/src/main/java/ani/dantotsu/App.kt @@ -7,12 +7,14 @@ import android.os.Bundle import androidx.multidex.MultiDex import androidx.multidex.MultiDexApplication import androidx.work.Constraints +import androidx.work.OneTimeWorkRequest import androidx.work.PeriodicWorkRequest import ani.dantotsu.aniyomi.anime.custom.AppModule import ani.dantotsu.aniyomi.anime.custom.PreferenceModule import ani.dantotsu.connections.comments.CommentsAPI import ani.dantotsu.connections.crashlytics.CrashlyticsInterface -import ani.dantotsu.notifications.NotificationWorker +import ani.dantotsu.notifications.CommentNotificationWorker +import ani.dantotsu.notifications.anilist.AnilistNotificationWorker import ani.dantotsu.others.DisabledReports import ani.dantotsu.parsers.AnimeSources import ani.dantotsu.parsers.MangaSources @@ -34,7 +36,6 @@ import kotlinx.coroutines.launch import logcat.AndroidLogcatLogger import logcat.LogPriority import logcat.LogcatLogger -import tachiyomi.core.util.system.logcat import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -122,18 +123,51 @@ class App : MultiDexApplication() { CommentsAPI.fetchAuthToken() } + startWorkers() + } + + private fun startWorkers() { val constraints = Constraints.Builder() .setRequiredNetworkType(androidx.work.NetworkType.CONNECTED) .build() - val recurringWork = PeriodicWorkRequest.Builder(NotificationWorker::class.java, - 15, java.util.concurrent.TimeUnit.MINUTES) - .setConstraints(constraints) - .build() - androidx.work.WorkManager.getInstance(this).enqueueUniquePeriodicWork( - NotificationWorker.WORK_NAME, - androidx.work.ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, - recurringWork - ) + + // CommentNotificationWorker + val commentInterval = CommentNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.CommentNotificationInterval)] + if (commentInterval.toInt() != 0) { + val recurringWork = PeriodicWorkRequest.Builder(CommentNotificationWorker::class.java, + commentInterval, java.util.concurrent.TimeUnit.MINUTES) + .setConstraints(constraints) + .build() + androidx.work.WorkManager.getInstance(this).enqueueUniquePeriodicWork( + CommentNotificationWorker.WORK_NAME, + androidx.work.ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, + recurringWork + ) + } else { + androidx.work.WorkManager.getInstance(this).cancelUniqueWork(CommentNotificationWorker.WORK_NAME) + //run once + androidx.work.WorkManager.getInstance(this).enqueue(OneTimeWorkRequest.Companion.from(CommentNotificationWorker::class.java)) + } + + // AnilistNotificationWorker + val anilistInterval = AnilistNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.AnilistNotificationInterval)] + if (anilistInterval.toInt() != 0) { + val recurringWork = PeriodicWorkRequest.Builder( + AnilistNotificationWorker::class.java, + anilistInterval, java.util.concurrent.TimeUnit.MINUTES) + .setConstraints(constraints) + .build() + androidx.work.WorkManager.getInstance(this).enqueueUniquePeriodicWork( + AnilistNotificationWorker.WORK_NAME, + androidx.work.ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, + recurringWork + ) + } else { + androidx.work.WorkManager.getInstance(this).cancelUniqueWork(AnilistNotificationWorker.WORK_NAME) + //run once + androidx.work.WorkManager.getInstance(this).enqueue(OneTimeWorkRequest.Companion.from(AnilistNotificationWorker::class.java)) + } + androidx.work.WorkManager.getInstance(this).cancelUniqueWork("ani.dantotsu.notifications.NotificationWorker") } 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 a298f94d..777b0f62 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt @@ -1339,9 +1339,10 @@ Page(page:$page,perPage:50) { return """MediaListCollection(userId: ${id}, type: $type, chunk:1,perChunk:25, sort: [SCORE_DESC,UPDATED_TIME_DESC]) { lists { entries{ media { id bannerImage } } } }""" } - suspend fun getNotifications(id: Int, page: Int = 1): NotificationResponse? { + suspend fun getNotifications(id: Int, page: Int = 1, resetNotification: Boolean = true): NotificationResponse? { + val reset = if (resetNotification) "true" else "false" 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,}}}}""", + """{User(id:$id){unreadNotificationCount}Page(page:$page,perPage:$ITEMS_PER_PAGE){notifications(resetNotificationCount:$reset){__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) { @@ -1354,10 +1355,9 @@ Page(page:$page,perPage:50) { val filter = if (userId != null) "userId:$userId," else if (global) "isFollowing:false,type:TEXT," else "isFollowing:true,type_not:MESSAGE," - val res = executeQuery( + return 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 likeCount 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 { diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt index bf4310a7..5240c575 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt @@ -699,7 +699,7 @@ class Query { // // Activity reply query // val ActivityReply: ActivityReply?, -// // Comment query +// // CommentNotificationWorker query // val ThreadComment: List?, // // Notification query diff --git a/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt b/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt index 974c41bf..919a13cd 100644 --- a/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt +++ b/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt @@ -129,12 +129,12 @@ class CommentItem(val comment: Comment, viewBinding.modBadge.visibility = if (comment.isMod == true) View.VISIBLE else View.GONE viewBinding.adminBadge.visibility = if (comment.isAdmin == true) View.VISIBLE else View.GONE viewBinding.commentDelete.setOnClickListener { - dialogBuilder("Delete Comment", "Are you sure you want to delete this comment?") { + dialogBuilder("Delete CommentNotificationWorker", "Are you sure you want to delete this comment?") { val scope = CoroutineScope(Dispatchers.Main + SupervisorJob()) scope.launch { val success = CommentsAPI.deleteComment(comment.commentId) if (success) { - snackString("Comment Deleted") + snackString("CommentNotificationWorker Deleted") parentSection.remove(this@CommentItem) } } @@ -152,12 +152,12 @@ class CommentItem(val comment: Comment, } } viewBinding.commentReport.setOnClickListener { - dialogBuilder("Report Comment", "Only report comments that violate the rules. Are you sure you want to report this comment?") { + dialogBuilder("Report CommentNotificationWorker", "Only report comments that violate the rules. Are you sure you want to report this comment?") { val scope = CoroutineScope(Dispatchers.Main + SupervisorJob()) scope.launch { val success = CommentsAPI.reportComment(comment.commentId, comment.username, commentsFragment.mediaName, comment.userId) if (success) { - snackString("Comment Reported") + snackString("CommentNotificationWorker Reported") } } } diff --git a/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt b/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt index 25af1d1d..9fe160ad 100644 --- a/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt @@ -270,7 +270,7 @@ class CommentsFragment : Fragment() { } //adds additional comments to the section - private suspend fun updateUIWithComment(comment: Comment) { + private suspend fun updateUIWithComment(comment: CommentNotificationWorker) { withContext(Dispatchers.Main) { section.add( CommentItem( @@ -297,7 +297,7 @@ class CommentsFragment : Fragment() { override fun afterTextChanged(s: android.text.Editable?) { if (binding.commentInput.text.length > 300) { binding.commentInput.text.delete(300, binding.commentInput.text.length) - snackString("Comment cannot be longer than 300 characters") + snackString("CommentNotificationWorker cannot be longer than 300 characters") } } }) @@ -626,7 +626,7 @@ class CommentsFragment : Fragment() { private fun processComment() { val commentText = binding.commentInput.text.toString() if (commentText.isEmpty()) { - snackString("Comment cannot be empty") + snackString("CommentNotificationWorker cannot be empty") return } @@ -662,7 +662,7 @@ class CommentsFragment : Fragment() { groups.forEach { item -> if (item is CommentItem && item.comment.commentId == commentWithInteraction?.comment?.commentId) { updateCommentItem(item, commentText) - snackString("Comment edited") + snackString("CommentNotificationWorker edited") } } } diff --git a/app/src/main/java/ani/dantotsu/notifications/NotificationWorker.kt b/app/src/main/java/ani/dantotsu/notifications/CommentNotificationWorker.kt similarity index 96% rename from app/src/main/java/ani/dantotsu/notifications/NotificationWorker.kt rename to app/src/main/java/ani/dantotsu/notifications/CommentNotificationWorker.kt index eab40120..5aeb75c3 100644 --- a/app/src/main/java/ani/dantotsu/notifications/NotificationWorker.kt +++ b/app/src/main/java/ani/dantotsu/notifications/CommentNotificationWorker.kt @@ -24,10 +24,9 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import okhttp3.OkHttpClient -import java.util.Locale -class NotificationWorker(appContext: Context, workerParams: WorkerParameters) : +class CommentNotificationWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) { override fun doWork(): Result { val scope = CoroutineScope(Dispatchers.IO) @@ -62,7 +61,7 @@ class NotificationWorker(appContext: Context, workerParams: WorkerParameters) : "" ) } else { - val title = "New Comment Reply" + val title = "New CommentNotificationWorker Reply" val mediaName = names[it.mediaId]?.title ?: "Unknown" val message = "${it.username} replied to your comment in $mediaName" createNotification( @@ -187,6 +186,7 @@ class NotificationWorker(appContext: Context, workerParams: WorkerParameters) : } companion object { - const val WORK_NAME = "ani.dantotsu.notifications.NotificationWorker" + val checkIntervals = arrayOf(0L, 720, 1440) + const val WORK_NAME = "ani.dantotsu.notifications.CommentNotificationWorker" } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationWorker.kt b/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationWorker.kt new file mode 100644 index 00000000..18d8c84e --- /dev/null +++ b/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationWorker.kt @@ -0,0 +1,98 @@ +package ani.dantotsu.notifications.anilist + +import android.Manifest +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import androidx.core.app.ActivityCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.work.Worker +import androidx.work.WorkerParameters +import ani.dantotsu.R +import ani.dantotsu.connections.anilist.Anilist +import ani.dantotsu.profile.activity.ActivityItemBuilder +import ani.dantotsu.profile.activity.FeedActivity +import ani.dantotsu.settings.saving.PrefManager +import ani.dantotsu.settings.saving.PrefName +import eu.kanade.tachiyomi.data.notification.Notifications +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class AnilistNotificationWorker(appContext: Context, workerParams: WorkerParameters) : + Worker(appContext, workerParams) { + + override fun doWork(): Result { + val scope = CoroutineScope(Dispatchers.IO) + scope.launch { + PrefManager.init(applicationContext) //make sure prefs are initialized + val userId = PrefManager.getVal(PrefName.AnilistUserId) + if (userId.isNotEmpty()) { + Anilist.getSavedToken() + val res = Anilist.query.getNotifications(userId.toInt(), resetNotification = false) + val unreadNotificationCount = res?.data?.user?.unreadNotificationCount ?: 0 + if (unreadNotificationCount > 0) { + val unreadNotifications = res?.data?.page?.notifications?.sortedBy { it.id } + ?.takeLast(unreadNotificationCount) + val lastId = PrefManager.getVal(PrefName.LastAnilistNotificationId) + val newNotifications = unreadNotifications?.filter { it.id > lastId } + val filteredTypes = + PrefManager.getVal>(PrefName.AnilistFilteredTypes) + newNotifications?.forEach { + if (!filteredTypes.contains(it.notificationType)) { + val content = ActivityItemBuilder.getContent(it) + val notification = createNotification(applicationContext, content) + if (ActivityCompat.checkSelfPermission( + applicationContext, + Manifest.permission.POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED + ) { + NotificationManagerCompat.from(applicationContext) + .notify( + Notifications.CHANNEL_ANILIST, + System.currentTimeMillis().toInt(), + notification + ) + } + } + } + if (newNotifications?.isNotEmpty() == true) { + PrefManager.setVal(PrefName.LastAnilistNotificationId, 0) + } + } + } + } + return Result.success() + } + + + private fun createNotification( + context: Context, + content: String + ): android.app.Notification { + val title = "New Anilist Notification" + val intent = Intent(applicationContext, FeedActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + val pendingIntent = PendingIntent.getActivity( + applicationContext, + 0, + intent, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + ) + return NotificationCompat.Builder(context, Notifications.CHANNEL_ANILIST) + .setSmallIcon(R.drawable.notification_icon) + .setContentTitle(title) + .setContentText(content) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setContentIntent(pendingIntent) + .setAutoCancel(true) + .build() + } + + companion object { + val checkIntervals = arrayOf(0L, 30, 60, 120, 240, 360, 720, 1440) + const val WORK_NAME = "ani.dantotsu.notifications.anilist.AnilistNotificationWorker" + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt index 245da7bb..2ef1d8ee 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt @@ -17,7 +17,6 @@ import android.view.ViewGroup import android.view.animation.AnimationUtils import android.view.inputmethod.EditorInfo import android.widget.ArrayAdapter -import android.widget.ImageView import android.widget.TextView import android.widget.Toast import androidx.activity.OnBackPressedCallback @@ -34,6 +33,7 @@ import ani.dantotsu.BuildConfig import ani.dantotsu.R import ani.dantotsu.Refresh import ani.dantotsu.connections.anilist.Anilist +import ani.dantotsu.connections.anilist.api.NotificationType import ani.dantotsu.connections.discord.Discord import ani.dantotsu.connections.mal.MAL import ani.dantotsu.copyToClipboard @@ -47,6 +47,8 @@ import ani.dantotsu.initActivity import ani.dantotsu.loadImage import ani.dantotsu.util.Logger import ani.dantotsu.navBarHeight +import ani.dantotsu.notifications.CommentNotificationWorker +import ani.dantotsu.notifications.anilist.AnilistNotificationWorker import ani.dantotsu.openLinkInBrowser import ani.dantotsu.others.AppUpdater import ani.dantotsu.others.CustomBottomDialog @@ -673,6 +675,75 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene true } + val aTimeNames = AnilistNotificationWorker.checkIntervals.map { it.toInt() } + val aItems = aTimeNames.map { + val mins = it % 60 + val hours = it / 60 + if (it > 0) "${if (hours > 0) "$hours hrs " else ""}${if (mins > 0) "$mins mins" else ""}" + else getString(R.string.do_not_update) + } + binding.settingsAnilistSubscriptionsTime.text = + getString(R.string.anilist_notifications_checking_time, aItems[PrefManager.getVal(PrefName.AnilistNotificationInterval)]) + binding.settingsAnilistSubscriptionsTime.setOnClickListener { + + val selected = PrefManager.getVal(PrefName.AnilistNotificationInterval) + val dialog = AlertDialog.Builder(this, R.style.MyPopup) + .setTitle(R.string.subscriptions_checking_time) + .setSingleChoiceItems(aItems.toTypedArray(), selected) { dialog, i -> + PrefManager.setVal(PrefName.AnilistNotificationInterval, i) + binding.settingsAnilistSubscriptionsTime.text = + getString(R.string.anilist_notifications_checking_time, aItems[i]) + dialog.dismiss() + } + .create() + dialog.window?.setDimAmount(0.8f) + dialog.show() + } + + binding.settingsAnilistNotifications.setOnClickListener { + val types = NotificationType.entries.map { it.name } + val filteredTypes = PrefManager.getVal>(PrefName.AnilistFilteredTypes).toMutableSet() + val selected = types.map { filteredTypes.contains(it) }.toBooleanArray() + val dialog = AlertDialog.Builder(this, R.style.MyPopup) + .setTitle(R.string.anilist_notification_filters) + .setMultiChoiceItems(types.toTypedArray(), selected) { _, which, isChecked -> + val type = types[which] + if (isChecked) { + filteredTypes.add(type) + } else { + filteredTypes.remove(type) + } + PrefManager.setVal(PrefName.AnilistFilteredTypes, filteredTypes) + } + .create() + dialog.window?.setDimAmount(0.8f) + dialog.show() + } + + val cTimeNames = CommentNotificationWorker.checkIntervals.map { it.toInt() } + val cItems = cTimeNames.map { + val mins = it % 60 + val hours = it / 60 + if (it > 0) "${if (hours > 0) "$hours hrs " else ""}${if (mins > 0) "$mins mins" else ""}" + else getString(R.string.do_not_update) + } + binding.settingsCommentSubscriptionsTime.text = + getString(R.string.comment_notification_checking_time, cItems[PrefManager.getVal(PrefName.CommentNotificationInterval)]) + binding.settingsCommentSubscriptionsTime.setOnClickListener { + val selected = PrefManager.getVal(PrefName.CommentNotificationInterval) + val dialog = AlertDialog.Builder(this, R.style.MyPopup) + .setTitle(R.string.subscriptions_checking_time) + .setSingleChoiceItems(cItems.toTypedArray(), selected) { dialog, i -> + PrefManager.setVal(PrefName.CommentNotificationInterval, i) + binding.settingsCommentSubscriptionsTime.text = + getString(R.string.comment_notification_checking_time, cItems[i]) + dialog.dismiss() + } + .create() + dialog.window?.setDimAmount(0.8f) + dialog.show() + } + binding.settingsNotificationsCheckingSubscriptions.isChecked = PrefManager.getVal(PrefName.SubscriptionCheckingNotifications) binding.settingsNotificationsCheckingSubscriptions.setOnCheckedChangeListener { _, isChecked -> diff --git a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt index 0e0a6a22..ccfb99f1 100644 --- a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt +++ b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt @@ -33,7 +33,10 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files MangaSourcesOrder(Pref(Location.General, List::class, listOf())), MangaSearchHistory(Pref(Location.General, Set::class, setOf())), NovelSourcesOrder(Pref(Location.General, List::class, listOf())), - NotificationInterval(Pref(Location.General, Int::class, 0)), + CommentNotificationInterval(Pref(Location.General, Int::class, 0)), + AnilistNotificationInterval(Pref(Location.General, Int::class, 3)), + LastAnilistNotificationId(Pref(Location.General, Int::class, 0)), + AnilistFilteredTypes(Pref(Location.General, Set::class, setOf())), //User Interface UseOLED(Pref(Location.UI, Boolean::class, false)), diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt index 148492df..11491edf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt @@ -53,6 +53,13 @@ object Notifications { const val CHANNEL_COMMENT_WARING = "comment_warning_channel" const val ID_COMMENT_REPLY = -801 + /** + * Notification channel and ids used for anilist updates. + */ + const val GROUP_ANILIST = "group_anilist" + const val CHANNEL_ANILIST = "anilist_channel" + const val ID_ANILIST = -901 + /** * Notification channel and ids used for app and extension updates. @@ -105,6 +112,9 @@ object Notifications { buildNotificationChannelGroup(GROUP_COMMENTS) { setName("Comments") }, + buildNotificationChannelGroup(GROUP_ANILIST) { + setName("Anilist") + }, ), ) @@ -134,9 +144,13 @@ object Notifications { setGroup(GROUP_COMMENTS) }, buildNotificationChannel(CHANNEL_COMMENT_WARING, IMPORTANCE_HIGH) { - setName("Comment Warnings") + setName("CommentNotificationWorker Warnings") setGroup(GROUP_COMMENTS) }, + buildNotificationChannel(CHANNEL_ANILIST, IMPORTANCE_DEFAULT) { + setName("Anilist") + setGroup(GROUP_ANILIST) + }, buildNotificationChannel(CHANNEL_APP_UPDATE, IMPORTANCE_DEFAULT) { setGroup(GROUP_APK_UPDATES) setName("App Updates") diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 4b83127e..42fe4cf0 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -971,70 +971,6 @@ app:showText="false" app:thumbTint="@color/button_switch_track" /> - - - - - - - - - - - - - - - - + + +