diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt index 89cbc1aaf..45232e2ef 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -141,6 +141,12 @@ class AppSettings(context: Context) { } } + val isDownloadsSlowdownEnabled: Boolean + get() = prefs.getBoolean(KEY_DOWNLOADS_SLOWDOWN, false) + + val downloadsParallelism: Int + get() = prefs.getInt(KEY_DOWNLOADS_PARALLELISM, 2) + val isSuggestionsEnabled: Boolean get() = prefs.getBoolean(KEY_SUGGESTIONS, false) @@ -261,6 +267,8 @@ class AppSettings(context: Context) { const val KEY_SUGGESTIONS_EXCLUDE_NSFW = "suggestions_exclude_nsfw" const val KEY_SUGGESTIONS_EXCLUDE_TAGS = "suggestions_exclude_tags" const val KEY_SEARCH_SINGLE_SOURCE = "search_single_source" + const val KEY_DOWNLOADS_PARALLELISM = "downloads_parallelism" + const val KEY_DOWNLOADS_SLOWDOWN = "downloads_slowdown" // About const val KEY_APP_UPDATE = "app_update" diff --git a/app/src/main/java/org/koitharu/kotatsu/download/domain/DownloadManager.kt b/app/src/main/java/org/koitharu/kotatsu/download/domain/DownloadManager.kt index 3a2067705..4708c0849 100644 --- a/app/src/main/java/org/koitharu/kotatsu/download/domain/DownloadManager.kt +++ b/app/src/main/java/org/koitharu/kotatsu/download/domain/DownloadManager.kt @@ -17,6 +17,7 @@ import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.local.data.PagesCache import org.koitharu.kotatsu.local.domain.CbzMangaOutput import org.koitharu.kotatsu.local.domain.LocalMangaRepository @@ -30,8 +31,8 @@ import org.koitharu.kotatsu.utils.progress.ProgressJob import java.io.File private const val MAX_DOWNLOAD_ATTEMPTS = 3 -private const val MAX_PARALLEL_DOWNLOADS = 2 private const val DOWNLOAD_ERROR_DELAY = 500L +private const val SLOWDOWN_DELAY = 200L class DownloadManager( private val coroutineScope: CoroutineScope, @@ -40,9 +41,10 @@ class DownloadManager( private val okHttp: OkHttpClient, private val cache: PagesCache, private val localMangaRepository: LocalMangaRepository, + private val settings: AppSettings, ) { - private val connectivityManager = context.applicationContext.getSystemService( + private val connectivityManager = context.getSystemService( Context.CONNECTIVITY_SERVICE ) as ConnectivityManager private val coverWidth = context.resources.getDimensionPixelSize( @@ -51,7 +53,7 @@ class DownloadManager( private val coverHeight = context.resources.getDimensionPixelSize( androidx.core.R.dimen.compat_notification_large_icon_max_height ) - private val semaphore = Semaphore(MAX_PARALLEL_DOWNLOADS) + private val semaphore = Semaphore(settings.downloadsParallelism) fun downloadManga( manga: Manga, @@ -141,6 +143,10 @@ class DownloadManager( totalPages = pages.size, currentPage = pageIndex, ) + + if (settings.isDownloadsSlowdownEnabled) { + delay(SLOWDOWN_DELAY) + } } } outState.value = DownloadState.PostProcessing(startId, data, cover) @@ -206,4 +212,24 @@ class DownloadManager( error = throwable, ) } + + class Factory( + private val context: Context, + private val imageLoader: ImageLoader, + private val okHttp: OkHttpClient, + private val cache: PagesCache, + private val localMangaRepository: LocalMangaRepository, + private val settings: AppSettings, + ) { + + fun create(coroutineScope: CoroutineScope) = DownloadManager( + coroutineScope = coroutineScope, + context = context, + imageLoader = imageLoader, + okHttp = okHttp, + cache = cache, + localMangaRepository = localMangaRepository, + settings = settings, + ) + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/download/ui/service/DownloadService.kt b/app/src/main/java/org/koitharu/kotatsu/download/ui/service/DownloadService.kt index 41bb3e273..13365094c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/download/ui/service/DownloadService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/download/ui/service/DownloadService.kt @@ -49,13 +49,8 @@ class DownloadService : BaseService() { notificationSwitcher = ForegroundNotificationSwitcher(this) val wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager) .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "kotatsu:downloading") - downloadManager = DownloadManager( + downloadManager = get().create( coroutineScope = lifecycleScope + WakeLockNode(wakeLock, TimeUnit.HOURS.toMillis(1)), - context = this, - imageLoader = get(), - okHttp = get(), - cache = get(), - localMangaRepository = get(), ) DownloadNotification.createChannel(this) registerReceiver(controlReceiver, IntentFilter(ACTION_DOWNLOAD_CANCEL)) diff --git a/app/src/main/java/org/koitharu/kotatsu/local/LocalModule.kt b/app/src/main/java/org/koitharu/kotatsu/local/LocalModule.kt index cda2dbad6..928fe706b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/LocalModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/LocalModule.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.local import org.koin.android.ext.koin.androidContext import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.dsl.module +import org.koitharu.kotatsu.download.domain.DownloadManager import org.koitharu.kotatsu.local.data.LocalStorageManager import org.koitharu.kotatsu.local.domain.LocalMangaRepository import org.koitharu.kotatsu.local.ui.LocalListViewModel @@ -16,5 +17,7 @@ val localModule factory { ExternalStorageHelper(androidContext()) } + factory { DownloadManager.Factory(androidContext(), get(), get(), get(), get(), get()) } + viewModel { LocalListViewModel(get(), get(), get(), get()) } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt index 5e4c7555b..d9c8da758 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt @@ -4,7 +4,6 @@ import android.content.SharedPreferences import android.os.Bundle import android.view.View import androidx.preference.Preference -import java.io.File import kotlinx.coroutines.launch import org.koin.android.ext.android.inject import org.koitharu.kotatsu.R @@ -13,8 +12,10 @@ import org.koitharu.kotatsu.base.ui.dialog.StorageSelectDialog import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.local.data.LocalStorageManager import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.settings.utils.SliderPreference import org.koitharu.kotatsu.utils.ext.getStorageName import org.koitharu.kotatsu.utils.ext.viewLifecycleScope +import java.io.File class ContentSettingsFragment : BasePreferenceFragment(R.string.content), @@ -29,6 +30,13 @@ class ContentSettingsFragment : findPreference(AppSettings.KEY_SUGGESTIONS)?.setSummary( if (settings.isSuggestionsEnabled) R.string.enabled else R.string.disabled ) + findPreference(AppSettings.KEY_DOWNLOADS_PARALLELISM)?.run { + summary = value.toString() + setOnPreferenceChangeListener { preference, newValue -> + preference.summary = newValue.toString() + true + } + } bindRemoteSourcesSummary() } diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/AndroidExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/AndroidExt.kt index 21140be00..d1ff7ae6c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/AndroidExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/AndroidExt.kt @@ -17,6 +17,7 @@ suspend fun ConnectivityManager.waitForNetwork(): Network { return suspendCancellableCoroutine { cont -> val callback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { + unregisterNetworkCallback(this) if (cont.isActive) { cont.resume(network) } diff --git a/app/src/main/res/layout/preference_slider.xml b/app/src/main/res/layout/preference_slider.xml index f3dfdc29a..dc98ba093 100644 --- a/app/src/main/res/layout/preference_slider.xml +++ b/app/src/main/res/layout/preference_slider.xml @@ -5,13 +5,12 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="?android:attr/selectableItemBackground" android:baselineAligned="false" android:clipChildren="false" android:clipToPadding="false" - android:orientation="horizontal" android:gravity="center_vertical" android:minHeight="?android:attr/listPreferredItemHeightSmall" + android:orientation="horizontal" android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" tools:ignore="PrivateResource"> @@ -27,17 +26,18 @@ android:clipToPadding="false" android:orientation="vertical"> - + - + Удалить выбранную мангу с накопителя? Удаление завершено Загрузить выбранную мангу со всеми главами? Это может привести к большому расходу трафика и места на накопителе + Загружать параллельно + Замедление загрузки + Помогает избежать блокировки IP-адреса \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a44dfec15..e249d13f7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -274,4 +274,7 @@ Delete selected items from device permanently? Removal completed Are you sure you want to download all selected manga with all its chapters? This action can consume a lot of traffic and storage + Parallel downloads + Download slowdown + Helps avoid blocking your IP address \ No newline at end of file diff --git a/app/src/main/res/xml/pref_content.xml b/app/src/main/res/xml/pref_content.xml index 9508fdf58..53fee4124 100644 --- a/app/src/main/res/xml/pref_content.xml +++ b/app/src/main/res/xml/pref_content.xml @@ -8,17 +8,32 @@ android:key="remote_sources" android:title="@string/remote_sources" /> - - + + + + + +