diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt b/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt index 2a61414e3..817abfd6b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt @@ -20,7 +20,7 @@ class CaptchaNotifier( ) : EventListener { fun notify(exception: CloudFlareProtectedException) { - if (!context.checkNotificationPermission()) { + if (!context.checkNotificationPermission(CHANNEL_ID)) { return } val manager = NotificationManagerCompat.from(context) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Android.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Android.kt index ddc3d3613..2e4a59062 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Android.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Android.kt @@ -14,7 +14,7 @@ import android.content.ContextWrapper import android.content.OperationApplicationException import android.content.SharedPreferences import android.content.SyncResult -import android.content.pm.PackageManager +import android.content.pm.PackageManager.PERMISSION_GRANTED import android.content.pm.ResolveInfo import android.database.SQLException import android.graphics.Bitmap @@ -216,10 +216,19 @@ fun Context.findActivity(): Activity? = when (this) { else -> null } -fun Context.checkNotificationPermission(): Boolean = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED -} else { - NotificationManagerCompat.from(this).areNotificationsEnabled() +fun Context.checkNotificationPermission(channelId: String?): Boolean { + val hasPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PERMISSION_GRANTED + } else { + NotificationManagerCompat.from(this).areNotificationsEnabled() + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && hasPermission && channelId != null) { + val channel = NotificationManagerCompat.from(this).getNotificationChannel(channelId) + if (channel != null && channel.importance == NotificationManagerCompat.IMPORTANCE_NONE) { + return false + } + } + return hasPermission } @WorkerThread diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportWorker.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportWorker.kt index 831f4514f..2841bbe02 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportWorker.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportWorker.kt @@ -50,7 +50,7 @@ class ImportWorker @AssistedInject constructor( val result = runCatchingCancellable { importer.import(uri).manga } - if (applicationContext.checkNotificationPermission()) { + if (applicationContext.checkNotificationPermission(CHANNEL_ID)) { val notification = buildNotification(result) notificationManager.notify(uri.hashCode(), notification) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SuggestionsSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SuggestionsSettingsFragment.kt index ed6f8ed14..93fd45511 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SuggestionsSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SuggestionsSettingsFragment.kt @@ -4,6 +4,7 @@ import android.content.SharedPreferences import android.os.Bundle import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.AppSettings @@ -15,8 +16,7 @@ import org.koitharu.kotatsu.suggestions.ui.SuggestionsWorker import javax.inject.Inject @AndroidEntryPoint -class SuggestionsSettingsFragment : - BasePreferenceFragment(R.string.suggestions), +class SuggestionsSettingsFragment : BasePreferenceFragment(R.string.suggestions), SharedPreferences.OnSharedPreferenceChangeListener { @Inject @@ -48,16 +48,17 @@ class SuggestionsSettingsFragment : } override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { - if (key == AppSettings.KEY_SUGGESTIONS && settings.isSuggestionsEnabled) { - onSuggestionsEnabled() + if (settings.isSuggestionsEnabled && (key == AppSettings.KEY_SUGGESTIONS + || key == AppSettings.KEY_SUGGESTIONS_EXCLUDE_TAGS + || key == AppSettings.KEY_SUGGESTIONS_EXCLUDE_NSFW) + ) { + updateSuggestions() } } - private fun onSuggestionsEnabled() { - lifecycleScope.launch { - if (repository.isEmpty()) { - suggestionsScheduler.startNow() - } + private fun updateSuggestions() { + lifecycleScope.launch(Dispatchers.Default) { + suggestionsScheduler.startNow() } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt index 2dcde8897..a87d532f7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt @@ -64,6 +64,8 @@ class SuggestionsViewModel @Inject constructor( override fun onRetry() = Unit fun updateSuggestions() { - suggestionsScheduler.startNow() + launchJob(Dispatchers.Default) { + suggestionsScheduler.startNow() + } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt index f9a88d34b..f541a5fd2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt @@ -1,11 +1,12 @@ package org.koitharu.kotatsu.suggestions.ui -import android.annotation.SuppressLint +import android.Manifest import android.app.PendingIntent import android.content.Context import android.content.pm.ServiceInfo import android.os.Build import androidx.annotation.FloatRange +import androidx.annotation.RequiresPermission import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat @@ -50,6 +51,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.ext.almostEquals import org.koitharu.kotatsu.core.util.ext.asArrayList import org.koitharu.kotatsu.core.util.ext.awaitUniqueWorkInfoByName +import org.koitharu.kotatsu.core.util.ext.awaitWorkInfosByTag import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission import org.koitharu.kotatsu.core.util.ext.flatten import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug @@ -189,7 +191,9 @@ class SuggestionsWorker @AssistedInject constructor( .sortedBy { it.relevance } .take(MAX_RESULTS) suggestionRepository.replace(suggestions) - if (appSettings.isSuggestionsNotificationAvailable && applicationContext.checkNotificationPermission()) { + if (appSettings.isSuggestionsNotificationAvailable + && applicationContext.checkNotificationPermission(MANGA_CHANNEL_ID) + ) { for (i in 0..3) { try { val manga = suggestions[Random.nextInt(0, suggestions.size / 3)] @@ -252,7 +256,7 @@ class SuggestionsWorker @AssistedInject constructor( e.printStackTraceDebug() }.getOrDefault(emptyList()) - @SuppressLint("MissingPermission") + @RequiresPermission(Manifest.permission.POST_NOTIFICATIONS) private suspend fun showNotification(manga: Manga) { val channel = NotificationChannelCompat.Builder(MANGA_CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_DEFAULT) .setName(applicationContext.getString(R.string.suggestions)) @@ -393,7 +397,10 @@ class SuggestionsWorker @AssistedInject constructor( .any { !it.state.isFinished } } - fun startNow() { + suspend fun startNow() { + if (workManager.awaitWorkInfosByTag(TAG_ONESHOT).any { !it.state.isFinished }) { + return + } val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() @@ -402,7 +409,7 @@ class SuggestionsWorker @AssistedInject constructor( .addTag(TAG_ONESHOT) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .build() - workManager.enqueue(request) + workManager.enqueue(request).await() } private fun createConstraints() = Constraints.Builder() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackWorker.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackWorker.kt index 41dccf6c4..4cce1ea8e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackWorker.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackWorker.kt @@ -168,7 +168,7 @@ class TrackWorker @AssistedInject constructor( } } }.onEachIndexed { index, it -> - if (applicationContext.checkNotificationPermission()) { + if (applicationContext.checkNotificationPermission(WORKER_CHANNEL_ID)) { notificationManager.notify(WORKER_NOTIFICATION_ID, createWorkerNotification(tracks.size, index + 1)) } when (it) { @@ -197,7 +197,7 @@ class TrackWorker @AssistedInject constructor( channelId: String?, newChapters: List, ) { - if (newChapters.isEmpty() || channelId == null || !applicationContext.checkNotificationPermission()) { + if (newChapters.isEmpty() || channelId == null || !applicationContext.checkNotificationPermission(channelId)) { return } val id = manga.url.hashCode()