diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 52ad1196..11800322 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -16,6 +16,7 @@
+
@@ -314,6 +315,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
(PrefName.UseAlarmManager)
- // 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))
- }
+ TaskScheduler.create(this, useAlarmManager).scheduleAllTasks(this)
+
+ androidx.work.WorkManager.getInstance(this)
+ .enqueue(OneTimeWorkRequest.Companion.from(CommentNotificationWorker::class.java))
+
+ androidx.work.WorkManager.getInstance(this)
+ .enqueue(OneTimeWorkRequest.Companion.from(AnilistNotificationWorker::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") //legacy worker
}
diff --git a/app/src/main/java/ani/dantotsu/notifications/AlarmManagerScheduler.kt b/app/src/main/java/ani/dantotsu/notifications/AlarmManagerScheduler.kt
new file mode 100644
index 00000000..63dc757c
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/notifications/AlarmManagerScheduler.kt
@@ -0,0 +1,77 @@
+package ani.dantotsu.notifications
+
+import android.app.AlarmManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import ani.dantotsu.notifications.anilist.AnilistNotificationReceiver
+import ani.dantotsu.notifications.comment.CommentNotificationReceiver
+import ani.dantotsu.notifications.TaskScheduler.TaskType
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
+import java.util.concurrent.TimeUnit
+
+class AlarmManagerScheduler(private val context: Context) : TaskScheduler {
+ override fun scheduleRepeatingTask(taskType: TaskType, interval: Long) {
+ val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
+ val intent = when (taskType) {
+ TaskType.COMMENT_NOTIFICATION -> Intent(
+ context,
+ CommentNotificationReceiver::class.java
+ )
+
+ TaskType.ANILIST_NOTIFICATION -> Intent(
+ context,
+ AnilistNotificationReceiver::class.java
+ )
+ }
+
+ val pendingIntent = PendingIntent.getBroadcast(
+ context,
+ taskType.ordinal,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+
+ val triggerAtMillis = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(interval)
+ try {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ alarmManager.setExactAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP,
+ triggerAtMillis,
+ pendingIntent
+ )
+ } else {
+ alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent)
+ }
+ } catch (e: SecurityException) {
+ PrefManager.setVal(PrefName.UseAlarmManager, false)
+ TaskScheduler.create(context, true).cancelAllTasks()
+ TaskScheduler.create(context, false).scheduleAllTasks(context)
+ }
+ }
+
+ override fun cancelTask(taskType: TaskType) {
+ val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
+ val intent = when (taskType) {
+ TaskType.COMMENT_NOTIFICATION -> Intent(
+ context,
+ CommentNotificationReceiver::class.java
+ )
+
+ TaskType.ANILIST_NOTIFICATION -> Intent(
+ context,
+ AnilistNotificationReceiver::class.java
+ )
+ }
+
+ val pendingIntent = PendingIntent.getBroadcast(
+ context,
+ taskType.ordinal,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ alarmManager.cancel(pendingIntent)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/notifications/BootCompletedReceiver.kt b/app/src/main/java/ani/dantotsu/notifications/BootCompletedReceiver.kt
new file mode 100644
index 00000000..3ebc9412
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/notifications/BootCompletedReceiver.kt
@@ -0,0 +1,64 @@
+package ani.dantotsu.notifications
+
+import android.app.AlarmManager
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
+import ani.dantotsu.notifications.comment.CommentNotificationWorker
+import ani.dantotsu.notifications.TaskScheduler.TaskType
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
+import ani.dantotsu.util.Logger
+
+class BootCompletedReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
+ if (context != null) {
+ val scheduler = AlarmManagerScheduler(context)
+ PrefManager.init(context)
+ Logger.init(context)
+ Logger.log("Starting Dantotsu Subscription Service on Boot")
+ if (PrefManager.getVal(PrefName.UseAlarmManager)) {
+ val commentInterval =
+ CommentNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.CommentNotificationInterval)]
+ val anilistInterval =
+ AnilistNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.AnilistNotificationInterval)]
+ scheduler.scheduleRepeatingTask(
+ TaskType.COMMENT_NOTIFICATION,
+ commentInterval
+ )
+ scheduler.scheduleRepeatingTask(
+ TaskType.ANILIST_NOTIFICATION,
+ anilistInterval
+ )
+ }
+ }
+ }
+ }
+}
+
+class AlarmPermissionStateReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (intent?.action == AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED) {
+ if (context != null) {
+ PrefManager.init(context)
+ val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
+ val canScheduleExactAlarms = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ alarmManager.canScheduleExactAlarms()
+ } else {
+ true
+ }
+ if(canScheduleExactAlarms) {
+ TaskScheduler.create(context, false).cancelAllTasks()
+ TaskScheduler.create(context, true).scheduleAllTasks(context)
+ } else {
+ TaskScheduler.create(context, true).cancelAllTasks()
+ TaskScheduler.create(context, false).scheduleAllTasks(context)
+ }
+ PrefManager.setVal(PrefName.UseAlarmManager, canScheduleExactAlarms)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/ani/dantotsu/notifications/TaskScheduler.kt b/app/src/main/java/ani/dantotsu/notifications/TaskScheduler.kt
new file mode 100644
index 00000000..1a75422c
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/notifications/TaskScheduler.kt
@@ -0,0 +1,48 @@
+package ani.dantotsu.notifications
+
+import android.content.Context
+import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
+import ani.dantotsu.notifications.comment.CommentNotificationWorker
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
+
+interface TaskScheduler {
+ fun scheduleRepeatingTask(taskType: TaskType, interval: Long)
+ fun cancelTask(taskType: TaskType)
+
+ fun cancelAllTasks() {
+ for (taskType in TaskType.entries) {
+ cancelTask(taskType)
+ }
+ }
+
+ fun scheduleAllTasks(context: Context) {
+ for (taskType in TaskType.entries) {
+ val interval = when (taskType) {
+ TaskType.COMMENT_NOTIFICATION -> CommentNotificationWorker.checkIntervals[PrefManager.getVal(
+ PrefName.CommentNotificationInterval)]
+ TaskType.ANILIST_NOTIFICATION -> AnilistNotificationWorker.checkIntervals[PrefManager.getVal(
+ PrefName.AnilistNotificationInterval)]
+ }
+ scheduleRepeatingTask(taskType, interval)
+ }
+ }
+
+ companion object {
+ fun create(context: Context, useAlarmManager: Boolean): TaskScheduler {
+ return if (useAlarmManager) {
+ AlarmManagerScheduler(context)
+ } else {
+ WorkManagerScheduler(context)
+ }
+ }
+ }
+ enum class TaskType {
+ COMMENT_NOTIFICATION,
+ ANILIST_NOTIFICATION
+ }
+}
+
+interface Task {
+ suspend fun execute(context: Context): Boolean
+}
diff --git a/app/src/main/java/ani/dantotsu/notifications/WorkManagerScheduler.kt b/app/src/main/java/ani/dantotsu/notifications/WorkManagerScheduler.kt
new file mode 100644
index 00000000..aa8c9439
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/notifications/WorkManagerScheduler.kt
@@ -0,0 +1,62 @@
+package ani.dantotsu.notifications
+
+import android.content.Context
+import androidx.work.Constraints
+import androidx.work.PeriodicWorkRequest
+import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
+import ani.dantotsu.notifications.comment.CommentNotificationWorker
+import ani.dantotsu.notifications.TaskScheduler.TaskType
+
+class WorkManagerScheduler(private val context: Context) : TaskScheduler {
+ override fun scheduleRepeatingTask(taskType: TaskType, interval: Long) {
+ val constraints = Constraints.Builder()
+ .setRequiredNetworkType(androidx.work.NetworkType.CONNECTED)
+ .build()
+
+ when (taskType) {
+ TaskType.COMMENT_NOTIFICATION -> {
+ val recurringWork = PeriodicWorkRequest.Builder(
+ CommentNotificationWorker::class.java,
+ interval, java.util.concurrent.TimeUnit.MINUTES,
+ PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS, java.util.concurrent.TimeUnit.MINUTES
+ )
+ .setConstraints(constraints)
+ .build()
+ androidx.work.WorkManager.getInstance(context).enqueueUniquePeriodicWork(
+ CommentNotificationWorker.WORK_NAME,
+ androidx.work.ExistingPeriodicWorkPolicy.UPDATE,
+ recurringWork
+ )
+ }
+
+ TaskType.ANILIST_NOTIFICATION -> {
+ val recurringWork = PeriodicWorkRequest.Builder(
+ AnilistNotificationWorker::class.java,
+ interval, java.util.concurrent.TimeUnit.MINUTES,
+ PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS, java.util.concurrent.TimeUnit.MINUTES
+ )
+ .setConstraints(constraints)
+ .build()
+ androidx.work.WorkManager.getInstance(context).enqueueUniquePeriodicWork(
+ AnilistNotificationWorker.WORK_NAME,
+ androidx.work.ExistingPeriodicWorkPolicy.UPDATE,
+ recurringWork
+ )
+ }
+ }
+ }
+
+ override fun cancelTask(taskType: TaskType) {
+ when (taskType) {
+ TaskType.COMMENT_NOTIFICATION -> {
+ androidx.work.WorkManager.getInstance(context)
+ .cancelUniqueWork(CommentNotificationWorker.WORK_NAME)
+ }
+
+ TaskType.ANILIST_NOTIFICATION -> {
+ androidx.work.WorkManager.getInstance(context)
+ .cancelUniqueWork(AnilistNotificationWorker.WORK_NAME)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationReceiver.kt b/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationReceiver.kt
new file mode 100644
index 00000000..ace8029c
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationReceiver.kt
@@ -0,0 +1,25 @@
+package ani.dantotsu.notifications.anilist
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import ani.dantotsu.notifications.AlarmManagerScheduler
+import ani.dantotsu.notifications.TaskScheduler
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
+import ani.dantotsu.util.Logger
+import kotlinx.coroutines.runBlocking
+
+class AnilistNotificationReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ Logger.log("AnilistNotificationReceiver: onReceive")
+ if (context != null) {
+ runBlocking {
+ AnilistNotificationTask().execute(context)
+ }
+ val anilistInterval =
+ AnilistNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.AnilistNotificationInterval)]
+ AlarmManagerScheduler(context).scheduleRepeatingTask(TaskScheduler.TaskType.ANILIST_NOTIFICATION, anilistInterval)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationTask.kt b/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationTask.kt
new file mode 100644
index 00000000..56be25ad
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationTask.kt
@@ -0,0 +1,109 @@
+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 ani.dantotsu.MainActivity
+import ani.dantotsu.R
+import ani.dantotsu.connections.anilist.Anilist
+import ani.dantotsu.notifications.Task
+import ani.dantotsu.profile.activity.ActivityItemBuilder
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
+import ani.dantotsu.util.Logger
+import eu.kanade.tachiyomi.data.notification.Notifications
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+class AnilistNotificationTask : Task {
+ override suspend fun execute(context: Context): Boolean {
+ try {
+ withContext(Dispatchers.IO) {
+ PrefManager.init(context) //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(context, content, it.id)
+ if (ActivityCompat.checkSelfPermission(
+ context,
+ Manifest.permission.POST_NOTIFICATIONS
+ ) == PackageManager.PERMISSION_GRANTED
+ ) {
+ NotificationManagerCompat.from(context)
+ .notify(
+ Notifications.CHANNEL_ANILIST,
+ System.currentTimeMillis().toInt(),
+ notification
+ )
+ }
+ }
+ }
+ if (newNotifications?.isNotEmpty() == true) {
+ PrefManager.setVal(
+ PrefName.LastAnilistNotificationId,
+ newNotifications.last().id
+ )
+ }
+ }
+ }
+ }
+ return true
+ } catch (e: Exception) {
+ Logger.log("AnilistNotificationTask: ${e.message}")
+ Logger.log(e)
+ return false
+ }
+ }
+
+ private fun createNotification(
+ context: Context,
+ content: String,
+ notificationId: Int? = null
+ ): android.app.Notification {
+ val title = "New Anilist Notification"
+ val intent = Intent(context, MainActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ putExtra("FRAGMENT_TO_LOAD", "NOTIFICATIONS")
+ if (notificationId != null) {
+ Logger.log("notificationId: $notificationId")
+ putExtra("activityId", notificationId)
+ }
+ }
+ val pendingIntent = PendingIntent.getActivity(
+ context,
+ notificationId ?: 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()
+ }
+
+}
\ 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
index 50a9befb..645b9733 100644
--- a/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationWorker.kt
+++ b/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationWorker.kt
@@ -1,102 +1,20 @@
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.CoroutineWorker
import androidx.work.WorkerParameters
-import ani.dantotsu.MainActivity
-import ani.dantotsu.R
-import ani.dantotsu.connections.anilist.Anilist
-import ani.dantotsu.profile.activity.ActivityItemBuilder
-import ani.dantotsu.settings.saving.PrefManager
-import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.util.Logger
-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) {
+ CoroutineWorker(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, it.id)
- 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, newNotifications.last().id)
- }
- }
- }
+ override suspend fun doWork(): Result {
+ Logger.log("AnilistNotificationWorker: doWork")
+ return if (AnilistNotificationTask().execute(applicationContext)) {
+ Result.success()
+ } else {
+ Result.retry()
}
- return Result.success()
- }
-
-
- private fun createNotification(
- context: Context,
- content: String,
- notificationId: Int? = null
- ): android.app.Notification {
- val title = "New Anilist Notification"
- val intent = Intent(applicationContext, MainActivity::class.java).apply {
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
- putExtra("FRAGMENT_TO_LOAD", "NOTIFICATIONS")
- if (notificationId != null) {
- Logger.log("notificationId: $notificationId")
- putExtra("activityId", notificationId)
- }
- }
- val pendingIntent = PendingIntent.getActivity(
- applicationContext,
- notificationId ?: 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 {
diff --git a/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationReceiver.kt b/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationReceiver.kt
new file mode 100644
index 00000000..65c78e98
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationReceiver.kt
@@ -0,0 +1,18 @@
+package ani.dantotsu.notifications.comment
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import ani.dantotsu.util.Logger
+import kotlinx.coroutines.runBlocking
+
+class CommentNotificationReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ Logger.log("CommentNotificationReceiver: onReceive")
+ if (context != null) {
+ runBlocking {
+ CommentNotificationTask().execute(context)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationTask.kt b/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationTask.kt
new file mode 100644
index 00000000..ec6b2361
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationTask.kt
@@ -0,0 +1,316 @@
+package ani.dantotsu.notifications.comment
+
+import android.Manifest
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Canvas
+import android.graphics.Color
+import androidx.core.app.ActivityCompat
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.content.ContextCompat
+import ani.dantotsu.MainActivity
+import ani.dantotsu.R
+import ani.dantotsu.connections.comments.CommentsAPI
+import ani.dantotsu.notifications.Task
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
+import ani.dantotsu.util.Logger
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import okhttp3.OkHttpClient
+
+class CommentNotificationTask : Task {
+ override suspend fun execute(context: Context): Boolean {
+ try {
+ withContext(Dispatchers.IO) {
+ PrefManager.init(context) //make sure prefs are initialized
+ val client = OkHttpClient()
+ CommentsAPI.fetchAuthToken(client)
+ val notificationResponse = CommentsAPI.getNotifications(client)
+ var notifications = notificationResponse?.notifications?.toMutableList()
+ //if we have at least one reply notification, we need to fetch the media titles
+ var names = emptyMap()
+ if (notifications?.any { it.type == 1 || it.type == null } == true) {
+ val mediaIds =
+ notifications.filter { it.type == 1 || it.type == null }.map { it.mediaId }
+ names = MediaNameFetch.fetchMediaTitles(mediaIds)
+ }
+
+ val recentGlobal = PrefManager.getVal(
+ PrefName.RecentGlobalNotification
+ )
+
+ notifications =
+ notifications?.filter { it.type != 3 || it.notificationId > recentGlobal }
+ ?.toMutableList()
+
+ val newRecentGlobal =
+ notifications?.filter { it.type == 3 }?.maxOfOrNull { it.notificationId }
+ if (newRecentGlobal != null) {
+ PrefManager.setVal(PrefName.RecentGlobalNotification, newRecentGlobal)
+ }
+ if (notifications.isNullOrEmpty()) return@withContext
+ PrefManager.setVal(
+ PrefName.UnreadCommentNotifications,
+ PrefManager.getVal(PrefName.UnreadCommentNotifications) + (notifications.size
+ ?: 0)
+ )
+
+ notifications.forEach {
+ val type: CommentNotificationWorker.NotificationType = when (it.type) {
+ 1 -> CommentNotificationWorker.NotificationType.COMMENT_REPLY
+ 2 -> CommentNotificationWorker.NotificationType.COMMENT_WARNING
+ 3 -> CommentNotificationWorker.NotificationType.APP_GLOBAL
+ 420 -> CommentNotificationWorker.NotificationType.NO_NOTIFICATION
+ else -> CommentNotificationWorker.NotificationType.UNKNOWN
+ }
+ val notification = when (type) {
+ CommentNotificationWorker.NotificationType.COMMENT_WARNING -> {
+ val title = "You received a warning"
+ val message = it.content ?: "Be more thoughtful with your comments"
+
+ val commentStore = CommentStore(
+ title,
+ message,
+ it.mediaId,
+ it.commentId
+ )
+ addNotificationToStore(commentStore)
+
+ createNotification(
+ context,
+ CommentNotificationWorker.NotificationType.COMMENT_WARNING,
+ message,
+ title,
+ it.mediaId,
+ it.commentId,
+ "",
+ ""
+ )
+ }
+
+ CommentNotificationWorker.NotificationType.COMMENT_REPLY -> {
+ val title = "New Comment Reply"
+ val mediaName = names[it.mediaId]?.title ?: "Unknown"
+ val message = "${it.username} replied to your comment in $mediaName"
+
+ val commentStore = CommentStore(
+ title,
+ message,
+ it.mediaId,
+ it.commentId
+ )
+ addNotificationToStore(commentStore)
+
+ createNotification(
+ context,
+ CommentNotificationWorker.NotificationType.COMMENT_REPLY,
+ message,
+ title,
+ it.mediaId,
+ it.commentId,
+ names[it.mediaId]?.color ?: "#222222",
+ names[it.mediaId]?.coverImage ?: ""
+ )
+ }
+
+ CommentNotificationWorker.NotificationType.APP_GLOBAL -> {
+ val title = "Update from Dantotsu"
+ val message = it.content ?: "New feature available"
+
+ val commentStore = CommentStore(
+ title,
+ message,
+ null,
+ null
+ )
+ addNotificationToStore(commentStore)
+
+ createNotification(
+ context,
+ CommentNotificationWorker.NotificationType.APP_GLOBAL,
+ message,
+ title,
+ 0,
+ 0,
+ "",
+ ""
+ )
+ }
+
+ CommentNotificationWorker.NotificationType.NO_NOTIFICATION -> {
+ PrefManager.removeCustomVal("genre_thumb")
+ PrefManager.removeCustomVal("banner_ANIME_time")
+ PrefManager.removeCustomVal("banner_MANGA_time")
+ PrefManager.setVal(PrefName.ImageUrl, it.content ?: "")
+ null
+ }
+
+ CommentNotificationWorker.NotificationType.UNKNOWN -> {
+ null
+ }
+ }
+
+ if (ActivityCompat.checkSelfPermission(
+ context,
+ Manifest.permission.POST_NOTIFICATIONS
+ ) == PackageManager.PERMISSION_GRANTED
+ ) {
+ if (notification != null) {
+ NotificationManagerCompat.from(context)
+ .notify(
+ type.id,
+ System.currentTimeMillis().toInt(),
+ notification
+ )
+ }
+ }
+ }
+ }
+ return true
+ } catch (e: Exception) {
+ Logger.log("CommentNotificationTask: ${e.message}")
+ Logger.log(e)
+ return false
+ }
+ }
+
+ private fun addNotificationToStore(notification: CommentStore) {
+ val notificationStore = PrefManager.getNullableVal>(
+ PrefName.CommentNotificationStore,
+ null
+ ) ?: listOf()
+ val newStore = notificationStore.toMutableList()
+ if (newStore.size > 10) {
+ newStore.remove(newStore.minByOrNull { it.time })
+ }
+ if (newStore.any { it.content == notification.content }) {
+ return
+ }
+ newStore.add(notification)
+ PrefManager.setVal(PrefName.CommentNotificationStore, newStore)
+ }
+
+ private fun createNotification(
+ context: Context,
+ notificationType: CommentNotificationWorker.NotificationType,
+ message: String,
+ title: String,
+ mediaId: Int,
+ commentId: Int,
+ color: String,
+ imageUrl: String
+ ): android.app.Notification? {
+ Logger.log(
+ "Creating notification of type $notificationType" +
+ ", message: $message, title: $title, mediaId: $mediaId, commentId: $commentId"
+ )
+ val notification = when (notificationType) {
+ CommentNotificationWorker.NotificationType.COMMENT_WARNING -> {
+ val intent = Intent(context, MainActivity::class.java).apply {
+ putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
+ putExtra("mediaId", mediaId)
+ putExtra("commentId", commentId)
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ }
+ val pendingIntent = PendingIntent.getActivity(
+ context,
+ commentId,
+ intent,
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ )
+ val builder = NotificationCompat.Builder(context, notificationType.id)
+ .setContentTitle(title)
+ .setContentText(message)
+ .setSmallIcon(R.drawable.notification_icon)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setContentIntent(pendingIntent)
+ .setAutoCancel(true)
+ builder.build()
+ }
+
+ CommentNotificationWorker.NotificationType.COMMENT_REPLY -> {
+ val intent = Intent(context, MainActivity::class.java).apply {
+ putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
+ putExtra("mediaId", mediaId)
+ putExtra("commentId", commentId)
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ }
+ val pendingIntent = PendingIntent.getActivity(
+ context,
+ commentId,
+ intent,
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ )
+ val builder = NotificationCompat.Builder(context, notificationType.id)
+ .setContentTitle(title)
+ .setContentText(message)
+ .setSmallIcon(R.drawable.notification_icon)
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ .setContentIntent(pendingIntent)
+ .setAutoCancel(true)
+ if (imageUrl.isNotEmpty()) {
+ val bitmap = getBitmapFromUrl(imageUrl)
+ if (bitmap != null) {
+ builder.setLargeIcon(bitmap)
+ }
+ }
+ if (color.isNotEmpty()) {
+ builder.color = Color.parseColor(color)
+ }
+ builder.build()
+ }
+
+ CommentNotificationWorker.NotificationType.APP_GLOBAL -> {
+ val intent = Intent(context, MainActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ }
+ val pendingIntent = PendingIntent.getActivity(
+ context,
+ System.currentTimeMillis().toInt(),
+ intent,
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ )
+ val builder = NotificationCompat.Builder(context, notificationType.id)
+ .setContentTitle(title)
+ .setContentText(message)
+ .setSmallIcon(R.drawable.notification_icon)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setContentIntent(pendingIntent)
+ .setAutoCancel(true)
+ builder.build()
+ }
+
+ else -> {
+ null
+ }
+ }
+ return notification
+ }
+
+ private fun getBitmapFromVectorDrawable(context: Context, drawableId: Int): Bitmap? {
+ val drawable = ContextCompat.getDrawable(context, drawableId) ?: return null
+ val bitmap = Bitmap.createBitmap(
+ drawable.intrinsicWidth,
+ drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
+ )
+ val canvas = Canvas(bitmap)
+ drawable.setBounds(0, 0, canvas.width, canvas.height)
+ drawable.draw(canvas)
+ return bitmap
+ }
+
+ private fun getBitmapFromUrl(url: String): Bitmap? {
+ return try {
+ val inputStream = java.net.URL(url).openStream()
+ BitmapFactory.decodeStream(inputStream)
+ } catch (e: Exception) {
+ null
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationWorker.kt b/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationWorker.kt
index 5ecc65db..5b75b481 100644
--- a/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationWorker.kt
+++ b/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationWorker.kt
@@ -1,310 +1,20 @@
package ani.dantotsu.notifications.comment
-import android.Manifest
-import android.app.PendingIntent
import android.content.Context
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
-import android.graphics.Canvas
-import android.graphics.Color
-import androidx.core.app.ActivityCompat
-import androidx.core.app.NotificationCompat
-import androidx.core.app.NotificationManagerCompat
-import androidx.core.content.ContextCompat
-import androidx.work.Worker
+import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
-import ani.dantotsu.MainActivity
-import ani.dantotsu.R
-import ani.dantotsu.connections.comments.CommentsAPI
-import ani.dantotsu.settings.saving.PrefManager
-import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.data.notification.Notifications
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import okhttp3.OkHttpClient
class CommentNotificationWorker(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 client = OkHttpClient()
- CommentsAPI.fetchAuthToken(client)
- val notificationResponse = CommentsAPI.getNotifications(client)
- var notifications = notificationResponse?.notifications?.toMutableList()
- //if we have at least one reply notification, we need to fetch the media titles
- var names = emptyMap()
- if (notifications?.any { it.type == 1 || it.type == null } == true) {
- val mediaIds =
- notifications.filter { it.type == 1 || it.type == null }.map { it.mediaId }
- names = MediaNameFetch.fetchMediaTitles(mediaIds)
- }
-
- val recentGlobal = PrefManager.getVal(
- PrefName.RecentGlobalNotification
- )
-
- notifications =
- notifications?.filter { it.type != 3 || it.notificationId > recentGlobal }
- ?.toMutableList()
-
- val newRecentGlobal =
- notifications?.filter { it.type == 3 }?.maxOfOrNull { it.notificationId }
- if (newRecentGlobal != null) {
- PrefManager.setVal(PrefName.RecentGlobalNotification, newRecentGlobal)
- }
- if (notifications.isNullOrEmpty()) return@launch
- PrefManager.setVal(PrefName.UnreadCommentNotifications,
- PrefManager.getVal(PrefName.UnreadCommentNotifications) + (notifications?.size ?: 0)
- )
-
- notifications.forEach {
- val type: NotificationType = when (it.type) {
- 1 -> NotificationType.COMMENT_REPLY
- 2 -> NotificationType.COMMENT_WARNING
- 3 -> NotificationType.APP_GLOBAL
- 420 -> NotificationType.NO_NOTIFICATION
- else -> NotificationType.UNKNOWN
- }
- val notification = when (type) {
- NotificationType.COMMENT_WARNING -> {
- val title = "You received a warning"
- val message = it.content ?: "Be more thoughtful with your comments"
-
- val commentStore = CommentStore(
- title,
- message,
- it.mediaId,
- it.commentId
- )
- addNotificationToStore(commentStore)
-
- createNotification(
- NotificationType.COMMENT_WARNING,
- message,
- title,
- it.mediaId,
- it.commentId,
- "",
- ""
- )
- }
-
- NotificationType.COMMENT_REPLY -> {
- val title = "New Comment Reply"
- val mediaName = names[it.mediaId]?.title ?: "Unknown"
- val message = "${it.username} replied to your comment in $mediaName"
-
- val commentStore = CommentStore(
- title,
- message,
- it.mediaId,
- it.commentId
- )
- addNotificationToStore(commentStore)
-
- createNotification(
- NotificationType.COMMENT_REPLY,
- message,
- title,
- it.mediaId,
- it.commentId,
- names[it.mediaId]?.color ?: "#222222",
- names[it.mediaId]?.coverImage ?: ""
- )
- }
-
- NotificationType.APP_GLOBAL -> {
- val title = "Update from Dantotsu"
- val message = it.content ?: "New feature available"
-
- val commentStore = CommentStore(
- title,
- message,
- null,
- null
- )
- addNotificationToStore(commentStore)
-
- createNotification(
- NotificationType.APP_GLOBAL,
- message,
- title,
- 0,
- 0,
- "",
- ""
- )
- }
-
- NotificationType.NO_NOTIFICATION -> {
- PrefManager.removeCustomVal("genre_thumb")
- PrefManager.removeCustomVal("banner_ANIME_time")
- PrefManager.removeCustomVal("banner_MANGA_time")
- PrefManager.setVal(PrefName.ImageUrl, it.content ?: "")
- null
- }
-
- NotificationType.UNKNOWN -> {
- null
- }
- }
-
- if (ActivityCompat.checkSelfPermission(
- applicationContext,
- Manifest.permission.POST_NOTIFICATIONS
- ) == PackageManager.PERMISSION_GRANTED
- ) {
- if (notification != null) {
- NotificationManagerCompat.from(applicationContext)
- .notify(
- type.id,
- System.currentTimeMillis().toInt(),
- notification
- )
- }
- }
- }
- }
- return Result.success()
- }
-
- private fun addNotificationToStore(notification: CommentStore) {
- val notificationStore = PrefManager.getNullableVal>(
- PrefName.CommentNotificationStore,
- null
- ) ?: listOf()
- val newStore = notificationStore.toMutableList()
- if (newStore.size > 10) {
- newStore.remove(newStore.minByOrNull { it.time })
- }
- if (newStore.any { it.content == notification.content }) {
- return
- }
- newStore.add(notification)
- PrefManager.setVal(PrefName.CommentNotificationStore, newStore)
- }
-
- private fun createNotification(
- notificationType: NotificationType,
- message: String,
- title: String,
- mediaId: Int,
- commentId: Int,
- color: String,
- imageUrl: String
- ): android.app.Notification? {
- Logger.log(
- "Creating notification of type $notificationType" +
- ", message: $message, title: $title, mediaId: $mediaId, commentId: $commentId"
- )
- val notification = when (notificationType) {
- NotificationType.COMMENT_WARNING -> {
- val intent = Intent(applicationContext, MainActivity::class.java).apply {
- putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
- putExtra("mediaId", mediaId)
- putExtra("commentId", commentId)
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
- }
- val pendingIntent = PendingIntent.getActivity(
- applicationContext,
- commentId,
- intent,
- PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- )
- val builder = NotificationCompat.Builder(applicationContext, notificationType.id)
- .setContentTitle(title)
- .setContentText(message)
- .setSmallIcon(R.drawable.notification_icon)
- .setPriority(NotificationCompat.PRIORITY_HIGH)
- .setContentIntent(pendingIntent)
- .setAutoCancel(true)
- builder.build()
- }
-
- NotificationType.COMMENT_REPLY -> {
- val intent = Intent(applicationContext, MainActivity::class.java).apply {
- putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
- putExtra("mediaId", mediaId)
- putExtra("commentId", commentId)
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
- }
- val pendingIntent = PendingIntent.getActivity(
- applicationContext,
- commentId,
- intent,
- PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- )
- val builder = NotificationCompat.Builder(applicationContext, notificationType.id)
- .setContentTitle(title)
- .setContentText(message)
- .setSmallIcon(R.drawable.notification_icon)
- .setPriority(NotificationCompat.PRIORITY_DEFAULT)
- .setContentIntent(pendingIntent)
- .setAutoCancel(true)
- if (imageUrl.isNotEmpty()) {
- val bitmap = getBitmapFromUrl(imageUrl)
- if (bitmap != null) {
- builder.setLargeIcon(bitmap)
- }
- }
- if (color.isNotEmpty()) {
- builder.color = Color.parseColor(color)
- }
- builder.build()
- }
-
- NotificationType.APP_GLOBAL -> {
- val intent = Intent(applicationContext, MainActivity::class.java).apply {
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
- }
- val pendingIntent = PendingIntent.getActivity(
- applicationContext,
- System.currentTimeMillis().toInt(),
- intent,
- PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- )
- val builder = NotificationCompat.Builder(applicationContext, notificationType.id)
- .setContentTitle(title)
- .setContentText(message)
- .setSmallIcon(R.drawable.notification_icon)
- .setPriority(NotificationCompat.PRIORITY_HIGH)
- .setContentIntent(pendingIntent)
- .setAutoCancel(true)
- builder.build()
- }
-
- else -> {
- null
- }
- }
- return notification
- }
-
- private fun getBitmapFromVectorDrawable(context: Context, drawableId: Int): Bitmap? {
- val drawable = ContextCompat.getDrawable(context, drawableId) ?: return null
- val bitmap = Bitmap.createBitmap(
- drawable.intrinsicWidth,
- drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
- )
- val canvas = Canvas(bitmap)
- drawable.setBounds(0, 0, canvas.width, canvas.height)
- drawable.draw(canvas)
- return bitmap
- }
-
- private fun getBitmapFromUrl(url: String): Bitmap? {
- return try {
- val inputStream = java.net.URL(url).openStream()
- BitmapFactory.decodeStream(inputStream)
- } catch (e: Exception) {
- null
+ CoroutineWorker(appContext, workerParams) {
+ override suspend fun doWork(): Result {
+ Logger.log("CommentNotificationWorker: doWork")
+ return if (CommentNotificationTask().execute(applicationContext)) {
+ Result.success()
+ } else {
+ Result.retry()
}
}
diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt
index 907ba068..80c99b4a 100644
--- a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt
@@ -1,9 +1,13 @@
package ani.dantotsu.settings
import android.annotation.SuppressLint
+import android.app.AlarmManager
import android.app.AlertDialog
+import android.content.Context
import android.content.Intent
import android.graphics.drawable.Animatable
+import android.net.Uri
+import android.os.Build
import android.os.Build.BRAND
import android.os.Build.DEVICE
import android.os.Build.SUPPORTED_ABIS
@@ -47,6 +51,7 @@ import ani.dantotsu.initActivity
import ani.dantotsu.loadImage
import ani.dantotsu.util.Logger
import ani.dantotsu.navBarHeight
+import ani.dantotsu.notifications.TaskScheduler
import ani.dantotsu.notifications.comment.CommentNotificationWorker
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
import ani.dantotsu.openLinkInBrowser
@@ -764,6 +769,40 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
openSettings(this, null)
}
+ binding.settingsNotificationsUseAlarmManager.isChecked =
+ PrefManager.getVal(PrefName.UseAlarmManager)
+
+ binding.settingsNotificationsUseAlarmManager.setOnCheckedChangeListener { _, isChecked ->
+ if (isChecked) {
+ val alertDialog = AlertDialog.Builder(this, R.style.MyPopup)
+ .setTitle("Use Alarm Manager")
+ .setMessage("Using Alarm Manger can help fight against battery optimization, but may consume more battery. It also requires the Alarm Manager permission.")
+ .setPositiveButton("Use") { dialog, _ ->
+ PrefManager.setVal(PrefName.UseAlarmManager, true)
+ if (SDK_INT >= Build.VERSION_CODES.S) {
+ if (!(getSystemService(Context.ALARM_SERVICE) as AlarmManager).canScheduleExactAlarms()) {
+ val intent = Intent("android.settings.REQUEST_SCHEDULE_EXACT_ALARM")
+ startActivity(intent)
+ binding.settingsNotificationsCheckingSubscriptions.isChecked = true
+ }
+ }
+ dialog.dismiss()
+ }
+ .setNegativeButton("Cancel") { dialog, _ ->
+ binding.settingsNotificationsCheckingSubscriptions.isChecked = false
+ PrefManager.setVal(PrefName.UseAlarmManager, false)
+ dialog.dismiss()
+ }
+ .create()
+ alertDialog.window?.setDimAmount(0.8f)
+ alertDialog.show()
+ } else {
+ PrefManager.setVal(PrefName.UseAlarmManager, false)
+ TaskScheduler.create(this, true).cancelAllTasks()
+ TaskScheduler.create(this, false).scheduleAllTasks(this)
+ }
+ }
+
if (!BuildConfig.FLAVOR.contains("fdroid")) {
binding.settingsLogo.setOnLongClickListener {
lifecycleScope.launch(Dispatchers.IO) {
diff --git a/app/src/main/java/ani/dantotsu/settings/saving/PrefManager.kt b/app/src/main/java/ani/dantotsu/settings/saving/PrefManager.kt
index d277b011..3a13cb88 100644
--- a/app/src/main/java/ani/dantotsu/settings/saving/PrefManager.kt
+++ b/app/src/main/java/ani/dantotsu/settings/saving/PrefManager.kt
@@ -24,6 +24,7 @@ object PrefManager {
private var protectedPreferences: SharedPreferences? = null
fun init(context: Context) { //must be called in Application class or will crash
+ if (generalPreferences != null) return
generalPreferences =
context.getSharedPreferences(Location.General.location, Context.MODE_PRIVATE)
uiPreferences =
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 9b2beb3c..13486d4d 100644
--- a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt
+++ b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt
@@ -38,6 +38,7 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
AnilistNotificationInterval(Pref(Location.General, Int::class, 3)),
LastAnilistNotificationId(Pref(Location.General, Int::class, 0)),
AnilistFilteredTypes(Pref(Location.General, Set::class, setOf())),
+ UseAlarmManager(Pref(Location.General, Boolean::class, false)),
//User Interface
UseOLED(Pref(Location.UI, Boolean::class, false)),
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index 42fe4cf0..2a1eabbc 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -1469,6 +1469,24 @@
app:showText="false"
app:thumbTint="@color/button_switch_track" />
+
+
Show notification for Checking Subscriptions
+ Use Alarm Manager for reliable Notifications
Notification for Checking Subscriptions
Subscriptions Update Frequency : %1$s
Subscriptions Update Frequency