feat: anilist notifications (real)

This commit is contained in:
rebelonion 2024-03-14 06:00:48 -05:00
parent 19697f4f39
commit 4d2a08c258
12 changed files with 390 additions and 95 deletions

View file

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

View file

@ -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<NotificationResponse>(
"""{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<FeedResponse>(
return executeQuery<FeedResponse>(
"""{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 {

View file

@ -699,7 +699,7 @@ class Query {
// // Activity reply query
// val ActivityReply: ActivityReply?,
// // Comment query
// // CommentNotificationWorker query
// val ThreadComment: List<ThreadComment>?,
// // Notification query

View file

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

View file

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

View file

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

View file

@ -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<String>(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<Int>(PrefName.LastAnilistNotificationId)
val newNotifications = unreadNotifications?.filter { it.id > lastId }
val filteredTypes =
PrefManager.getVal<Set<String>>(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"
}
}

View file

@ -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<Int>(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<Set<String>>(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<Int>(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 ->

View file

@ -33,7 +33,10 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
MangaSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
MangaSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
NovelSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
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<String>())),
//User Interface
UseOLED(Pref(Location.UI, Boolean::class, false)),

View file

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

View file

@ -971,70 +971,6 @@
app:showText="false"
app:thumbTint="@color/button_switch_track" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/settingsShareUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="false"
android:drawableStart="@drawable/ic_round_search_24"
android:drawablePadding="16dp"
android:elegantTextHeight="true"
android:fontFamily="@font/poppins_bold"
android:minHeight="64dp"
android:text="@string/share_username_in_crash_reports"
android:textAlignment="viewStart"
android:textColor="?attr/colorOnBackground"
app:cornerRadius="0dp"
app:drawableTint="?attr/colorPrimary"
app:showText="false"
app:thumbTint="@color/button_switch_track" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/settingsLogToFile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="false"
android:drawableStart="@drawable/ic_round_edit_note_24"
android:drawablePadding="16dp"
android:elegantTextHeight="true"
android:fontFamily="@font/poppins_bold"
android:minHeight="64dp"
android:text="@string/log_to_file"
android:textAlignment="viewStart"
android:textColor="?attr/colorOnBackground"
app:cornerRadius="0dp"
app:drawableTint="?attr/colorPrimary"
app:showText="false"
app:thumbTint="@color/button_switch_track" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:alpha="0.58"
android:fontFamily="@font/poppins_bold"
android:text="@string/logging_warning" />
<ImageButton
android:id="@+id/settingsShareLog"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_round_share_24"
android:padding="16dp" />
</LinearLayout>
</ani.dantotsu.others.Xpandable>
<ani.dantotsu.others.Xpandable
@ -1440,6 +1376,81 @@
android:text="@string/subscriptions_info"
android:textSize="14sp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="-16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="-16dp"
android:background="?android:attr/listDivider" />
<Button
android:id="@+id/settingsAnilistNotifications"
android:layout_width="match_parent"
android:layout_height="64dp"
android:layout_marginStart="-31dp"
android:layout_marginEnd="-31dp"
android:background="@drawable/ui_bg"
android:fontFamily="@font/poppins_bold"
android:insetTop="0dp"
android:insetBottom="0dp"
android:paddingStart="31dp"
android:paddingEnd="31dp"
android:text="@string/anilist_notification_filters"
android:textAlignment="viewStart"
android:textAllCaps="false"
android:textColor="?attr/colorOnBackground"
app:cornerRadius="0dp"
app:icon="@drawable/ic_anilist"
app:iconPadding="16dp"
app:iconSize="24dp"
app:iconTint="?attr/colorPrimary" />
<Button
android:id="@+id/settingsAnilistSubscriptionsTime"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="match_parent"
android:layout_height="64dp"
android:layout_marginStart="-11dp"
android:layout_marginTop="8dp"
android:fontFamily="@font/poppins_bold"
android:insetTop="0dp"
android:insetBottom="0dp"
android:text="@string/anilist_notifications_checking_time"
android:textAlignment="viewStart"
android:textAllCaps="false"
android:textColor="?attr/colorOnBackground"
app:cornerRadius="0dp"
app:icon="@drawable/ic_round_notifications_none_24"
app:iconPadding="16dp"
app:iconSize="24dp" />
<Button
android:id="@+id/settingsCommentSubscriptionsTime"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="match_parent"
android:layout_height="64dp"
android:layout_marginStart="-11dp"
android:layout_marginTop="8dp"
android:fontFamily="@font/poppins_bold"
android:insetTop="0dp"
android:insetBottom="0dp"
android:text="@string/comment_notification_checking_time"
android:textAlignment="viewStart"
android:textAllCaps="false"
android:textColor="?attr/colorOnBackground"
app:cornerRadius="0dp"
app:icon="@drawable/ic_round_notifications_none_24"
app:iconPadding="16dp"
app:iconSize="24dp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="-16dp"
android:layout_marginEnd="-16dp"
android:background="?android:attr/listDivider" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/settingsNotificationsCheckingSubscriptions"
android:layout_width="match_parent"
@ -1517,6 +1528,67 @@
app:drawableTint="?attr/colorPrimary"
app:showText="false"
app:thumbTint="@color/button_switch_track" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/settingsShareUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="false"
android:drawableStart="@drawable/ic_round_search_24"
android:drawablePadding="16dp"
android:elegantTextHeight="true"
android:fontFamily="@font/poppins_bold"
android:minHeight="64dp"
android:text="@string/share_username_in_crash_reports"
android:textAlignment="viewStart"
android:textColor="?attr/colorOnBackground"
app:cornerRadius="0dp"
app:drawableTint="?attr/colorPrimary"
app:showText="false"
app:thumbTint="@color/button_switch_track" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/settingsLogToFile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="false"
android:drawableStart="@drawable/ic_round_edit_note_24"
android:drawablePadding="16dp"
android:elegantTextHeight="true"
android:fontFamily="@font/poppins_bold"
android:minHeight="64dp"
android:text="@string/log_to_file"
android:textAlignment="viewStart"
android:textColor="?attr/colorOnBackground"
app:cornerRadius="0dp"
app:drawableTint="?attr/colorPrimary"
app:showText="false"
app:thumbTint="@color/button_switch_track" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:alpha="0.58"
android:fontFamily="@font/poppins_bold"
android:text="@string/logging_warning" />
<ImageButton
android:id="@+id/settingsShareLog"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_round_share_24"
android:padding="16dp" />
</LinearLayout>
<Button
android:id="@+id/settingsDev"

View file

@ -700,4 +700,7 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
<string name="blur">Blur</string>
<string name="hide_scroll_bar">Hide Scroll Bar</string>
<string name="view_on_anilist">View on AniList</string>
<string name="anilist_notification_filters">Anilist Notification Filters</string>
<string name="anilist_notifications_checking_time">Anilist notifications update frequency : %1$s</string>
<string name="comment_notification_checking_time">Comment notifications update frequency : %1$s</string>
</resources>