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.isGlobal() || it.notificationId > recentGlobal } ?.toMutableList() val newRecentGlobal = notifications?.filter { it.type.isGlobal() }?.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) ) 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 } @Suppress("unused") 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 } } private fun Int?.isGlobal() = this == 3 || this == 420 }