diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 590a97816..79790a1be 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -40,8 +40,8 @@ import eu.kanade.tachiyomi.util.system.WebViewUtil import eu.kanade.tachiyomi.util.system.animatorDurationScale import eu.kanade.tachiyomi.util.system.isPreviewBuildType import eu.kanade.tachiyomi.util.system.isReleaseBuildType -import eu.kanade.tachiyomi.util.system.notification import eu.kanade.tachiyomi.util.system.notificationManager +import eu.kanade.tachiyomi.util.system.notify import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -97,7 +97,10 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { .onEach { enabled -> if (enabled) { disableIncognitoReceiver.register() - val notification = notification(Notifications.CHANNEL_INCOGNITO_MODE) { + notify( + Notifications.ID_INCOGNITO_MODE, + Notifications.CHANNEL_INCOGNITO_MODE, + ) { setContentTitle(getString(R.string.pref_incognito_mode)) setContentText(getString(R.string.notification_incognito_text)) setSmallIcon(R.drawable.ic_glasses_24dp) @@ -111,7 +114,6 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { ) setContentIntent(pendingIntent) } - notificationManager.notify(Notifications.ID_INCOGNITO_MODE, notification) } else { disableIncognitoReceiver.unregister() notificationManager.cancel(Notifications.ID_INCOGNITO_MODE) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadService.kt index b66dabc08..a79295ab9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadService.kt @@ -14,7 +14,7 @@ import eu.kanade.tachiyomi.util.system.acquireWakeLock import eu.kanade.tachiyomi.util.system.isConnectedToWifi import eu.kanade.tachiyomi.util.system.isOnline import eu.kanade.tachiyomi.util.system.isServiceRunning -import eu.kanade.tachiyomi.util.system.notification +import eu.kanade.tachiyomi.util.system.notificationBuilder import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -143,8 +143,8 @@ class DownloadService : Service() { } private fun getPlaceholderNotification(): Notification { - return notification(Notifications.CHANNEL_DOWNLOADER_PROGRESS) { + return notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) { setContentTitle(getString(R.string.download_notifier_downloader_title)) - } + }.build() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index f848b1aba..92930c063 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -20,9 +20,9 @@ import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.util.lang.chop -import eu.kanade.tachiyomi.util.system.notification import eu.kanade.tachiyomi.util.system.notificationBuilder import eu.kanade.tachiyomi.util.system.notificationManager +import eu.kanade.tachiyomi.util.system.notify import tachiyomi.core.Constants import tachiyomi.core.util.lang.launchUI import tachiyomi.domain.chapter.model.Chapter @@ -91,18 +91,16 @@ class LibraryUpdateNotifier(private val context: Context) { } fun showQueueSizeWarningNotification() { - val notificationBuilder = context.notificationBuilder(Notifications.CHANNEL_LIBRARY_PROGRESS) { + context.notify( + Notifications.ID_LIBRARY_SIZE_WARNING, + Notifications.CHANNEL_LIBRARY_PROGRESS, + ) { setContentTitle(context.getString(R.string.label_warning)) setStyle(NotificationCompat.BigTextStyle().bigText(context.getString(R.string.notification_size_warning))) setSmallIcon(R.drawable.ic_warning_white_24dp) setTimeoutAfter(Downloader.WARNING_NOTIF_TIMEOUT_MS) setContentIntent(NotificationHandler.openUrl(context, HELP_WARNING_URL)) } - - context.notificationManager.notify( - Notifications.ID_LIBRARY_SIZE_WARNING, - notificationBuilder.build(), - ) } /** @@ -116,17 +114,16 @@ class LibraryUpdateNotifier(private val context: Context) { return } - context.notificationManager.notify( + context.notify( Notifications.ID_LIBRARY_ERROR, - context.notificationBuilder(Notifications.CHANNEL_LIBRARY_ERROR) { - setContentTitle(context.resources.getString(R.string.notification_update_error, failed)) - setContentText(context.getString(R.string.action_show_errors)) - setSmallIcon(R.drawable.ic_tachi) + Notifications.CHANNEL_LIBRARY_ERROR, + ) { + setContentTitle(context.resources.getString(R.string.notification_update_error, failed)) + setContentText(context.getString(R.string.action_show_errors)) + setSmallIcon(R.drawable.ic_tachi) - setContentIntent(NotificationReceiver.openErrorLogPendingActivity(context, uri)) - } - .build(), - ) + setContentIntent(NotificationReceiver.openErrorLogPendingActivity(context, uri)) + } } /** @@ -139,16 +136,15 @@ class LibraryUpdateNotifier(private val context: Context) { return } - context.notificationManager.notify( + context.notify( Notifications.ID_LIBRARY_SKIPPED, - context.notificationBuilder(Notifications.CHANNEL_LIBRARY_SKIPPED) { - setContentTitle(context.resources.getString(R.string.notification_update_skipped, skipped)) - setContentText(context.getString(R.string.learn_more)) - setSmallIcon(R.drawable.ic_tachi) - setContentIntent(NotificationHandler.openUrl(context, HELP_SKIPPED_URL)) - } - .build(), - ) + Notifications.CHANNEL_LIBRARY_SKIPPED, + ) { + setContentTitle(context.resources.getString(R.string.notification_update_skipped, skipped)) + setContentText(context.getString(R.string.learn_more)) + setSmallIcon(R.drawable.ic_tachi) + setContentIntent(NotificationHandler.openUrl(context, HELP_SKIPPED_URL)) + } } /** @@ -158,38 +154,38 @@ class LibraryUpdateNotifier(private val context: Context) { */ fun showUpdateNotifications(updates: List>>) { // Parent group notification - context.notificationManager.notify( + context.notify( Notifications.ID_NEW_CHAPTERS, - context.notification(Notifications.CHANNEL_NEW_CHAPTERS) { - setContentTitle(context.getString(R.string.notification_new_chapters)) - if (updates.size == 1 && !preferences.hideNotificationContent().get()) { - setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN)) - } else { - setContentText(context.resources.getQuantityString(R.plurals.notification_new_chapters_summary, updates.size, updates.size)) + Notifications.CHANNEL_NEW_CHAPTERS, + ) { + setContentTitle(context.getString(R.string.notification_new_chapters)) + if (updates.size == 1 && !preferences.hideNotificationContent().get()) { + setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN)) + } else { + setContentText(context.resources.getQuantityString(R.plurals.notification_new_chapters_summary, updates.size, updates.size)) - if (!preferences.hideNotificationContent().get()) { - setStyle( - NotificationCompat.BigTextStyle().bigText( - updates.joinToString("\n") { - it.first.title.chop(NOTIF_TITLE_MAX_LEN) - }, - ), - ) - } + if (!preferences.hideNotificationContent().get()) { + setStyle( + NotificationCompat.BigTextStyle().bigText( + updates.joinToString("\n") { + it.first.title.chop(NOTIF_TITLE_MAX_LEN) + }, + ), + ) } + } - setSmallIcon(R.drawable.ic_tachi) - setLargeIcon(notificationBitmap) + setSmallIcon(R.drawable.ic_tachi) + setLargeIcon(notificationBitmap) - setGroup(Notifications.GROUP_NEW_CHAPTERS) - setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY) - setGroupSummary(true) - priority = NotificationCompat.PRIORITY_HIGH + setGroup(Notifications.GROUP_NEW_CHAPTERS) + setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY) + setGroupSummary(true) + priority = NotificationCompat.PRIORITY_HIGH - setContentIntent(getNotificationIntent()) - setAutoCancel(true) - }, - ) + setContentIntent(getNotificationIntent()) + setAutoCancel(true) + } // Per-manga notification if (!preferences.hideNotificationContent().get()) { @@ -203,7 +199,7 @@ class LibraryUpdateNotifier(private val context: Context) { private suspend fun createNewChaptersNotification(manga: Manga, chapters: Array): Notification { val icon = getMangaIcon(manga) - return context.notification(Notifications.CHANNEL_NEW_CHAPTERS) { + return context.notificationBuilder(Notifications.CHANNEL_NEW_CHAPTERS) { setContentTitle(manga.title) val description = getNewChaptersDescription(chapters) @@ -259,7 +255,7 @@ class LibraryUpdateNotifier(private val context: Context) { ), ) } - } + }.build() } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt index ad8a74761..f891607f6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt @@ -5,29 +5,28 @@ import androidx.core.app.NotificationCompat import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications -import eu.kanade.tachiyomi.util.system.notification -import eu.kanade.tachiyomi.util.system.notificationManager +import eu.kanade.tachiyomi.util.system.notify class ExtensionUpdateNotifier(private val context: Context) { fun promptUpdates(names: List) { - context.notificationManager.notify( + context.notify( Notifications.ID_UPDATES_TO_EXTS, - context.notification(Notifications.CHANNEL_EXTENSIONS_UPDATE) { - setContentTitle( - context.resources.getQuantityString( - R.plurals.update_check_notification_ext_updates, - names.size, - names.size, - ), - ) - val extNames = names.joinToString(", ") - setContentText(extNames) - setStyle(NotificationCompat.BigTextStyle().bigText(extNames)) - setSmallIcon(R.drawable.ic_extension_24dp) - setContentIntent(NotificationReceiver.openExtensionsPendingActivity(context)) - setAutoCancel(true) - }, - ) + Notifications.CHANNEL_EXTENSIONS_UPDATE, + ) { + setContentTitle( + context.resources.getQuantityString( + R.plurals.update_check_notification_ext_updates, + names.size, + names.size, + ), + ) + val extNames = names.joinToString(", ") + setContentText(extNames) + setStyle(NotificationCompat.BigTextStyle().bigText(extNames)) + setSmallIcon(R.drawable.ic_extension_24dp) + setContentIntent(NotificationReceiver.openExtensionsPendingActivity(context)) + setAutoCancel(true) + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt index 9d1bf13ae..83c641bbc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt @@ -9,18 +9,14 @@ import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.system.createFileInCacheDir -import eu.kanade.tachiyomi.util.system.notificationBuilder import eu.kanade.tachiyomi.util.system.notificationManager +import eu.kanade.tachiyomi.util.system.notify import eu.kanade.tachiyomi.util.system.toast import tachiyomi.core.util.lang.withNonCancellableContext import tachiyomi.core.util.lang.withUIContext class CrashLogUtil(private val context: Context) { - private val notificationBuilder = context.notificationBuilder(Notifications.CHANNEL_CRASH_LOGS) { - setSmallIcon(R.drawable.ic_tachi) - } - suspend fun dumpLogs() = withNonCancellableContext { try { val file = context.createFileInCacheDir("tachiyomi_crash_logs.txt") @@ -49,8 +45,12 @@ class CrashLogUtil(private val context: Context) { private fun showNotification(uri: Uri) { context.notificationManager.cancel(Notifications.ID_CRASH_LOGS) - with(notificationBuilder) { + context.notify( + Notifications.ID_CRASH_LOGS, + Notifications.CHANNEL_CRASH_LOGS, + ) { setContentTitle(context.getString(R.string.crash_log_saved)) + setSmallIcon(R.drawable.ic_tachi) clearActions() addAction( @@ -63,8 +63,6 @@ class CrashLogUtil(private val context: Context) { context.getString(R.string.action_share), NotificationReceiver.shareCrashLogPendingBroadcast(context, uri, Notifications.ID_CRASH_LOGS), ) - - context.notificationManager.notify(Notifications.ID_CRASH_LOGS, build()) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt index 318b9175d..e90c9cf3e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt @@ -25,7 +25,7 @@ import android.view.WindowManager import androidx.annotation.AttrRes import androidx.annotation.ColorInt import androidx.appcompat.view.ContextThemeWrapper -import androidx.core.content.ContextCompat +import androidx.core.content.PermissionChecker import androidx.core.content.getSystemService import androidx.core.graphics.alpha import androidx.core.graphics.blue @@ -78,7 +78,7 @@ fun Context.copyToClipboard(label: String, content: String) { * @param permission the permission to check. * @return true if it has permissions. */ -fun Context.hasPermission(permission: String) = ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED +fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED /** * Returns the color for the given attribute. diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt index 19ef75a8b..c4e207c83 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt @@ -1,12 +1,39 @@ package eu.kanade.tachiyomi.util.system -import android.app.Notification +import android.Manifest import android.content.Context import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationChannelGroupCompat import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.core.content.PermissionChecker import eu.kanade.tachiyomi.R +fun Context.notify(id: Int, channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null) { + if (PermissionChecker.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PermissionChecker.PERMISSION_GRANTED) { + return + } + + val notification = notificationBuilder(channelId, block).build() + NotificationManagerCompat.from(this).notify(id, notification) +} + +/** + * Helper method to create a notification builder. + * + * @param id the channel id. + * @param block the function that will execute inside the builder. + * @return a notification to be displayed or updated. + */ +fun Context.notificationBuilder(channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null): NotificationCompat.Builder { + val builder = NotificationCompat.Builder(this, channelId) + .setColor(getColor(R.color.accent_blue)) + if (block != null) { + builder.block() + } + return builder +} + /** * Helper method to build a notification channel group. * @@ -40,31 +67,3 @@ fun buildNotificationChannel( builder.block() return builder.build() } - -/** - * Helper method to create a notification builder. - * - * @param id the channel id. - * @param block the function that will execute inside the builder. - * @return a notification to be displayed or updated. - */ -fun Context.notificationBuilder(channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null): NotificationCompat.Builder { - val builder = NotificationCompat.Builder(this, channelId) - .setColor(getColor(R.color.accent_blue)) - if (block != null) { - builder.block() - } - return builder -} - -/** - * Helper method to create a notification. - * - * @param id the channel id. - * @param block the function that will execute inside the builder. - * @return a notification to be displayed or updated. - */ -fun Context.notification(channelId: String, block: (NotificationCompat.Builder.() -> Unit)?): Notification { - val builder = notificationBuilder(channelId, block) - return builder.build() -}