This commit is contained in:
@@ -160,6 +160,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
val isTrackerNsfwDisabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_TRACKER_NO_NSFW, false)
|
||||
|
||||
val trackerDownloadStrategy: TrackerDownloadStrategy
|
||||
get() = prefs.getEnumValue(KEY_TRACKER_DOWNLOAD, TrackerDownloadStrategy.DISABLED)
|
||||
|
||||
var notificationSound: Uri
|
||||
get() = prefs.getString(KEY_NOTIFICATIONS_SOUND, null)?.toUriOrNull()
|
||||
?: Settings.System.DEFAULT_NOTIFICATION_URI
|
||||
@@ -600,6 +603,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
const val KEY_TRACK_WARNING = "track_warning"
|
||||
const val KEY_TRACKER_NOTIFICATIONS = "tracker_notifications"
|
||||
const val KEY_TRACKER_NO_NSFW = "tracker_no_nsfw"
|
||||
const val KEY_TRACKER_DOWNLOAD = "tracker_download"
|
||||
const val KEY_NOTIFICATIONS_SETTINGS = "notifications_settings"
|
||||
const val KEY_NOTIFICATIONS_SOUND = "notifications_sound"
|
||||
const val KEY_NOTIFICATIONS_VIBRATE = "notifications_vibrate"
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.annotation.StyleRes
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.parsers.util.find
|
||||
|
||||
@Keep
|
||||
enum class ColorScheme(
|
||||
@StyleRes val styleResId: Int,
|
||||
@StringRes val titleResId: Int,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
enum class DownloadFormat {
|
||||
|
||||
AUTOMATIC,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
enum class ListMode {
|
||||
|
||||
LIST, DETAILED_LIST, GRID;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.annotation.Keep
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
@Keep
|
||||
enum class NavItem(
|
||||
@IdRes val id: Int,
|
||||
@StringRes val title: Int,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
import android.net.ConnectivityManager
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
enum class NetworkPolicy(
|
||||
private val key: Int,
|
||||
) {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
enum class ProgressIndicatorMode {
|
||||
|
||||
NONE, PERCENT_READ, PERCENT_LEFT, CHAPTERS_READ, CHAPTERS_LEFT;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
enum class ReaderAnimation {
|
||||
|
||||
// Do not rename this
|
||||
|
||||
@@ -2,11 +2,13 @@ package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ContextThemeWrapper
|
||||
import androidx.annotation.Keep
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeDrawable
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
@Keep
|
||||
enum class ReaderBackground {
|
||||
|
||||
DEFAULT, LIGHT, DARK, WHITE, BLACK;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
enum class ReaderMode(val id: Int) {
|
||||
|
||||
STANDARD(1),
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
enum class ScreenshotsPolicy {
|
||||
|
||||
// Do not rename this
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
@Keep
|
||||
enum class SearchSuggestionType(
|
||||
@StringRes val titleResId: Int,
|
||||
) {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
enum class TrackerDownloadStrategy {
|
||||
|
||||
DISABLED, DOWNLOADED;
|
||||
}
|
||||
@@ -166,8 +166,9 @@ abstract class ChaptersPagesViewModel(
|
||||
fun download(chaptersIds: Set<Long>?) {
|
||||
launchJob(Dispatchers.Default) {
|
||||
downloadScheduler.schedule(
|
||||
requireManga(),
|
||||
chaptersIds,
|
||||
manga = requireManga(),
|
||||
chaptersIds = chaptersIds,
|
||||
isSilent = false,
|
||||
)
|
||||
onDownloadStarted.call(Unit)
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ import org.koitharu.kotatsu.search.ui.MangaListActivity
|
||||
import java.util.UUID
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
private const val CHANNEL_ID = "download"
|
||||
private const val CHANNEL_ID_DEFAULT = "download"
|
||||
private const val CHANNEL_ID_SILENT = "download_bg"
|
||||
private const val GROUP_ID = "downloads"
|
||||
|
||||
class DownloadNotificationFactory @AssistedInject constructor(
|
||||
@@ -45,10 +46,11 @@ class DownloadNotificationFactory @AssistedInject constructor(
|
||||
private val workManager: WorkManager,
|
||||
private val coil: ImageLoader,
|
||||
@Assisted private val uuid: UUID,
|
||||
@Assisted val isSilent: Boolean,
|
||||
) {
|
||||
|
||||
private val covers = HashMap<Manga, Drawable>()
|
||||
private val builder = NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
private val builder = NotificationCompat.Builder(context, if (isSilent) CHANNEL_ID_SILENT else CHANNEL_ID_DEFAULT)
|
||||
private val mutex = Mutex()
|
||||
|
||||
private val coverWidth = context.resources.getDimensionPixelSize(
|
||||
@@ -106,14 +108,18 @@ class DownloadNotificationFactory @AssistedInject constructor(
|
||||
}
|
||||
|
||||
init {
|
||||
createChannel()
|
||||
createChannels()
|
||||
builder.setOnlyAlertOnce(true)
|
||||
builder.setDefaults(0)
|
||||
builder.foregroundServiceBehavior = NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE
|
||||
builder.foregroundServiceBehavior = if (isSilent) {
|
||||
NotificationCompat.FOREGROUND_SERVICE_DEFERRED
|
||||
} else {
|
||||
NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE
|
||||
}
|
||||
builder.setSilent(true)
|
||||
builder.setGroup(GROUP_ID)
|
||||
builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
||||
builder.priority = NotificationCompat.PRIORITY_DEFAULT
|
||||
builder.priority = if (isSilent) NotificationCompat.PRIORITY_MIN else NotificationCompat.PRIORITY_DEFAULT
|
||||
}
|
||||
|
||||
suspend fun create(state: DownloadState?): Notification = mutex.withLock {
|
||||
@@ -283,20 +289,30 @@ class DownloadNotificationFactory @AssistedInject constructor(
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
private fun createChannel() {
|
||||
private fun createChannels() {
|
||||
val manager = NotificationManagerCompat.from(context)
|
||||
val channel = NotificationChannelCompat.Builder(CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_LOW)
|
||||
.setName(context.getString(R.string.downloads))
|
||||
.setVibrationEnabled(false)
|
||||
.setLightsEnabled(false)
|
||||
.setSound(null, null)
|
||||
.build()
|
||||
manager.createNotificationChannel(channel)
|
||||
manager.createNotificationChannel(
|
||||
NotificationChannelCompat.Builder(CHANNEL_ID_DEFAULT, NotificationManagerCompat.IMPORTANCE_LOW)
|
||||
.setName(context.getString(R.string.downloads))
|
||||
.setVibrationEnabled(false)
|
||||
.setLightsEnabled(false)
|
||||
.setSound(null, null)
|
||||
.build(),
|
||||
)
|
||||
manager.createNotificationChannel(
|
||||
NotificationChannelCompat.Builder(CHANNEL_ID_SILENT, NotificationManagerCompat.IMPORTANCE_MIN)
|
||||
.setName(context.getString(R.string.downloads_background))
|
||||
.setVibrationEnabled(false)
|
||||
.setLightsEnabled(false)
|
||||
.setSound(null, null)
|
||||
.setShowBadge(false)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
|
||||
fun create(uuid: UUID): DownloadNotificationFactory
|
||||
fun create(uuid: UUID, isSilent: Boolean): DownloadNotificationFactory
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,10 @@ class DownloadWorker @AssistedInject constructor(
|
||||
notificationFactoryFactory: DownloadNotificationFactory.Factory,
|
||||
) : CoroutineWorker(appContext, params) {
|
||||
|
||||
private val notificationFactory = notificationFactoryFactory.create(params.id)
|
||||
private val notificationFactory = notificationFactoryFactory.create(
|
||||
uuid = params.id,
|
||||
isSilent = params.inputData.getBoolean(IS_SILENT, false),
|
||||
)
|
||||
private val notificationManager = appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
private val slowdownDispatcher = DownloadSlowdownDispatcher(mangaRepositoryFactory, SLOWDOWN_DELAY)
|
||||
|
||||
@@ -120,8 +123,7 @@ class DownloadWorker @AssistedInject constructor(
|
||||
setForeground(getForegroundInfo())
|
||||
val mangaId = inputData.getLong(MANGA_ID, 0L)
|
||||
val manga = mangaDataRepository.findMangaById(mangaId) ?: return Result.failure()
|
||||
lastPublishedState = DownloadState(manga, isIndeterminate = true)
|
||||
publishState(DownloadState(manga, isIndeterminate = true))
|
||||
publishState(DownloadState(manga = manga, isIndeterminate = true).also { lastPublishedState = it })
|
||||
val chaptersIds = inputData.getLongArray(CHAPTERS_IDS)?.takeUnless { it.isEmpty() }
|
||||
val downloadedIds = getDoneChapters(manga)
|
||||
return try {
|
||||
@@ -380,7 +382,9 @@ class DownloadWorker @AssistedInject constructor(
|
||||
}
|
||||
val notification = notificationFactory.create(state)
|
||||
if (state.isFinalState) {
|
||||
notificationManager.notify(id.toString(), id.hashCode(), notification)
|
||||
if (!notificationFactory.isSilent) {
|
||||
notificationManager.notify(id.toString(), id.hashCode(), notification)
|
||||
}
|
||||
} else if (notificationThrottler.throttle()) {
|
||||
notificationManager.notify(id.hashCode(), notification)
|
||||
} else {
|
||||
@@ -426,10 +430,11 @@ class DownloadWorker @AssistedInject constructor(
|
||||
private val settings: AppSettings,
|
||||
) {
|
||||
|
||||
suspend fun schedule(manga: Manga, chaptersIds: Collection<Long>?) {
|
||||
suspend fun schedule(manga: Manga, chaptersIds: Collection<Long>?, isSilent: Boolean) {
|
||||
dataRepository.storeManga(manga)
|
||||
val data = Data.Builder()
|
||||
.putLong(MANGA_ID, manga.id)
|
||||
.putBoolean(IS_SILENT, isSilent)
|
||||
if (!chaptersIds.isNullOrEmpty()) {
|
||||
data.putLongArray(CHAPTERS_IDS, chaptersIds.toLongArray())
|
||||
}
|
||||
@@ -549,6 +554,7 @@ class DownloadWorker @AssistedInject constructor(
|
||||
const val SLOWDOWN_DELAY = 200L
|
||||
const val MANGA_ID = "manga_id"
|
||||
const val CHAPTERS_IDS = "chapters"
|
||||
const val IS_SILENT = "silent"
|
||||
const val TAG = "download"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,17 @@ import android.view.View
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.text.inSpans
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.MultiSelectListPreference
|
||||
import androidx.preference.Preference
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.TrackerDownloadStrategy
|
||||
import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.setDefaultValueCompat
|
||||
import org.koitharu.kotatsu.parsers.util.names
|
||||
import org.koitharu.kotatsu.settings.tracker.categories.TrackerCategoriesConfigSheet
|
||||
import org.koitharu.kotatsu.settings.utils.DozeHelper
|
||||
import org.koitharu.kotatsu.settings.utils.MultiSummaryProvider
|
||||
@@ -50,6 +54,10 @@ class TrackerSettingsFragment :
|
||||
}
|
||||
}
|
||||
}
|
||||
findPreference<ListPreference>(AppSettings.KEY_TRACKER_DOWNLOAD)?.run {
|
||||
entryValues = TrackerDownloadStrategy.entries.names()
|
||||
setDefaultValueCompat(TrackerDownloadStrategy.DISABLED.name)
|
||||
}
|
||||
dozeHelper.updatePreference()
|
||||
updateCategoriesEnabled()
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.work.WorkManager
|
||||
import androidx.work.WorkQuery
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.await
|
||||
import dagger.Lazy
|
||||
import dagger.Reusable
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
@@ -47,10 +48,14 @@ import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||
import org.koitharu.kotatsu.core.logs.FileLogger
|
||||
import org.koitharu.kotatsu.core.logs.TrackerLogger
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.TrackerDownloadStrategy
|
||||
import org.koitharu.kotatsu.core.util.ext.awaitUniqueWorkInfoByName
|
||||
import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission
|
||||
import org.koitharu.kotatsu.core.util.ext.onEachIndexed
|
||||
import org.koitharu.kotatsu.core.util.ext.trySetForeground
|
||||
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
|
||||
import org.koitharu.kotatsu.local.data.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
import org.koitharu.kotatsu.parsers.util.toIntUp
|
||||
import org.koitharu.kotatsu.settings.SettingsActivity
|
||||
@@ -76,6 +81,8 @@ class TrackWorker @AssistedInject constructor(
|
||||
private val checkNewChaptersUseCase: CheckNewChaptersUseCase,
|
||||
private val workManager: WorkManager,
|
||||
@TrackerLogger private val logger: FileLogger,
|
||||
private val localRepositoryLazy: Lazy<LocalMangaRepository>,
|
||||
private val downloadSchedulerLazy: Lazy<DownloadWorker.Scheduler>,
|
||||
) : CoroutineWorker(context, workerParams) {
|
||||
|
||||
private val notificationManager by lazy { NotificationManagerCompat.from(applicationContext) }
|
||||
@@ -144,12 +151,16 @@ class TrackWorker @AssistedInject constructor(
|
||||
if (applicationContext.checkNotificationPermission(WORKER_CHANNEL_ID)) {
|
||||
notificationManager.notify(WORKER_NOTIFICATION_ID, createWorkerNotification(tracks.size, index + 1))
|
||||
}
|
||||
if (it is MangaUpdates.Failure) {
|
||||
val e = it.error
|
||||
logger.log("checkUpdatesAsync", e)
|
||||
if (e is CloudFlareProtectedException) {
|
||||
CaptchaNotifier(applicationContext).notify(e)
|
||||
when (it) {
|
||||
is MangaUpdates.Failure -> {
|
||||
val e = it.error
|
||||
logger.log("checkUpdatesAsync", e)
|
||||
if (e is CloudFlareProtectedException) {
|
||||
CaptchaNotifier(applicationContext).notify(e)
|
||||
}
|
||||
}
|
||||
|
||||
is MangaUpdates.Success -> processDownload(it)
|
||||
}
|
||||
}.mapNotNull {
|
||||
when (it) {
|
||||
@@ -237,6 +248,25 @@ class TrackWorker @AssistedInject constructor(
|
||||
}
|
||||
}.build()
|
||||
|
||||
private suspend fun processDownload(mangaUpdates: MangaUpdates.Success) {
|
||||
if (!mangaUpdates.isValid || mangaUpdates.newChapters.isEmpty()) {
|
||||
return
|
||||
}
|
||||
when (settings.trackerDownloadStrategy) {
|
||||
TrackerDownloadStrategy.DISABLED -> Unit
|
||||
TrackerDownloadStrategy.DOWNLOADED -> {
|
||||
val localManga = localRepositoryLazy.get().findSavedManga(mangaUpdates.manga)
|
||||
if (localManga != null) {
|
||||
downloadSchedulerLazy.get().schedule(
|
||||
manga = mangaUpdates.manga,
|
||||
chaptersIds = mangaUpdates.newChapters.mapToSet { it.id },
|
||||
isSilent = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Reusable
|
||||
class Scheduler @Inject constructor(
|
||||
private val workManager: WorkManager,
|
||||
|
||||
@@ -109,4 +109,8 @@
|
||||
<item>@string/chapters_read</item>
|
||||
<item>@string/chapters_left</item>
|
||||
</string-array>
|
||||
<string-array name="tracker_download_strategies" translatable="false">
|
||||
<item>@string/never</item>
|
||||
<item>@string/manga_with_downloaded_chapters</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -698,4 +698,7 @@
|
||||
<string name="scrobbler_auth_intro">Sign in to set up integration with %s. This will allow you to track your manga reading progress and status</string>
|
||||
<string name="unstable_feature">Unstable feature</string>
|
||||
<string name="unstable_feature_summary">This function is experimental. Please make sure you have a backup to avoid data loss</string>
|
||||
<string name="downloads_background">Background downloads</string>
|
||||
<string name="download_new_chapters">Download new chapters</string>
|
||||
<string name="manga_with_downloaded_chapters">Manga with downloaded chapters</string>
|
||||
</resources>
|
||||
|
||||
@@ -52,6 +52,13 @@
|
||||
android:summary="@string/disable_nsfw_notifications_summary"
|
||||
android:title="@string/disable_nsfw_notifications" />
|
||||
|
||||
<ListPreference
|
||||
android:dependency="tracker_enabled"
|
||||
android:entries="@array/tracker_download_strategies"
|
||||
android:key="tracker_download"
|
||||
android:title="@string/download_new_chapters"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<Preference
|
||||
android:dependency="tracker_enabled"
|
||||
android:key="ignore_dose"
|
||||
|
||||
Reference in New Issue
Block a user