From fd3c83cb137afc5f5b1262aacaa9a01f51d2d12f Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 6 Feb 2023 19:45:55 +0200 Subject: [PATCH 1/8] Allow to use own UserAgent for each manga source --- app/build.gradle | 2 +- .../bookmarks/ui/adapter/BookmarkListAD.kt | 2 +- .../bookmarks/ui/adapter/BookmarksGroupAD.kt | 8 +++-- .../browser/cloudflare/CloudFlareDialog.kt | 10 ++++-- .../org/koitharu/kotatsu/core/AppModule.kt | 3 +- .../CloudFlareProtectedException.kt | 6 ++-- .../exceptions/resolve/ExceptionResolver.kt | 7 ++-- .../core/network/CloudFlareInterceptor.kt | 10 ++++-- .../core/network/UserAgentInterceptor.kt | 32 ++++++++++++++----- .../kotatsu/core/os/ShortcutsUpdater.kt | 1 + .../core/parser/RemoteMangaRepository.kt | 4 +++ .../core/parser/favicon/FaviconFetcher.kt | 7 ++-- .../kotatsu/details/ui/DetailsFragment.kt | 7 +++- .../details/ui/scrobbling/ScrobblingInfoAD.kt | 2 +- .../scrobbling/ScrobblingInfoBottomSheet.kt | 4 +-- .../download/domain/DownloadManager.kt | 15 +++++++-- .../kotatsu/download/ui/DownloadItemAD.kt | 14 ++++++-- .../ui/adapter/ExploreAdapterDelegates.kt | 2 +- .../ui/categories/adapter/CategoryAD.kt | 10 ++++-- .../kotatsu/image/ui/ImageActivity.kt | 9 ++++-- .../kotatsu/list/ui/MangaListFragment.kt | 2 +- .../list/ui/adapter/MangaGridItemAD.kt | 2 +- .../ui/adapter/MangaListDetailedItemAD.kt | 2 +- .../list/ui/adapter/MangaListItemAD.kt | 8 +++-- .../kotatsu/list/ui/model/MangaItemModel.kt | 6 +++- .../kotatsu/local/ui/ImportService.kt | 6 +++- .../kotatsu/reader/domain/PageLoader.kt | 1 + .../colorfilter/ColorFilterConfigActivity.kt | 12 +++++-- .../ui/thumbnails/adapter/PageThumbnailAD.kt | 9 ++++-- .../adapter/SearchSuggestionSourceAD.kt | 6 ++-- .../adapter/SearchSuggestionsMangaListAD.kt | 2 +- .../adapter/SourceConfigAdapterDelegates.kt | 14 ++++---- .../sources/auth/SourceAuthActivity.kt | 10 +++--- .../tracker/ui/feed/adapter/FeedItemAD.kt | 2 +- .../kotatsu/tracker/work/TrackWorker.kt | 6 +++- .../org/koitharu/kotatsu/utils/ext/CoilExt.kt | 5 ++- .../widget/recent/RecentListFactory.kt | 1 + .../kotatsu/widget/shelf/ShelfListFactory.kt | 1 + 38 files changed, 181 insertions(+), 69 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 2fea65e6f..24559e2ff 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -86,7 +86,7 @@ afterEvaluate { } } dependencies { - implementation('com.github.KotatsuApp:kotatsu-parsers:c28e2a72d5') { + implementation('com.github.KotatsuApp:kotatsu-parsers:00abaea324') { exclude group: 'org.json', module: 'json' } diff --git a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt index 79ce1e06b..6b3c383f4 100644 --- a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt @@ -26,7 +26,7 @@ fun bookmarkListAD( binding.root.setOnLongClickListener(listener) bind { - binding.imageViewThumb.newImageRequest(item.imageUrl)?.run { + binding.imageViewThumb.newImageRequest(item.imageUrl, item.manga.source)?.run { referer(item.manga.publicUrl) placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksGroupAD.kt b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksGroupAD.kt index 7927ed287..c3beeb87b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksGroupAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksGroupAD.kt @@ -14,7 +14,11 @@ import org.koitharu.kotatsu.bookmarks.ui.model.BookmarksGroup import org.koitharu.kotatsu.databinding.ItemBookmarksGroupBinding import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.utils.ext.* +import org.koitharu.kotatsu.utils.ext.clearItemDecorations +import org.koitharu.kotatsu.utils.ext.disposeImageRequest +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.newImageRequest +import org.koitharu.kotatsu.utils.ext.referer fun bookmarksGroupAD( coil: ImageLoader, @@ -45,7 +49,7 @@ fun bookmarksGroupAD( binding.recyclerView.addItemDecoration(spacingDecoration) selectionController.attachToRecyclerView(item.manga, binding.recyclerView) } - binding.imageViewCover.newImageRequest(item.manga.coverUrl)?.run { + binding.imageViewCover.newImageRequest(item.manga.coverUrl, item.manga.source)?.run { referer(item.manga.publicUrl) placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt index b95eeac99..2b4ed813c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt @@ -12,7 +12,9 @@ import androidx.core.view.isInvisible import androidx.fragment.app.setFragmentResult import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint +import okhttp3.Headers import org.koitharu.kotatsu.base.ui.AlertDialogFragment +import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.network.UserAgentInterceptor import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar import org.koitharu.kotatsu.databinding.FragmentCloudflareBinding @@ -42,7 +44,7 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud cacheMode = WebSettings.LOAD_DEFAULT domStorageEnabled = true databaseEnabled = true - userAgentString = UserAgentInterceptor.userAgentChrome + userAgentString = arguments?.getString(ARG_UA) ?: UserAgentInterceptor.userAgentChrome } binding.webView.webViewClient = CloudFlareClient(cookieJar, this, url.orEmpty()) CookieManager.getInstance().setAcceptThirdPartyCookies(binding.webView, true) @@ -92,9 +94,13 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud const val TAG = "CloudFlareDialog" const val EXTRA_RESULT = "result" private const val ARG_URL = "url" + private const val ARG_UA = "ua" - fun newInstance(url: String) = CloudFlareDialog().withArgs(1) { + fun newInstance(url: String, headers: Headers?) = CloudFlareDialog().withArgs(2) { putString(ARG_URL, url) + headers?.get(CommonHeaders.USER_AGENT)?.let { + putString(ARG_UA, it) + } } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt b/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt index bf4c98354..968731bce 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt @@ -85,6 +85,7 @@ interface AppModule { @Singleton fun provideOkHttpClient( localStorageManager: LocalStorageManager, + userAgentInterceptor: UserAgentInterceptor, cookieJar: CookieJar, settings: AppSettings, ): OkHttpClient { @@ -97,7 +98,7 @@ interface AppModule { dns(DoHManager(cache, settings)) cache(cache) addInterceptor(GZipInterceptor()) - addInterceptor(UserAgentInterceptor()) + addInterceptor(userAgentInterceptor) addInterceptor(CloudFlareInterceptor()) if (BuildConfig.DEBUG) { addInterceptor(CurlLoggingInterceptor()) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt index ef20b4fb0..ee6163e25 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt @@ -1,7 +1,9 @@ package org.koitharu.kotatsu.core.exceptions +import okhttp3.Headers import okio.IOException class CloudFlareProtectedException( - val url: String -) : IOException("Protected by CloudFlare") \ No newline at end of file + val url: String, + val headers: Headers, +) : IOException("Protected by CloudFlare") diff --git a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt index b7147aadd..4afa01e5d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt @@ -7,6 +7,7 @@ import androidx.collection.ArrayMap import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import kotlinx.coroutines.suspendCancellableCoroutine +import okhttp3.Headers import org.koitharu.kotatsu.R import org.koitharu.kotatsu.browser.BrowserActivity import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog @@ -43,7 +44,7 @@ class ExceptionResolver private constructor( } suspend fun resolve(e: Throwable): Boolean = when (e) { - is CloudFlareProtectedException -> resolveCF(e.url) + is CloudFlareProtectedException -> resolveCF(e.url, e.headers) is AuthRequiredException -> resolveAuthException(e.source) is NotFoundException -> { openInBrowser(e.url) @@ -53,8 +54,8 @@ class ExceptionResolver private constructor( else -> false } - private suspend fun resolveCF(url: String): Boolean { - val dialog = CloudFlareDialog.newInstance(url) + private suspend fun resolveCF(url: String, headers: Headers): Boolean { + val dialog = CloudFlareDialog.newInstance(url, headers) val fm = getFragmentManager() return suspendCancellableCoroutine { cont -> fm.clearFragmentResult(CloudFlareDialog.TAG) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt index a32a94c83..b4dcc9e62 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt @@ -13,13 +13,17 @@ private const val SERVER_CLOUDFLARE = "cloudflare" class CloudFlareInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { - val response = chain.proceed(chain.request()) + val request = chain.request() + val response = chain.proceed(request) if (response.code == HTTP_FORBIDDEN || response.code == HTTP_UNAVAILABLE) { if (response.header(HEADER_SERVER)?.startsWith(SERVER_CLOUDFLARE) == true) { response.closeQuietly() - throw CloudFlareProtectedException(response.request.url.toString()) + throw CloudFlareProtectedException( + url = response.request.url.toString(), + headers = request.headers, + ) } } return response } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt index 6a781fd45..a0b46d309 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt @@ -1,24 +1,40 @@ package org.koitharu.kotatsu.core.network import android.os.Build -import java.util.* +import dagger.Lazy import okhttp3.Interceptor +import okhttp3.Request import okhttp3.Response import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.core.parser.RemoteMangaRepository +import org.koitharu.kotatsu.parsers.model.MangaSource +import java.util.Locale +import javax.inject.Inject +import javax.inject.Singleton -class UserAgentInterceptor : Interceptor { +@Singleton +class UserAgentInterceptor @Inject constructor( + private val mangaRepositoryFactoryLazy: Lazy, +) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() return chain.proceed( if (request.header(CommonHeaders.USER_AGENT) == null) { request.newBuilder() - .addHeader(CommonHeaders.USER_AGENT, userAgentChrome) + .addHeader(CommonHeaders.USER_AGENT, getUserAgent(request)) .build() - } else request + } else request, ) } + private fun getUserAgent(request: Request): String { + val source = request.tag(MangaSource::class.java) ?: return userAgent + val repository = mangaRepositoryFactoryLazy.get().create(source) as? RemoteMangaRepository + return repository?.userAgent ?: userAgent + } + companion object { val userAgent @@ -28,7 +44,7 @@ class UserAgentInterceptor : Interceptor { Build.MODEL, Build.BRAND, Build.DEVICE, - Locale.getDefault().language + Locale.getDefault().language, ) // TODO Decide what to do with this afterwards val userAgentChrome @@ -36,8 +52,8 @@ class UserAgentInterceptor : Interceptor { "Mozilla/5.0 (Linux; Android %s; %s) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/100.0.4896.127 Mobile Safari/537.36" ).format( - Build.VERSION.RELEASE, - Build.MODEL, - ) + Build.VERSION.RELEASE, + Build.MODEL, + ) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/os/ShortcutsUpdater.kt b/app/src/main/java/org/koitharu/kotatsu/core/os/ShortcutsUpdater.kt index 82d7fa4bb..39708b1bd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/os/ShortcutsUpdater.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/os/ShortcutsUpdater.kt @@ -118,6 +118,7 @@ class ShortcutsUpdater @Inject constructor( ImageRequest.Builder(context) .data(manga.coverUrl) .size(iconSize.width, iconSize.height) + .tag(manga.source) .precision(Precision.EXACT) .scale(Scale.FILL) .build(), diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt index ecd2d170f..ba4da8fa9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.currentCoroutineContext import org.koitharu.kotatsu.core.cache.ContentCache import org.koitharu.kotatsu.core.cache.SafeDeferred +import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.prefs.SourceSettings import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParserAuthProvider @@ -39,6 +40,9 @@ class RemoteMangaRepository( getConfig().defaultSortOrder = value } + val userAgent: String? + get() = parser.headers?.get(CommonHeaders.USER_AGENT) + override suspend fun getList(offset: Int, query: String): List { return parser.getList(offset, query) } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt index ee052cfdd..0fdd23162 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt @@ -53,7 +53,7 @@ class FaviconFetcher( options.size.height.pxOrElse { FALLBACK_SIZE }, ) val icon = checkNotNull(favicons.find(sizePx)) { "No favicons found" } - val response = loadIcon(icon.url, favicons.referer) + val response = loadIcon(icon.url, repo.userAgent, favicons.referer) val responseBody = response.requireBody() val source = writeToDiskCache(responseBody)?.toImageSource() ?: responseBody.toImageSource() return SourceResult( @@ -63,11 +63,14 @@ class FaviconFetcher( ) } - private suspend fun loadIcon(url: String, referer: String): Response { + private suspend fun loadIcon(url: String, userAgent: String?, referer: String): Response { val request = Request.Builder() .url(url) .get() .header(CommonHeaders.REFERER, referer) + if (userAgent != null) { + request.header(CommonHeaders.USER_AGENT, userAgent) + } @Suppress("UNCHECKED_CAST") options.tags.asMap().forEach { request.tag(it.key as Class, it.value) } val response = okHttpClient.newCall(request.build()).await() diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt index 6d2203632..5211d9320 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt @@ -254,7 +254,11 @@ class DetailsFragment : R.id.imageView_cover -> { startActivity( - ImageActivity.newIntent(v.context, manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }), + ImageActivity.newIntent( + v.context, + manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }, + manga.source, + ), scaleUpActivityOptionsOf(v).toBundle(), ) } @@ -337,6 +341,7 @@ class DetailsFragment : .target(binding.imageViewCover) .size(CoverSizeResolver(binding.imageViewCover)) .data(imageUrl) + .tag(manga.source) .crossfade(context) .referer(manga.publicUrl) .lifecycle(viewLifecycleOwner) diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt index 7c3af4dc8..848e73361 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt @@ -23,7 +23,7 @@ fun scrobblingInfoAD( } bind { - binding.imageViewCover.newImageRequest(item.coverUrl)?.run { + binding.imageViewCover.newImageRequest(item.coverUrl /* TODO */, null)?.run { placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) error(R.drawable.ic_error_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoBottomSheet.kt index 39fd5fd45..c5350923b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoBottomSheet.kt @@ -106,7 +106,7 @@ class ScrobblingInfoBottomSheet : R.id.imageView_cover -> { val coverUrl = viewModel.scrobblingInfo.value?.getOrNull(scrobblerIndex)?.coverUrl ?: return val options = scaleUpActivityOptionsOf(v) - startActivity(ImageActivity.newIntent(v.context, coverUrl), options.toBundle()) + startActivity(ImageActivity.newIntent(v.context, coverUrl, null), options.toBundle()) } } } @@ -123,7 +123,7 @@ class ScrobblingInfoBottomSheet : binding.spinnerStatus.setSelection(scrobbling.status?.ordinal ?: -1) binding.imageViewLogo.contentDescription = getString(scrobbling.scrobbler.titleResId) binding.imageViewLogo.setImageResource(scrobbling.scrobbler.iconResId) - binding.imageViewCover.newImageRequest(scrobbling.coverUrl)?.apply { + binding.imageViewCover.newImageRequest(scrobbling.coverUrl, null)?.apply { lifecycle(viewLifecycleOwner) placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) 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 931d09ae9..e534f9120 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 @@ -118,7 +118,7 @@ class DownloadManager @AssistedInject constructor( val data = if (manga.chapters.isNullOrEmpty()) repo.getDetails(manga) else manga output = CbzMangaOutput.get(destination, data) val coverUrl = data.largeCoverUrl ?: data.coverUrl - downloadFile(coverUrl, data.publicUrl, destination, tempFileName).let { file -> + downloadFile(coverUrl, data.publicUrl, destination, tempFileName, repo.source).let { file -> output.addCover(file, MimeTypeMap.getFileExtensionFromUrl(coverUrl)) } val chapters = checkNotNull( @@ -139,7 +139,8 @@ class DownloadManager @AssistedInject constructor( for ((pageIndex, page) in pages.withIndex()) { runFailsafe(outState, pausingHandle) { val url = repo.getPageUrl(page) - val file = cache[url] ?: downloadFile(url, page.referer, destination, tempFileName) + val file = cache[url] + ?: downloadFile(url, page.referer, destination, tempFileName, repo.source) output.addPage( chapter = chapter, file = file, @@ -209,10 +210,17 @@ class DownloadManager @AssistedInject constructor( } } - private suspend fun downloadFile(url: String, referer: String, destination: File, tempFileName: String): File { + private suspend fun downloadFile( + url: String, + referer: String, + destination: File, + tempFileName: String, + source: MangaSource, + ): File { val request = Request.Builder() .url(url) .header(CommonHeaders.REFERER, referer) + .tag(source) .cacheControl(CommonHeaders.CACHE_CONTROL_DISABLED) .get() .build() @@ -243,6 +251,7 @@ class DownloadManager @AssistedInject constructor( ImageRequest.Builder(context) .data(manga.coverUrl) .referer(manga.publicUrl) + .tag(manga.source) .size(coverWidth, coverHeight) .scale(Scale.FILL) .build(), diff --git a/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadItemAD.kt index 4d24c9104..81554a088 100644 --- a/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadItemAD.kt @@ -13,7 +13,11 @@ import org.koitharu.kotatsu.databinding.ItemDownloadBinding import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.download.domain.DownloadState import org.koitharu.kotatsu.parsers.util.format -import org.koitharu.kotatsu.utils.ext.* +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.getDisplayMessage +import org.koitharu.kotatsu.utils.ext.newImageRequest +import org.koitharu.kotatsu.utils.ext.onFirst +import org.koitharu.kotatsu.utils.ext.referer fun downloadItemAD( scope: CoroutineScope, @@ -40,7 +44,7 @@ fun downloadItemAD( bind { job?.cancel() job = item.progressAsFlow().onFirst { state -> - binding.imageViewCover.newImageRequest(state.manga.coverUrl)?.run { + binding.imageViewCover.newImageRequest(state.manga.coverUrl, state.manga.source)?.run { referer(state.manga.publicUrl) placeholder(state.cover) fallback(R.drawable.ic_placeholder) @@ -60,6 +64,7 @@ fun downloadItemAD( binding.buttonCancel.isVisible = false binding.buttonResume.isVisible = false } + is DownloadState.Done -> { binding.textViewStatus.setText(R.string.download_complete) binding.progressBar.isIndeterminate = false @@ -69,6 +74,7 @@ fun downloadItemAD( binding.buttonCancel.isVisible = false binding.buttonResume.isVisible = false } + is DownloadState.Error -> { binding.textViewStatus.setText(R.string.error_occurred) binding.progressBar.isIndeterminate = false @@ -79,6 +85,7 @@ fun downloadItemAD( binding.buttonCancel.isVisible = state.canRetry binding.buttonResume.isVisible = state.canRetry } + is DownloadState.PostProcessing -> { binding.textViewStatus.setText(R.string.processing_) binding.progressBar.isIndeterminate = true @@ -88,6 +95,7 @@ fun downloadItemAD( binding.buttonCancel.isVisible = false binding.buttonResume.isVisible = false } + is DownloadState.Preparing -> { binding.textViewStatus.setText(R.string.preparing_) binding.progressBar.isIndeterminate = true @@ -97,6 +105,7 @@ fun downloadItemAD( binding.buttonCancel.isVisible = true binding.buttonResume.isVisible = false } + is DownloadState.Progress -> { binding.textViewStatus.setText(R.string.manga_downloading_) binding.progressBar.isIndeterminate = false @@ -109,6 +118,7 @@ fun downloadItemAD( binding.buttonCancel.isVisible = true binding.buttonResume.isVisible = false } + is DownloadState.Queued -> { binding.textViewStatus.setText(R.string.queued) binding.progressBar.isIndeterminate = false diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt b/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt index c1cf4f753..0c9712fcd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt +++ b/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt @@ -76,7 +76,7 @@ fun exploreSourceListItemAD( bind { binding.textViewTitle.text = item.source.title val fallbackIcon = FaviconFallbackDrawable(context, item.source.name) - binding.imageViewIcon.newImageRequest(item.source.faviconUri())?.run { + binding.imageViewIcon.newImageRequest(item.source.faviconUri(), item.source)?.run { fallback(fallbackIcon) placeholder(fallbackIcon) error(fallbackIcon) diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt index db3ba22be..8c633bcfb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt @@ -5,7 +5,9 @@ import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.view.MotionEvent import android.view.View -import android.view.View.* +import android.view.View.OnClickListener +import android.view.View.OnLongClickListener +import android.view.View.OnTouchListener import androidx.core.graphics.ColorUtils import androidx.core.view.isVisible import androidx.core.widget.ImageViewCompat @@ -16,7 +18,11 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.databinding.ItemCategoryBinding import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesListListener import org.koitharu.kotatsu.list.ui.model.ListModel -import org.koitharu.kotatsu.utils.ext.* +import org.koitharu.kotatsu.utils.ext.animatorDurationScale +import org.koitharu.kotatsu.utils.ext.disposeImageRequest +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.getThemeColor +import org.koitharu.kotatsu.utils.ext.newImageRequest fun categoryAD( coil: ImageLoader, diff --git a/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt b/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt index c8e5300a2..0fe13a2d3 100644 --- a/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt @@ -17,11 +17,12 @@ import coil.target.ViewTarget import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivityImageBinding +import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.indicator +import javax.inject.Inject @AndroidEntryPoint class ImageActivity : BaseActivity() { @@ -56,6 +57,7 @@ class ImageActivity : BaseActivity() { .data(url) .memoryCachePolicy(CachePolicy.DISABLED) .lifecycle(this) + .tag(intent.getSerializableExtra(EXTRA_SOURCE) as? MangaSource) .target(SsivTarget(binding.ssiv)) .indicator(binding.progressBar) .enqueueWith(coil) @@ -88,9 +90,12 @@ class ImageActivity : BaseActivity() { companion object { - fun newIntent(context: Context, url: String): Intent { + private const val EXTRA_SOURCE = "source" + + fun newIntent(context: Context, url: String, source: MangaSource?): Intent { return Intent(context, ImageActivity::class.java) .setData(Uri.parse(url)) + .putExtra(EXTRA_SOURCE, source) } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index 01c09394e..ba9ccf454 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -177,7 +177,7 @@ abstract class MangaListFragment : private fun onError(e: Throwable) { if (e is CloudFlareProtectedException) { - CloudFlareDialog.newInstance(e.url).show(childFragmentManager, CloudFlareDialog.TAG) + CloudFlareDialog.newInstance(e.url, e.headers).show(childFragmentManager, CloudFlareDialog.TAG) } else { Snackbar.make( binding.recyclerView, diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt index 7484c6166..6e65e6f83 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt @@ -39,7 +39,7 @@ fun mangaGridItemAD( bind { payloads -> binding.textViewTitle.text = item.title binding.progressView.setPercent(item.progress, MangaListAdapter.PAYLOAD_PROGRESS in payloads) - binding.imageViewCover.newImageRequest(item.coverUrl)?.run { + binding.imageViewCover.newImageRequest(item.coverUrl, item.source)?.run { referer(item.manga.publicUrl) size(CoverSizeResolver(binding.imageViewCover)) placeholder(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt index 1caac32e5..6f7412361 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt @@ -52,7 +52,7 @@ fun mangaListDetailedItemAD( binding.textViewTitle.text = item.title binding.textViewSubtitle.textAndVisible = item.subtitle binding.progressView.setPercent(item.progress, MangaListAdapter.PAYLOAD_PROGRESS in payloads) - binding.imageViewCover.newImageRequest(item.coverUrl)?.run { + binding.imageViewCover.newImageRequest(item.coverUrl, item.source)?.run { referer(item.manga.publicUrl) size(CoverSizeResolver(binding.imageViewCover)) placeholder(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt index 36aa1ada0..dea006ed6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt @@ -10,7 +10,11 @@ import org.koitharu.kotatsu.databinding.ItemMangaListBinding import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.MangaListModel import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.utils.ext.* +import org.koitharu.kotatsu.utils.ext.disposeImageRequest +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.newImageRequest +import org.koitharu.kotatsu.utils.ext.referer +import org.koitharu.kotatsu.utils.ext.textAndVisible fun mangaListItemAD( coil: ImageLoader, @@ -31,7 +35,7 @@ fun mangaListItemAD( bind { binding.textViewTitle.text = item.title binding.textViewSubtitle.textAndVisible = item.subtitle - binding.imageViewCover.newImageRequest(item.coverUrl)?.run { + binding.imageViewCover.newImageRequest(item.coverUrl, item.source)?.run { referer(item.manga.publicUrl) placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/MangaItemModel.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/MangaItemModel.kt index ce5145d26..2d4710a70 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/MangaItemModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/MangaItemModel.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.list.ui.model import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaSource sealed interface MangaItemModel : ListModel { @@ -10,4 +11,7 @@ sealed interface MangaItemModel : ListModel { val coverUrl: String val counter: Int val progress: Float -} \ No newline at end of file + + val source: MangaSource + get() = manga.source +} diff --git a/app/src/main/java/org/koitharu/kotatsu/local/ui/ImportService.kt b/app/src/main/java/org/koitharu/kotatsu/local/ui/ImportService.kt index 1ae9dc6c7..ae7d5aa60 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/ui/ImportService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/ui/ImportService.kt @@ -99,7 +99,11 @@ class ImportService : CoroutineIntentService() { if (manga != null) { notification.setLargeIcon( coil.execute( - ImageRequest.Builder(applicationContext).data(manga.coverUrl).referer(manga.publicUrl).build(), + ImageRequest.Builder(applicationContext) + .data(manga.coverUrl) + .tag(manga.source) + .referer(manga.publicUrl) + .build(), ).toBitmapOrNull(), ) notification.setSubText(manga.title) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt b/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt index 0ac53f720..f90f8397b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt @@ -191,6 +191,7 @@ class PageLoader @Inject constructor( .header(CommonHeaders.REFERER, page.referer) .header(CommonHeaders.ACCEPT, "image/webp,image/png;q=0.9,image/jpeg,*/*;q=0.8") .cacheControl(CommonHeaders.CACHE_CONTROL_DISABLED) + .tag(page.source) .build() okHttp.newCall(request).await().use { response -> check(response.isSuccessful) { diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt index ee94a4f87..2696f393f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt @@ -13,11 +13,9 @@ import coil.ImageLoader import coil.request.ImageRequest import coil.size.Scale import coil.size.ViewSizeResolver -import com.google.android.material.R as materialR import com.google.android.material.slider.LabelFormatter import com.google.android.material.slider.Slider import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga @@ -27,7 +25,14 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.util.format import org.koitharu.kotatsu.reader.domain.ReaderColorFilter -import org.koitharu.kotatsu.utils.ext.* +import org.koitharu.kotatsu.utils.ext.assistedViewModels +import org.koitharu.kotatsu.utils.ext.decodeRegion +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.getParcelableExtraCompat +import org.koitharu.kotatsu.utils.ext.referer +import org.koitharu.kotatsu.utils.ext.setValueRounded +import javax.inject.Inject +import com.google.android.material.R as materialR @AndroidEntryPoint class ColorFilterConfigActivity : @@ -115,6 +120,7 @@ class ColorFilterConfigActivity : .referer(preview.referer) .scale(Scale.FILL) .decodeRegion() + .tag(preview.source) .error(R.drawable.ic_error_placeholder) .size(ViewSizeResolver(binding.imageViewBefore)) .allowRgb565(false) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/adapter/PageThumbnailAD.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/adapter/PageThumbnailAD.kt index d992f747a..fd881846f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/adapter/PageThumbnailAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/adapter/PageThumbnailAD.kt @@ -5,9 +5,12 @@ import coil.ImageLoader import coil.request.ImageRequest import coil.size.Scale import coil.size.Size -import com.google.android.material.R as materialR import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.databinding.ItemPageThumbBinding @@ -19,6 +22,7 @@ import org.koitharu.kotatsu.utils.ext.isLowRamDevice import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.runCatchingCancellable import org.koitharu.kotatsu.utils.ext.setTextColorAttr +import com.google.android.material.R as materialR fun pageThumbnailAD( coil: ImageLoader, @@ -41,6 +45,7 @@ fun pageThumbnailAD( ImageRequest.Builder(context) .data(url) .referer(item.page.referer) + .tag(item.page.source) .size(thumbSize) .scale(Scale.FILL) .allowRgb565(true) diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt index 5367bcbc3..086d52f39 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt @@ -17,7 +17,7 @@ fun searchSuggestionSourceAD( lifecycleOwner: LifecycleOwner, listener: SearchSuggestionListener, ) = adapterDelegateViewBinding( - { inflater, parent -> ItemSearchSuggestionSourceBinding.inflate(inflater, parent, false) } + { inflater, parent -> ItemSearchSuggestionSourceBinding.inflate(inflater, parent, false) }, ) { binding.switchLocal.setOnCheckedChangeListener { _, isChecked -> @@ -31,7 +31,7 @@ fun searchSuggestionSourceAD( binding.textViewTitle.text = item.source.title binding.switchLocal.isChecked = item.isEnabled val fallbackIcon = FaviconFallbackDrawable(context, item.source.name) - binding.imageViewCover.newImageRequest(item.source.faviconUri())?.run { + binding.imageViewCover.newImageRequest(item.source.faviconUri(), item.source)?.run { fallback(fallbackIcon) placeholder(fallbackIcon) error(fallbackIcon) @@ -43,4 +43,4 @@ fun searchSuggestionSourceAD( onViewRecycled { binding.imageViewCover.disposeImageRequest() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionsMangaListAD.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionsMangaListAD.kt index 7c108274a..b451a7997 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionsMangaListAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionsMangaListAD.kt @@ -55,7 +55,7 @@ private fun searchSuggestionMangaGridAD( } bind { - binding.imageViewCover.newImageRequest(item.coverUrl)?.run { + binding.imageViewCover.newImageRequest(item.coverUrl, item.source)?.run { placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) error(R.drawable.ic_error_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt b/app/src/main/java/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt index 299f35ad7..cd0471af2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt @@ -20,7 +20,7 @@ import org.koitharu.kotatsu.utils.image.FaviconFallbackDrawable fun sourceConfigHeaderDelegate() = adapterDelegateViewBinding( - { layoutInflater, parent -> ItemFilterHeaderBinding.inflate(layoutInflater, parent, false) } + { layoutInflater, parent -> ItemFilterHeaderBinding.inflate(layoutInflater, parent, false) }, ) { bind { @@ -31,7 +31,7 @@ fun sourceConfigHeaderDelegate() = fun sourceConfigGroupDelegate( listener: SourceConfigListener, ) = adapterDelegateViewBinding( - { layoutInflater, parent -> ItemExpandableBinding.inflate(layoutInflater, parent, false) } + { layoutInflater, parent -> ItemExpandableBinding.inflate(layoutInflater, parent, false) }, ) { binding.root.setOnClickListener { @@ -50,7 +50,7 @@ fun sourceConfigItemDelegate( lifecycleOwner: LifecycleOwner, ) = adapterDelegateViewBinding( { layoutInflater, parent -> ItemSourceConfigBinding.inflate(layoutInflater, parent, false) }, - on = { item, _, _ -> item is SourceConfigItem.SourceItem && !item.isDraggable } + on = { item, _, _ -> item is SourceConfigItem.SourceItem && !item.isDraggable }, ) { binding.switchToggle.setOnCheckedChangeListener { _, isChecked -> @@ -62,7 +62,7 @@ fun sourceConfigItemDelegate( binding.switchToggle.isChecked = item.isEnabled binding.textViewDescription.textAndVisible = item.summary val fallbackIcon = FaviconFallbackDrawable(context, item.source.name) - binding.imageViewIcon.newImageRequest(item.source.faviconUri())?.run { + binding.imageViewIcon.newImageRequest(item.source.faviconUri(), item.source)?.run { crossfade(context) error(fallbackIcon) placeholder(fallbackIcon) @@ -82,7 +82,7 @@ fun sourceConfigDraggableItemDelegate( listener: SourceConfigListener, ) = adapterDelegateViewBinding( { layoutInflater, parent -> ItemSourceConfigDraggableBinding.inflate(layoutInflater, parent, false) }, - on = { item, _, _ -> item is SourceConfigItem.SourceItem && item.isDraggable } + on = { item, _, _ -> item is SourceConfigItem.SourceItem && item.isDraggable }, ) { val eventListener = object : @@ -117,5 +117,5 @@ fun sourceConfigDraggableItemDelegate( } fun sourceConfigEmptySearchDelegate() = adapterDelegate( - R.layout.item_sources_empty -) { } \ No newline at end of file + R.layout.item_sources_empty, +) { } diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt b/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt index 4cac8671f..f9ce14a07 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt @@ -11,9 +11,7 @@ import androidx.activity.result.contract.ActivityResultContract import androidx.core.graphics.Insets import androidx.core.view.isVisible import androidx.core.view.updatePadding -import com.google.android.material.R as materialR import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.browser.BrowserCallback @@ -26,6 +24,8 @@ import org.koitharu.kotatsu.databinding.ActivityBrowserBinding import org.koitharu.kotatsu.parsers.MangaParserAuthProvider import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.TaggedActivityResult +import javax.inject.Inject +import com.google.android.material.R as materialR @AndroidEntryPoint class SourceAuthActivity : BaseActivity(), BrowserCallback { @@ -44,7 +44,8 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba finishAfterTransition() return } - authProvider = (mangaRepositoryFactory.create(source) as? RemoteMangaRepository)?.getAuthProvider() ?: run { + val repository = mangaRepositoryFactory.create(source) as? RemoteMangaRepository + authProvider = (repository)?.getAuthProvider() ?: run { Toast.makeText( this, getString(R.string.auth_not_supported_by, source.title), @@ -59,7 +60,7 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba } with(binding.webView.settings) { javaScriptEnabled = true - userAgentString = UserAgentInterceptor.userAgentChrome + userAgentString = repository.userAgent ?: UserAgentInterceptor.userAgent } binding.webView.webViewClient = BrowserClient(this) binding.webView.webChromeClient = ProgressChromeClient(binding.progressBar) @@ -96,6 +97,7 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba finishAfterTransition() true } + else -> super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedItemAD.kt index b98b25e17..901e4573a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedItemAD.kt @@ -28,7 +28,7 @@ fun feedItemAD( bind { binding.textViewTitle.isBold = item.isNew binding.textViewSummary.isBold = item.isNew - binding.imageViewCover.newImageRequest(item.imageUrl)?.run { + binding.imageViewCover.newImageRequest(item.imageUrl, item.manga.source)?.run { placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) error(R.drawable.ic_error_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt index fa488b146..4aa0b5a86 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt @@ -155,7 +155,11 @@ class TrackWorker @AssistedInject constructor( setNumber(newChapters.size) setLargeIcon( coil.execute( - ImageRequest.Builder(applicationContext).data(manga.coverUrl).referer(manga.publicUrl).build(), + ImageRequest.Builder(applicationContext) + .data(manga.coverUrl) + .referer(manga.publicUrl) + .tag(manga.source) + .build(), ).toBitmapOrNull(), ) setSmallIcon(R.drawable.ic_stat_book_plus) diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt index 914947ba2..28716c743 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt @@ -13,16 +13,18 @@ import com.google.android.material.progressindicator.BaseProgressIndicator import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.network.CommonHeaders +import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.image.RegionBitmapDecoder import org.koitharu.kotatsu.utils.progress.ImageRequestIndicatorListener -fun ImageView.newImageRequest(url: Any?): ImageRequest.Builder? { +fun ImageView.newImageRequest(url: Any?, mangaSource: MangaSource? = null): ImageRequest.Builder? { val current = CoilUtils.result(this) if (current != null && current.request.data == url) { return null } return ImageRequest.Builder(context) .data(url) + .tag(mangaSource) .crossfade(context) .target(this) } @@ -45,6 +47,7 @@ fun ImageResult.toBitmapOrNull() = when (this) { } catch (_: Throwable) { null } + is ErrorResult -> null } diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt b/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt index 88487c636..c023f9d05 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt @@ -53,6 +53,7 @@ class RecentListFactory( ImageRequest.Builder(context) .data(item.coverUrl) .size(coverSize) + .tag(item.source) .transformations(transformation) .build(), ).requireBitmap() diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt index 6572418d3..f3dd62b87 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt @@ -64,6 +64,7 @@ class ShelfListFactory( ImageRequest.Builder(context) .data(item.coverUrl) .size(coverSize) + .tag(item.source) .transformations(transformation) .build(), ).requireBitmap() From 9b290bea40bf4f49b384d6e1e6aa155355b7b293 Mon Sep 17 00:00:00 2001 From: Zakhar Timoshenko Date: Sun, 5 Feb 2023 18:30:28 +0300 Subject: [PATCH 2/8] Change user agent to Chrome --- .../java/org/koitharu/kotatsu/browser/BrowserActivity.kt | 2 +- .../koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt | 2 +- .../koitharu/kotatsu/core/network/UserAgentInterceptor.kt | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/BrowserActivity.kt b/app/src/main/java/org/koitharu/kotatsu/browser/BrowserActivity.kt index 81968d794..5ea45d36b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/BrowserActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/BrowserActivity.kt @@ -31,7 +31,7 @@ class BrowserActivity : BaseActivity(), BrowserCallback } with(binding.webView.settings) { javaScriptEnabled = true - userAgentString = UserAgentInterceptor.userAgent + userAgentString = UserAgentInterceptor.userAgentChrome } binding.webView.webViewClient = BrowserClient(this) binding.webView.webChromeClient = ProgressChromeClient(binding.progressBar) diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt index c2359ad91..b95eeac99 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt @@ -42,7 +42,7 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud cacheMode = WebSettings.LOAD_DEFAULT domStorageEnabled = true databaseEnabled = true - userAgentString = UserAgentInterceptor.userAgent + userAgentString = UserAgentInterceptor.userAgentChrome } binding.webView.webViewClient = CloudFlareClient(cookieJar, this, url.orEmpty()) CookieManager.getInstance().setAcceptThirdPartyCookies(binding.webView, true) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt index b6491f154..6a781fd45 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt @@ -13,7 +13,7 @@ class UserAgentInterceptor : Interceptor { return chain.proceed( if (request.header(CommonHeaders.USER_AGENT) == null) { request.newBuilder() - .addHeader(CommonHeaders.USER_AGENT, userAgent) + .addHeader(CommonHeaders.USER_AGENT, userAgentChrome) .build() } else request ) @@ -29,7 +29,7 @@ class UserAgentInterceptor : Interceptor { Build.BRAND, Build.DEVICE, Locale.getDefault().language - ) + ) // TODO Decide what to do with this afterwards val userAgentChrome get() = ( @@ -40,4 +40,4 @@ class UserAgentInterceptor : Interceptor { Build.MODEL, ) } -} \ No newline at end of file +} From 0f4de329e577e8add1be92d66638335651da4dd0 Mon Sep 17 00:00:00 2001 From: Zakhar Timoshenko Date: Sun, 5 Feb 2023 18:30:42 +0300 Subject: [PATCH 3/8] Update parsers --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 3fffce4e9..33b590145 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,7 +84,7 @@ afterEvaluate { } } dependencies { - implementation('com.github.KotatsuApp:kotatsu-parsers:7f630184c0') { + implementation('com.github.KotatsuApp:kotatsu-parsers:c28e2a72d5') { exclude group: 'org.json', module: 'json' } From 61b7117b97f5f5fc53ae68d1fde04e06bcbdddac Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 6 Feb 2023 19:45:55 +0200 Subject: [PATCH 4/8] Allow to use own UserAgent for each manga source --- app/build.gradle | 2 +- .../bookmarks/ui/adapter/BookmarkListAD.kt | 2 +- .../bookmarks/ui/adapter/BookmarksGroupAD.kt | 8 +++-- .../browser/cloudflare/CloudFlareDialog.kt | 10 ++++-- .../org/koitharu/kotatsu/core/AppModule.kt | 3 +- .../CloudFlareProtectedException.kt | 6 ++-- .../exceptions/resolve/ExceptionResolver.kt | 7 ++-- .../core/network/CloudFlareInterceptor.kt | 10 ++++-- .../core/network/UserAgentInterceptor.kt | 32 ++++++++++++++----- .../kotatsu/core/os/ShortcutsUpdater.kt | 1 + .../core/parser/RemoteMangaRepository.kt | 4 +++ .../core/parser/favicon/FaviconFetcher.kt | 7 ++-- .../kotatsu/details/ui/DetailsFragment.kt | 7 +++- .../details/ui/scrobbling/ScrobblingInfoAD.kt | 2 +- .../scrobbling/ScrobblingInfoBottomSheet.kt | 18 +++++------ .../download/domain/DownloadManager.kt | 15 +++++++-- .../kotatsu/download/ui/DownloadItemAD.kt | 14 ++++++-- .../ui/categories/adapter/CategoryAD.kt | 10 ++++-- .../kotatsu/image/ui/ImageActivity.kt | 9 ++++-- .../kotatsu/list/ui/MangaListFragment.kt | 2 +- .../list/ui/adapter/MangaGridItemAD.kt | 2 +- .../ui/adapter/MangaListDetailedItemAD.kt | 2 +- .../list/ui/adapter/MangaListItemAD.kt | 8 +++-- .../kotatsu/list/ui/model/MangaItemModel.kt | 6 +++- .../kotatsu/local/ui/ImportService.kt | 6 +++- .../kotatsu/reader/domain/PageLoader.kt | 1 + .../colorfilter/ColorFilterConfigActivity.kt | 12 +++++-- .../ui/thumbnails/adapter/PageThumbnailAD.kt | 9 ++++-- .../adapter/SearchSuggestionSourceAD.kt | 6 ++-- .../adapter/SearchSuggestionsMangaListAD.kt | 2 +- .../adapter/SourceConfigAdapterDelegates.kt | 14 ++++---- .../sources/auth/SourceAuthActivity.kt | 10 +++--- .../tracker/ui/feed/adapter/FeedItemAD.kt | 2 +- .../kotatsu/tracker/work/TrackWorker.kt | 6 +++- .../org/koitharu/kotatsu/utils/ext/CoilExt.kt | 5 ++- .../widget/recent/RecentListFactory.kt | 1 + .../kotatsu/widget/shelf/ShelfListFactory.kt | 1 + 37 files changed, 186 insertions(+), 76 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 33b590145..ee7ed7ef9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,7 +84,7 @@ afterEvaluate { } } dependencies { - implementation('com.github.KotatsuApp:kotatsu-parsers:c28e2a72d5') { + implementation('com.github.KotatsuApp:kotatsu-parsers:00abaea324') { exclude group: 'org.json', module: 'json' } diff --git a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt index 79ce1e06b..6b3c383f4 100644 --- a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt @@ -26,7 +26,7 @@ fun bookmarkListAD( binding.root.setOnLongClickListener(listener) bind { - binding.imageViewThumb.newImageRequest(item.imageUrl)?.run { + binding.imageViewThumb.newImageRequest(item.imageUrl, item.manga.source)?.run { referer(item.manga.publicUrl) placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksGroupAD.kt b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksGroupAD.kt index 7927ed287..c3beeb87b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksGroupAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksGroupAD.kt @@ -14,7 +14,11 @@ import org.koitharu.kotatsu.bookmarks.ui.model.BookmarksGroup import org.koitharu.kotatsu.databinding.ItemBookmarksGroupBinding import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.utils.ext.* +import org.koitharu.kotatsu.utils.ext.clearItemDecorations +import org.koitharu.kotatsu.utils.ext.disposeImageRequest +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.newImageRequest +import org.koitharu.kotatsu.utils.ext.referer fun bookmarksGroupAD( coil: ImageLoader, @@ -45,7 +49,7 @@ fun bookmarksGroupAD( binding.recyclerView.addItemDecoration(spacingDecoration) selectionController.attachToRecyclerView(item.manga, binding.recyclerView) } - binding.imageViewCover.newImageRequest(item.manga.coverUrl)?.run { + binding.imageViewCover.newImageRequest(item.manga.coverUrl, item.manga.source)?.run { referer(item.manga.publicUrl) placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt index b95eeac99..2b4ed813c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt @@ -12,7 +12,9 @@ import androidx.core.view.isInvisible import androidx.fragment.app.setFragmentResult import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint +import okhttp3.Headers import org.koitharu.kotatsu.base.ui.AlertDialogFragment +import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.network.UserAgentInterceptor import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar import org.koitharu.kotatsu.databinding.FragmentCloudflareBinding @@ -42,7 +44,7 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud cacheMode = WebSettings.LOAD_DEFAULT domStorageEnabled = true databaseEnabled = true - userAgentString = UserAgentInterceptor.userAgentChrome + userAgentString = arguments?.getString(ARG_UA) ?: UserAgentInterceptor.userAgentChrome } binding.webView.webViewClient = CloudFlareClient(cookieJar, this, url.orEmpty()) CookieManager.getInstance().setAcceptThirdPartyCookies(binding.webView, true) @@ -92,9 +94,13 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud const val TAG = "CloudFlareDialog" const val EXTRA_RESULT = "result" private const val ARG_URL = "url" + private const val ARG_UA = "ua" - fun newInstance(url: String) = CloudFlareDialog().withArgs(1) { + fun newInstance(url: String, headers: Headers?) = CloudFlareDialog().withArgs(2) { putString(ARG_URL, url) + headers?.get(CommonHeaders.USER_AGENT)?.let { + putString(ARG_UA, it) + } } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt b/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt index 4ee31403a..8dfb84726 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt @@ -85,6 +85,7 @@ interface AppModule { @Singleton fun provideOkHttpClient( localStorageManager: LocalStorageManager, + userAgentInterceptor: UserAgentInterceptor, cookieJar: CookieJar, settings: AppSettings, ): OkHttpClient { @@ -97,7 +98,7 @@ interface AppModule { dns(DoHManager(cache, settings)) cache(cache) addInterceptor(GZipInterceptor()) - addInterceptor(UserAgentInterceptor()) + addInterceptor(userAgentInterceptor) addInterceptor(CloudFlareInterceptor()) }.build() } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt index ef20b4fb0..ee6163e25 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt @@ -1,7 +1,9 @@ package org.koitharu.kotatsu.core.exceptions +import okhttp3.Headers import okio.IOException class CloudFlareProtectedException( - val url: String -) : IOException("Protected by CloudFlare") \ No newline at end of file + val url: String, + val headers: Headers, +) : IOException("Protected by CloudFlare") diff --git a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt index b7147aadd..4afa01e5d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt @@ -7,6 +7,7 @@ import androidx.collection.ArrayMap import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import kotlinx.coroutines.suspendCancellableCoroutine +import okhttp3.Headers import org.koitharu.kotatsu.R import org.koitharu.kotatsu.browser.BrowserActivity import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog @@ -43,7 +44,7 @@ class ExceptionResolver private constructor( } suspend fun resolve(e: Throwable): Boolean = when (e) { - is CloudFlareProtectedException -> resolveCF(e.url) + is CloudFlareProtectedException -> resolveCF(e.url, e.headers) is AuthRequiredException -> resolveAuthException(e.source) is NotFoundException -> { openInBrowser(e.url) @@ -53,8 +54,8 @@ class ExceptionResolver private constructor( else -> false } - private suspend fun resolveCF(url: String): Boolean { - val dialog = CloudFlareDialog.newInstance(url) + private suspend fun resolveCF(url: String, headers: Headers): Boolean { + val dialog = CloudFlareDialog.newInstance(url, headers) val fm = getFragmentManager() return suspendCancellableCoroutine { cont -> fm.clearFragmentResult(CloudFlareDialog.TAG) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt index a32a94c83..b4dcc9e62 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt @@ -13,13 +13,17 @@ private const val SERVER_CLOUDFLARE = "cloudflare" class CloudFlareInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { - val response = chain.proceed(chain.request()) + val request = chain.request() + val response = chain.proceed(request) if (response.code == HTTP_FORBIDDEN || response.code == HTTP_UNAVAILABLE) { if (response.header(HEADER_SERVER)?.startsWith(SERVER_CLOUDFLARE) == true) { response.closeQuietly() - throw CloudFlareProtectedException(response.request.url.toString()) + throw CloudFlareProtectedException( + url = response.request.url.toString(), + headers = request.headers, + ) } } return response } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt index 6a781fd45..a0b46d309 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt @@ -1,24 +1,40 @@ package org.koitharu.kotatsu.core.network import android.os.Build -import java.util.* +import dagger.Lazy import okhttp3.Interceptor +import okhttp3.Request import okhttp3.Response import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.core.parser.RemoteMangaRepository +import org.koitharu.kotatsu.parsers.model.MangaSource +import java.util.Locale +import javax.inject.Inject +import javax.inject.Singleton -class UserAgentInterceptor : Interceptor { +@Singleton +class UserAgentInterceptor @Inject constructor( + private val mangaRepositoryFactoryLazy: Lazy, +) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() return chain.proceed( if (request.header(CommonHeaders.USER_AGENT) == null) { request.newBuilder() - .addHeader(CommonHeaders.USER_AGENT, userAgentChrome) + .addHeader(CommonHeaders.USER_AGENT, getUserAgent(request)) .build() - } else request + } else request, ) } + private fun getUserAgent(request: Request): String { + val source = request.tag(MangaSource::class.java) ?: return userAgent + val repository = mangaRepositoryFactoryLazy.get().create(source) as? RemoteMangaRepository + return repository?.userAgent ?: userAgent + } + companion object { val userAgent @@ -28,7 +44,7 @@ class UserAgentInterceptor : Interceptor { Build.MODEL, Build.BRAND, Build.DEVICE, - Locale.getDefault().language + Locale.getDefault().language, ) // TODO Decide what to do with this afterwards val userAgentChrome @@ -36,8 +52,8 @@ class UserAgentInterceptor : Interceptor { "Mozilla/5.0 (Linux; Android %s; %s) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/100.0.4896.127 Mobile Safari/537.36" ).format( - Build.VERSION.RELEASE, - Build.MODEL, - ) + Build.VERSION.RELEASE, + Build.MODEL, + ) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/os/ShortcutsUpdater.kt b/app/src/main/java/org/koitharu/kotatsu/core/os/ShortcutsUpdater.kt index 82d7fa4bb..39708b1bd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/os/ShortcutsUpdater.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/os/ShortcutsUpdater.kt @@ -118,6 +118,7 @@ class ShortcutsUpdater @Inject constructor( ImageRequest.Builder(context) .data(manga.coverUrl) .size(iconSize.width, iconSize.height) + .tag(manga.source) .precision(Precision.EXACT) .scale(Scale.FILL) .build(), diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt index ecd2d170f..ba4da8fa9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.currentCoroutineContext import org.koitharu.kotatsu.core.cache.ContentCache import org.koitharu.kotatsu.core.cache.SafeDeferred +import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.prefs.SourceSettings import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParserAuthProvider @@ -39,6 +40,9 @@ class RemoteMangaRepository( getConfig().defaultSortOrder = value } + val userAgent: String? + get() = parser.headers?.get(CommonHeaders.USER_AGENT) + override suspend fun getList(offset: Int, query: String): List { return parser.getList(offset, query) } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt index ee052cfdd..0fdd23162 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt @@ -53,7 +53,7 @@ class FaviconFetcher( options.size.height.pxOrElse { FALLBACK_SIZE }, ) val icon = checkNotNull(favicons.find(sizePx)) { "No favicons found" } - val response = loadIcon(icon.url, favicons.referer) + val response = loadIcon(icon.url, repo.userAgent, favicons.referer) val responseBody = response.requireBody() val source = writeToDiskCache(responseBody)?.toImageSource() ?: responseBody.toImageSource() return SourceResult( @@ -63,11 +63,14 @@ class FaviconFetcher( ) } - private suspend fun loadIcon(url: String, referer: String): Response { + private suspend fun loadIcon(url: String, userAgent: String?, referer: String): Response { val request = Request.Builder() .url(url) .get() .header(CommonHeaders.REFERER, referer) + if (userAgent != null) { + request.header(CommonHeaders.USER_AGENT, userAgent) + } @Suppress("UNCHECKED_CAST") options.tags.asMap().forEach { request.tag(it.key as Class, it.value) } val response = okHttpClient.newCall(request.build()).await() diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt index 6d2203632..5211d9320 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt @@ -254,7 +254,11 @@ class DetailsFragment : R.id.imageView_cover -> { startActivity( - ImageActivity.newIntent(v.context, manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }), + ImageActivity.newIntent( + v.context, + manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }, + manga.source, + ), scaleUpActivityOptionsOf(v).toBundle(), ) } @@ -337,6 +341,7 @@ class DetailsFragment : .target(binding.imageViewCover) .size(CoverSizeResolver(binding.imageViewCover)) .data(imageUrl) + .tag(manga.source) .crossfade(context) .referer(manga.publicUrl) .lifecycle(viewLifecycleOwner) diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt index 7c3af4dc8..848e73361 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt @@ -23,7 +23,7 @@ fun scrobblingInfoAD( } bind { - binding.imageViewCover.newImageRequest(item.coverUrl)?.run { + binding.imageViewCover.newImageRequest(item.coverUrl /* TODO */, null)?.run { placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) error(R.drawable.ic_error_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoBottomSheet.kt index f5f8510e5..7b2dcbd70 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoBottomSheet.kt @@ -100,7 +100,7 @@ class ScrobblingInfoBottomSheet : R.id.imageView_cover -> { val coverUrl = viewModel.scrobblingInfo.value?.getOrNull(scrobblerIndex)?.coverUrl ?: return val options = scaleUpActivityOptionsOf(v) - startActivity(ImageActivity.newIntent(v.context, coverUrl), options.toBundle()) + startActivity(ImageActivity.newIntent(v.context, coverUrl, null), options.toBundle()) } } } @@ -115,15 +115,13 @@ class ScrobblingInfoBottomSheet : binding.ratingBar.rating = scrobbling.rating * binding.ratingBar.numStars binding.textViewDescription.text = scrobbling.description binding.spinnerStatus.setSelection(scrobbling.status?.ordinal ?: -1) - ImageRequest.Builder(context ?: return) - .target(binding.imageViewCover) - .data(scrobbling.coverUrl) - .crossfade(context) - .lifecycle(viewLifecycleOwner) - .placeholder(R.drawable.ic_placeholder) - .fallback(R.drawable.ic_placeholder) - .error(R.drawable.ic_error_placeholder) - .enqueueWith(coil) + binding.imageViewCover.newImageRequest(scrobbling.coverUrl)?.apply { + lifecycle(viewLifecycleOwner) + placeholder(R.drawable.ic_placeholder) + fallback(R.drawable.ic_placeholder) + error(R.drawable.ic_error_placeholder) + enqueueWith(coil) + } } override fun onMenuItemClick(item: MenuItem): Boolean { 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 931d09ae9..e534f9120 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 @@ -118,7 +118,7 @@ class DownloadManager @AssistedInject constructor( val data = if (manga.chapters.isNullOrEmpty()) repo.getDetails(manga) else manga output = CbzMangaOutput.get(destination, data) val coverUrl = data.largeCoverUrl ?: data.coverUrl - downloadFile(coverUrl, data.publicUrl, destination, tempFileName).let { file -> + downloadFile(coverUrl, data.publicUrl, destination, tempFileName, repo.source).let { file -> output.addCover(file, MimeTypeMap.getFileExtensionFromUrl(coverUrl)) } val chapters = checkNotNull( @@ -139,7 +139,8 @@ class DownloadManager @AssistedInject constructor( for ((pageIndex, page) in pages.withIndex()) { runFailsafe(outState, pausingHandle) { val url = repo.getPageUrl(page) - val file = cache[url] ?: downloadFile(url, page.referer, destination, tempFileName) + val file = cache[url] + ?: downloadFile(url, page.referer, destination, tempFileName, repo.source) output.addPage( chapter = chapter, file = file, @@ -209,10 +210,17 @@ class DownloadManager @AssistedInject constructor( } } - private suspend fun downloadFile(url: String, referer: String, destination: File, tempFileName: String): File { + private suspend fun downloadFile( + url: String, + referer: String, + destination: File, + tempFileName: String, + source: MangaSource, + ): File { val request = Request.Builder() .url(url) .header(CommonHeaders.REFERER, referer) + .tag(source) .cacheControl(CommonHeaders.CACHE_CONTROL_DISABLED) .get() .build() @@ -243,6 +251,7 @@ class DownloadManager @AssistedInject constructor( ImageRequest.Builder(context) .data(manga.coverUrl) .referer(manga.publicUrl) + .tag(manga.source) .size(coverWidth, coverHeight) .scale(Scale.FILL) .build(), diff --git a/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadItemAD.kt index 4d24c9104..81554a088 100644 --- a/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadItemAD.kt @@ -13,7 +13,11 @@ import org.koitharu.kotatsu.databinding.ItemDownloadBinding import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.download.domain.DownloadState import org.koitharu.kotatsu.parsers.util.format -import org.koitharu.kotatsu.utils.ext.* +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.getDisplayMessage +import org.koitharu.kotatsu.utils.ext.newImageRequest +import org.koitharu.kotatsu.utils.ext.onFirst +import org.koitharu.kotatsu.utils.ext.referer fun downloadItemAD( scope: CoroutineScope, @@ -40,7 +44,7 @@ fun downloadItemAD( bind { job?.cancel() job = item.progressAsFlow().onFirst { state -> - binding.imageViewCover.newImageRequest(state.manga.coverUrl)?.run { + binding.imageViewCover.newImageRequest(state.manga.coverUrl, state.manga.source)?.run { referer(state.manga.publicUrl) placeholder(state.cover) fallback(R.drawable.ic_placeholder) @@ -60,6 +64,7 @@ fun downloadItemAD( binding.buttonCancel.isVisible = false binding.buttonResume.isVisible = false } + is DownloadState.Done -> { binding.textViewStatus.setText(R.string.download_complete) binding.progressBar.isIndeterminate = false @@ -69,6 +74,7 @@ fun downloadItemAD( binding.buttonCancel.isVisible = false binding.buttonResume.isVisible = false } + is DownloadState.Error -> { binding.textViewStatus.setText(R.string.error_occurred) binding.progressBar.isIndeterminate = false @@ -79,6 +85,7 @@ fun downloadItemAD( binding.buttonCancel.isVisible = state.canRetry binding.buttonResume.isVisible = state.canRetry } + is DownloadState.PostProcessing -> { binding.textViewStatus.setText(R.string.processing_) binding.progressBar.isIndeterminate = true @@ -88,6 +95,7 @@ fun downloadItemAD( binding.buttonCancel.isVisible = false binding.buttonResume.isVisible = false } + is DownloadState.Preparing -> { binding.textViewStatus.setText(R.string.preparing_) binding.progressBar.isIndeterminate = true @@ -97,6 +105,7 @@ fun downloadItemAD( binding.buttonCancel.isVisible = true binding.buttonResume.isVisible = false } + is DownloadState.Progress -> { binding.textViewStatus.setText(R.string.manga_downloading_) binding.progressBar.isIndeterminate = false @@ -109,6 +118,7 @@ fun downloadItemAD( binding.buttonCancel.isVisible = true binding.buttonResume.isVisible = false } + is DownloadState.Queued -> { binding.textViewStatus.setText(R.string.queued) binding.progressBar.isIndeterminate = false diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt index db3ba22be..8c633bcfb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt @@ -5,7 +5,9 @@ import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.view.MotionEvent import android.view.View -import android.view.View.* +import android.view.View.OnClickListener +import android.view.View.OnLongClickListener +import android.view.View.OnTouchListener import androidx.core.graphics.ColorUtils import androidx.core.view.isVisible import androidx.core.widget.ImageViewCompat @@ -16,7 +18,11 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.databinding.ItemCategoryBinding import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesListListener import org.koitharu.kotatsu.list.ui.model.ListModel -import org.koitharu.kotatsu.utils.ext.* +import org.koitharu.kotatsu.utils.ext.animatorDurationScale +import org.koitharu.kotatsu.utils.ext.disposeImageRequest +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.getThemeColor +import org.koitharu.kotatsu.utils.ext.newImageRequest fun categoryAD( coil: ImageLoader, diff --git a/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt b/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt index c8e5300a2..0fe13a2d3 100644 --- a/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt @@ -17,11 +17,12 @@ import coil.target.ViewTarget import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivityImageBinding +import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.indicator +import javax.inject.Inject @AndroidEntryPoint class ImageActivity : BaseActivity() { @@ -56,6 +57,7 @@ class ImageActivity : BaseActivity() { .data(url) .memoryCachePolicy(CachePolicy.DISABLED) .lifecycle(this) + .tag(intent.getSerializableExtra(EXTRA_SOURCE) as? MangaSource) .target(SsivTarget(binding.ssiv)) .indicator(binding.progressBar) .enqueueWith(coil) @@ -88,9 +90,12 @@ class ImageActivity : BaseActivity() { companion object { - fun newIntent(context: Context, url: String): Intent { + private const val EXTRA_SOURCE = "source" + + fun newIntent(context: Context, url: String, source: MangaSource?): Intent { return Intent(context, ImageActivity::class.java) .setData(Uri.parse(url)) + .putExtra(EXTRA_SOURCE, source) } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index 01c09394e..ba9ccf454 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -177,7 +177,7 @@ abstract class MangaListFragment : private fun onError(e: Throwable) { if (e is CloudFlareProtectedException) { - CloudFlareDialog.newInstance(e.url).show(childFragmentManager, CloudFlareDialog.TAG) + CloudFlareDialog.newInstance(e.url, e.headers).show(childFragmentManager, CloudFlareDialog.TAG) } else { Snackbar.make( binding.recyclerView, diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt index 7484c6166..6e65e6f83 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt @@ -39,7 +39,7 @@ fun mangaGridItemAD( bind { payloads -> binding.textViewTitle.text = item.title binding.progressView.setPercent(item.progress, MangaListAdapter.PAYLOAD_PROGRESS in payloads) - binding.imageViewCover.newImageRequest(item.coverUrl)?.run { + binding.imageViewCover.newImageRequest(item.coverUrl, item.source)?.run { referer(item.manga.publicUrl) size(CoverSizeResolver(binding.imageViewCover)) placeholder(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt index 1caac32e5..6f7412361 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt @@ -52,7 +52,7 @@ fun mangaListDetailedItemAD( binding.textViewTitle.text = item.title binding.textViewSubtitle.textAndVisible = item.subtitle binding.progressView.setPercent(item.progress, MangaListAdapter.PAYLOAD_PROGRESS in payloads) - binding.imageViewCover.newImageRequest(item.coverUrl)?.run { + binding.imageViewCover.newImageRequest(item.coverUrl, item.source)?.run { referer(item.manga.publicUrl) size(CoverSizeResolver(binding.imageViewCover)) placeholder(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt index 36aa1ada0..dea006ed6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt @@ -10,7 +10,11 @@ import org.koitharu.kotatsu.databinding.ItemMangaListBinding import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.MangaListModel import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.utils.ext.* +import org.koitharu.kotatsu.utils.ext.disposeImageRequest +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.newImageRequest +import org.koitharu.kotatsu.utils.ext.referer +import org.koitharu.kotatsu.utils.ext.textAndVisible fun mangaListItemAD( coil: ImageLoader, @@ -31,7 +35,7 @@ fun mangaListItemAD( bind { binding.textViewTitle.text = item.title binding.textViewSubtitle.textAndVisible = item.subtitle - binding.imageViewCover.newImageRequest(item.coverUrl)?.run { + binding.imageViewCover.newImageRequest(item.coverUrl, item.source)?.run { referer(item.manga.publicUrl) placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/MangaItemModel.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/MangaItemModel.kt index ce5145d26..2d4710a70 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/MangaItemModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/MangaItemModel.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.list.ui.model import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaSource sealed interface MangaItemModel : ListModel { @@ -10,4 +11,7 @@ sealed interface MangaItemModel : ListModel { val coverUrl: String val counter: Int val progress: Float -} \ No newline at end of file + + val source: MangaSource + get() = manga.source +} diff --git a/app/src/main/java/org/koitharu/kotatsu/local/ui/ImportService.kt b/app/src/main/java/org/koitharu/kotatsu/local/ui/ImportService.kt index 1ae9dc6c7..ae7d5aa60 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/ui/ImportService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/ui/ImportService.kt @@ -99,7 +99,11 @@ class ImportService : CoroutineIntentService() { if (manga != null) { notification.setLargeIcon( coil.execute( - ImageRequest.Builder(applicationContext).data(manga.coverUrl).referer(manga.publicUrl).build(), + ImageRequest.Builder(applicationContext) + .data(manga.coverUrl) + .tag(manga.source) + .referer(manga.publicUrl) + .build(), ).toBitmapOrNull(), ) notification.setSubText(manga.title) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt b/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt index 0ac53f720..f90f8397b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt @@ -191,6 +191,7 @@ class PageLoader @Inject constructor( .header(CommonHeaders.REFERER, page.referer) .header(CommonHeaders.ACCEPT, "image/webp,image/png;q=0.9,image/jpeg,*/*;q=0.8") .cacheControl(CommonHeaders.CACHE_CONTROL_DISABLED) + .tag(page.source) .build() okHttp.newCall(request).await().use { response -> check(response.isSuccessful) { diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt index ee94a4f87..2696f393f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt @@ -13,11 +13,9 @@ import coil.ImageLoader import coil.request.ImageRequest import coil.size.Scale import coil.size.ViewSizeResolver -import com.google.android.material.R as materialR import com.google.android.material.slider.LabelFormatter import com.google.android.material.slider.Slider import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga @@ -27,7 +25,14 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.util.format import org.koitharu.kotatsu.reader.domain.ReaderColorFilter -import org.koitharu.kotatsu.utils.ext.* +import org.koitharu.kotatsu.utils.ext.assistedViewModels +import org.koitharu.kotatsu.utils.ext.decodeRegion +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.getParcelableExtraCompat +import org.koitharu.kotatsu.utils.ext.referer +import org.koitharu.kotatsu.utils.ext.setValueRounded +import javax.inject.Inject +import com.google.android.material.R as materialR @AndroidEntryPoint class ColorFilterConfigActivity : @@ -115,6 +120,7 @@ class ColorFilterConfigActivity : .referer(preview.referer) .scale(Scale.FILL) .decodeRegion() + .tag(preview.source) .error(R.drawable.ic_error_placeholder) .size(ViewSizeResolver(binding.imageViewBefore)) .allowRgb565(false) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/adapter/PageThumbnailAD.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/adapter/PageThumbnailAD.kt index d992f747a..fd881846f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/adapter/PageThumbnailAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/adapter/PageThumbnailAD.kt @@ -5,9 +5,12 @@ import coil.ImageLoader import coil.request.ImageRequest import coil.size.Scale import coil.size.Size -import com.google.android.material.R as materialR import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.databinding.ItemPageThumbBinding @@ -19,6 +22,7 @@ import org.koitharu.kotatsu.utils.ext.isLowRamDevice import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.runCatchingCancellable import org.koitharu.kotatsu.utils.ext.setTextColorAttr +import com.google.android.material.R as materialR fun pageThumbnailAD( coil: ImageLoader, @@ -41,6 +45,7 @@ fun pageThumbnailAD( ImageRequest.Builder(context) .data(url) .referer(item.page.referer) + .tag(item.page.source) .size(thumbSize) .scale(Scale.FILL) .allowRgb565(true) diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt index 5367bcbc3..086d52f39 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt @@ -17,7 +17,7 @@ fun searchSuggestionSourceAD( lifecycleOwner: LifecycleOwner, listener: SearchSuggestionListener, ) = adapterDelegateViewBinding( - { inflater, parent -> ItemSearchSuggestionSourceBinding.inflate(inflater, parent, false) } + { inflater, parent -> ItemSearchSuggestionSourceBinding.inflate(inflater, parent, false) }, ) { binding.switchLocal.setOnCheckedChangeListener { _, isChecked -> @@ -31,7 +31,7 @@ fun searchSuggestionSourceAD( binding.textViewTitle.text = item.source.title binding.switchLocal.isChecked = item.isEnabled val fallbackIcon = FaviconFallbackDrawable(context, item.source.name) - binding.imageViewCover.newImageRequest(item.source.faviconUri())?.run { + binding.imageViewCover.newImageRequest(item.source.faviconUri(), item.source)?.run { fallback(fallbackIcon) placeholder(fallbackIcon) error(fallbackIcon) @@ -43,4 +43,4 @@ fun searchSuggestionSourceAD( onViewRecycled { binding.imageViewCover.disposeImageRequest() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionsMangaListAD.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionsMangaListAD.kt index 7c108274a..b451a7997 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionsMangaListAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionsMangaListAD.kt @@ -55,7 +55,7 @@ private fun searchSuggestionMangaGridAD( } bind { - binding.imageViewCover.newImageRequest(item.coverUrl)?.run { + binding.imageViewCover.newImageRequest(item.coverUrl, item.source)?.run { placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) error(R.drawable.ic_error_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt b/app/src/main/java/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt index 299f35ad7..cd0471af2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt @@ -20,7 +20,7 @@ import org.koitharu.kotatsu.utils.image.FaviconFallbackDrawable fun sourceConfigHeaderDelegate() = adapterDelegateViewBinding( - { layoutInflater, parent -> ItemFilterHeaderBinding.inflate(layoutInflater, parent, false) } + { layoutInflater, parent -> ItemFilterHeaderBinding.inflate(layoutInflater, parent, false) }, ) { bind { @@ -31,7 +31,7 @@ fun sourceConfigHeaderDelegate() = fun sourceConfigGroupDelegate( listener: SourceConfigListener, ) = adapterDelegateViewBinding( - { layoutInflater, parent -> ItemExpandableBinding.inflate(layoutInflater, parent, false) } + { layoutInflater, parent -> ItemExpandableBinding.inflate(layoutInflater, parent, false) }, ) { binding.root.setOnClickListener { @@ -50,7 +50,7 @@ fun sourceConfigItemDelegate( lifecycleOwner: LifecycleOwner, ) = adapterDelegateViewBinding( { layoutInflater, parent -> ItemSourceConfigBinding.inflate(layoutInflater, parent, false) }, - on = { item, _, _ -> item is SourceConfigItem.SourceItem && !item.isDraggable } + on = { item, _, _ -> item is SourceConfigItem.SourceItem && !item.isDraggable }, ) { binding.switchToggle.setOnCheckedChangeListener { _, isChecked -> @@ -62,7 +62,7 @@ fun sourceConfigItemDelegate( binding.switchToggle.isChecked = item.isEnabled binding.textViewDescription.textAndVisible = item.summary val fallbackIcon = FaviconFallbackDrawable(context, item.source.name) - binding.imageViewIcon.newImageRequest(item.source.faviconUri())?.run { + binding.imageViewIcon.newImageRequest(item.source.faviconUri(), item.source)?.run { crossfade(context) error(fallbackIcon) placeholder(fallbackIcon) @@ -82,7 +82,7 @@ fun sourceConfigDraggableItemDelegate( listener: SourceConfigListener, ) = adapterDelegateViewBinding( { layoutInflater, parent -> ItemSourceConfigDraggableBinding.inflate(layoutInflater, parent, false) }, - on = { item, _, _ -> item is SourceConfigItem.SourceItem && item.isDraggable } + on = { item, _, _ -> item is SourceConfigItem.SourceItem && item.isDraggable }, ) { val eventListener = object : @@ -117,5 +117,5 @@ fun sourceConfigDraggableItemDelegate( } fun sourceConfigEmptySearchDelegate() = adapterDelegate( - R.layout.item_sources_empty -) { } \ No newline at end of file + R.layout.item_sources_empty, +) { } diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt b/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt index 4cac8671f..f9ce14a07 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt @@ -11,9 +11,7 @@ import androidx.activity.result.contract.ActivityResultContract import androidx.core.graphics.Insets import androidx.core.view.isVisible import androidx.core.view.updatePadding -import com.google.android.material.R as materialR import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.browser.BrowserCallback @@ -26,6 +24,8 @@ import org.koitharu.kotatsu.databinding.ActivityBrowserBinding import org.koitharu.kotatsu.parsers.MangaParserAuthProvider import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.TaggedActivityResult +import javax.inject.Inject +import com.google.android.material.R as materialR @AndroidEntryPoint class SourceAuthActivity : BaseActivity(), BrowserCallback { @@ -44,7 +44,8 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba finishAfterTransition() return } - authProvider = (mangaRepositoryFactory.create(source) as? RemoteMangaRepository)?.getAuthProvider() ?: run { + val repository = mangaRepositoryFactory.create(source) as? RemoteMangaRepository + authProvider = (repository)?.getAuthProvider() ?: run { Toast.makeText( this, getString(R.string.auth_not_supported_by, source.title), @@ -59,7 +60,7 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba } with(binding.webView.settings) { javaScriptEnabled = true - userAgentString = UserAgentInterceptor.userAgentChrome + userAgentString = repository.userAgent ?: UserAgentInterceptor.userAgent } binding.webView.webViewClient = BrowserClient(this) binding.webView.webChromeClient = ProgressChromeClient(binding.progressBar) @@ -96,6 +97,7 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba finishAfterTransition() true } + else -> super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedItemAD.kt index b98b25e17..901e4573a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedItemAD.kt @@ -28,7 +28,7 @@ fun feedItemAD( bind { binding.textViewTitle.isBold = item.isNew binding.textViewSummary.isBold = item.isNew - binding.imageViewCover.newImageRequest(item.imageUrl)?.run { + binding.imageViewCover.newImageRequest(item.imageUrl, item.manga.source)?.run { placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) error(R.drawable.ic_error_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt index fa488b146..4aa0b5a86 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt @@ -155,7 +155,11 @@ class TrackWorker @AssistedInject constructor( setNumber(newChapters.size) setLargeIcon( coil.execute( - ImageRequest.Builder(applicationContext).data(manga.coverUrl).referer(manga.publicUrl).build(), + ImageRequest.Builder(applicationContext) + .data(manga.coverUrl) + .referer(manga.publicUrl) + .tag(manga.source) + .build(), ).toBitmapOrNull(), ) setSmallIcon(R.drawable.ic_stat_book_plus) diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt index 914947ba2..28716c743 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt @@ -13,16 +13,18 @@ import com.google.android.material.progressindicator.BaseProgressIndicator import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.network.CommonHeaders +import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.image.RegionBitmapDecoder import org.koitharu.kotatsu.utils.progress.ImageRequestIndicatorListener -fun ImageView.newImageRequest(url: Any?): ImageRequest.Builder? { +fun ImageView.newImageRequest(url: Any?, mangaSource: MangaSource? = null): ImageRequest.Builder? { val current = CoilUtils.result(this) if (current != null && current.request.data == url) { return null } return ImageRequest.Builder(context) .data(url) + .tag(mangaSource) .crossfade(context) .target(this) } @@ -45,6 +47,7 @@ fun ImageResult.toBitmapOrNull() = when (this) { } catch (_: Throwable) { null } + is ErrorResult -> null } diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt b/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt index 88487c636..c023f9d05 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt @@ -53,6 +53,7 @@ class RecentListFactory( ImageRequest.Builder(context) .data(item.coverUrl) .size(coverSize) + .tag(item.source) .transformations(transformation) .build(), ).requireBitmap() diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt index 6572418d3..f3dd62b87 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt @@ -64,6 +64,7 @@ class ShelfListFactory( ImageRequest.Builder(context) .data(item.coverUrl) .size(coverSize) + .tag(item.source) .transformations(transformation) .build(), ).requireBitmap() From 155af8889b68634d88b37d65613f47b8910e4ee7 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Tue, 7 Feb 2023 07:40:33 +0200 Subject: [PATCH 5/8] Update version --- app/build.gradle | 4 ++-- .../org/koitharu/kotatsu/settings/SourceSettingsFragment.kt | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ee7ed7ef9..d9d1954dc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,8 +15,8 @@ android { applicationId 'org.koitharu.kotatsu' minSdkVersion 21 targetSdkVersion 33 - versionCode 513 - versionName '4.3.2' + versionCode 514 + versionName '4.3.3' generatedDensities = [] testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt index 91561b2cd..dbad1e134 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt @@ -105,6 +105,7 @@ class SourceSettingsFragment : BasePreferenceFragment(0) { } private fun resolveError(error: Throwable) { + view ?: return viewLifecycleScope.launch { if (exceptionResolver.resolve(error)) { val pref = findPreference(KEY_AUTH) ?: return@launch From e0f23d2e6d8045e6f235b07ee3d04669febf9492 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 8 Feb 2023 19:57:17 +0200 Subject: [PATCH 6/8] New headers processing approach --- README.md | 4 +- app/build.gradle | 2 +- .../kotatsu/core/parser/DummyParser.kt | 13 +++- .../bookmarks/ui/adapter/BookmarkListAD.kt | 2 - .../bookmarks/ui/adapter/BookmarksGroupAD.kt | 2 - .../kotatsu/browser/BrowserActivity.kt | 10 +-- .../browser/cloudflare/CloudFlareDialog.kt | 4 +- .../org/koitharu/kotatsu/core/AppModule.kt | 4 +- .../core/network/CommonHeadersInterceptor.kt | 77 +++++++++++++++++++ .../core/network/UserAgentInterceptor.kt | 59 -------------- .../core/parser/RemoteMangaRepository.kt | 22 +++++- .../core/parser/favicon/FaviconFetcher.kt | 10 +-- .../kotatsu/details/ui/DetailsFragment.kt | 2 - .../download/domain/DownloadManager.kt | 4 +- .../kotatsu/download/ui/DownloadItemAD.kt | 2 - .../list/ui/adapter/MangaGridItemAD.kt | 2 - .../ui/adapter/MangaListDetailedItemAD.kt | 2 - .../list/ui/adapter/MangaListItemAD.kt | 2 - .../kotatsu/local/ui/ImportService.kt | 2 - .../kotatsu/reader/domain/PageLoader.kt | 2 +- .../colorfilter/ColorFilterConfigActivity.kt | 2 - .../ui/thumbnails/adapter/PageThumbnailAD.kt | 2 - .../sources/auth/SourceAuthActivity.kt | 6 +- .../kotatsu/tracker/work/TrackWorker.kt | 2 - .../org/koitharu/kotatsu/utils/ext/CoilExt.kt | 25 ------ .../kotatsu/core/parser/DummyParser.kt | 2 +- 26 files changed, 126 insertions(+), 140 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/core/network/CommonHeadersInterceptor.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt diff --git a/README.md b/README.md index 599df4214..4a3f9d719 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Download APK directly from GitHub: ### Main Features -* Online manga catalogues +* Online [manga catalogues](https://github.com/KotatsuApp/kotatsu-parsers) * Search manga by name and genres * Reading history and bookmarks * Favourites organized by user-defined categories @@ -24,7 +24,7 @@ Download APK directly from GitHub: * Tablet-optimized Material You UI * Standard and Webtoon-optimized reader * Notifications about new chapters with updates feed -* Shikimori integration (manga tracking) +* Integration with manga tracking services: Shikimori, AniList, MAL (coming soon) * Password/fingerprint protect access to the app * History and favourites [synchronization](https://github.com/KotatsuApp/kotatsu-syncserver) across devices diff --git a/app/build.gradle b/app/build.gradle index 5a6798798..2ebdec573 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -86,7 +86,7 @@ afterEvaluate { } } dependencies { - implementation('com.github.KotatsuApp:kotatsu-parsers:00abaea324') { + implementation('com.github.KotatsuApp:kotatsu-parsers:05d705ac03') { exclude group: 'org.json', module: 'json' } diff --git a/app/src/debug/java/org/koitharu/kotatsu/core/parser/DummyParser.kt b/app/src/debug/java/org/koitharu/kotatsu/core/parser/DummyParser.kt index 9ebcba9f4..a60655a2a 100644 --- a/app/src/debug/java/org/koitharu/kotatsu/core/parser/DummyParser.kt +++ b/app/src/debug/java/org/koitharu/kotatsu/core/parser/DummyParser.kt @@ -1,15 +1,20 @@ package org.koitharu.kotatsu.core.parser -import java.util.* import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey -import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaChapter +import org.koitharu.kotatsu.parsers.model.MangaPage +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaTag +import org.koitharu.kotatsu.parsers.model.SortOrder +import java.util.EnumSet /** * This parser is just for parser development, it should not be used in releases */ -class DummyParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.DUMMY) { +class DummyParser(context: MangaLoaderContext) : MangaParser(context, MangaSource.DUMMY) { override val configKeyDomain: ConfigKey.Domain get() = ConfigKey.Domain("", null) @@ -37,4 +42,4 @@ class DummyParser(override val context: MangaLoaderContext) : MangaParser(MangaS override suspend fun getTags(): Set { TODO("Not yet implemented") } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt index 6b3c383f4..87ee3cbfc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt @@ -11,7 +11,6 @@ import org.koitharu.kotatsu.databinding.ItemBookmarkBinding import org.koitharu.kotatsu.utils.ext.disposeImageRequest import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.newImageRequest -import org.koitharu.kotatsu.utils.ext.referer fun bookmarkListAD( coil: ImageLoader, @@ -27,7 +26,6 @@ fun bookmarkListAD( bind { binding.imageViewThumb.newImageRequest(item.imageUrl, item.manga.source)?.run { - referer(item.manga.publicUrl) placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) error(R.drawable.ic_error_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksGroupAD.kt b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksGroupAD.kt index c3beeb87b..47f7dd04a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksGroupAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksGroupAD.kt @@ -18,7 +18,6 @@ import org.koitharu.kotatsu.utils.ext.clearItemDecorations import org.koitharu.kotatsu.utils.ext.disposeImageRequest import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.newImageRequest -import org.koitharu.kotatsu.utils.ext.referer fun bookmarksGroupAD( coil: ImageLoader, @@ -50,7 +49,6 @@ fun bookmarksGroupAD( selectionController.attachToRecyclerView(item.manga, binding.recyclerView) } binding.imageViewCover.newImageRequest(item.manga.coverUrl, item.manga.source)?.run { - referer(item.manga.publicUrl) placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) error(R.drawable.ic_error_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/BrowserActivity.kt b/app/src/main/java/org/koitharu/kotatsu/browser/BrowserActivity.kt index 5ea45d36b..27c25f2a4 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/BrowserActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/BrowserActivity.kt @@ -8,16 +8,14 @@ import android.net.Uri import android.os.Bundle import android.view.Menu import android.view.MenuItem -import android.view.ViewGroup import androidx.core.graphics.Insets import androidx.core.view.isVisible -import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding -import com.google.android.material.R as materialR import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity -import org.koitharu.kotatsu.core.network.UserAgentInterceptor +import org.koitharu.kotatsu.core.network.CommonHeadersInterceptor import org.koitharu.kotatsu.databinding.ActivityBrowserBinding +import com.google.android.material.R as materialR @SuppressLint("SetJavaScriptEnabled") class BrowserActivity : BaseActivity(), BrowserCallback { @@ -31,7 +29,7 @@ class BrowserActivity : BaseActivity(), BrowserCallback } with(binding.webView.settings) { javaScriptEnabled = true - userAgentString = UserAgentInterceptor.userAgentChrome + userAgentString = CommonHeadersInterceptor.userAgentChrome } binding.webView.webViewClient = BrowserClient(this) binding.webView.webChromeClient = ProgressChromeClient(binding.progressBar) @@ -72,6 +70,7 @@ class BrowserActivity : BaseActivity(), BrowserCallback finishAfterTransition() true } + R.id.action_browser -> { val intent = Intent(Intent.ACTION_VIEW) intent.data = Uri.parse(binding.webView.url) @@ -81,6 +80,7 @@ class BrowserActivity : BaseActivity(), BrowserCallback } true } + else -> super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt index 2b4ed813c..994e52c95 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt @@ -15,7 +15,7 @@ import dagger.hilt.android.AndroidEntryPoint import okhttp3.Headers import org.koitharu.kotatsu.base.ui.AlertDialogFragment import org.koitharu.kotatsu.core.network.CommonHeaders -import org.koitharu.kotatsu.core.network.UserAgentInterceptor +import org.koitharu.kotatsu.core.network.CommonHeadersInterceptor import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar import org.koitharu.kotatsu.databinding.FragmentCloudflareBinding import org.koitharu.kotatsu.utils.ext.stringArgument @@ -44,7 +44,7 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud cacheMode = WebSettings.LOAD_DEFAULT domStorageEnabled = true databaseEnabled = true - userAgentString = arguments?.getString(ARG_UA) ?: UserAgentInterceptor.userAgentChrome + userAgentString = arguments?.getString(ARG_UA) ?: CommonHeadersInterceptor.userAgentChrome } binding.webView.webViewClient = CloudFlareClient(cookieJar, this, url.orEmpty()) CookieManager.getInstance().setAcceptThirdPartyCookies(binding.webView, true) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt b/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt index 968731bce..565f5f653 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/AppModule.kt @@ -85,7 +85,7 @@ interface AppModule { @Singleton fun provideOkHttpClient( localStorageManager: LocalStorageManager, - userAgentInterceptor: UserAgentInterceptor, + commonHeadersInterceptor: CommonHeadersInterceptor, cookieJar: CookieJar, settings: AppSettings, ): OkHttpClient { @@ -98,7 +98,7 @@ interface AppModule { dns(DoHManager(cache, settings)) cache(cache) addInterceptor(GZipInterceptor()) - addInterceptor(userAgentInterceptor) + addInterceptor(commonHeadersInterceptor) addInterceptor(CloudFlareInterceptor()) if (BuildConfig.DEBUG) { addInterceptor(CurlLoggingInterceptor()) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/CommonHeadersInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/CommonHeadersInterceptor.kt new file mode 100644 index 000000000..7b26a9d71 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/CommonHeadersInterceptor.kt @@ -0,0 +1,77 @@ +package org.koitharu.kotatsu.core.network + +import android.os.Build +import android.util.Log +import dagger.Lazy +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response +import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.core.parser.RemoteMangaRepository +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.util.mergeWith +import java.util.Locale +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class CommonHeadersInterceptor @Inject constructor( + private val mangaRepositoryFactoryLazy: Lazy, +) : Interceptor { + + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + val source = request.tag(MangaSource::class.java) + val repository = if (source != null) { + mangaRepositoryFactoryLazy.get().create(source) as? RemoteMangaRepository + } else { + if (BuildConfig.DEBUG) { + Log.w("Http", "Request without source tag: ${request.url}") + } + null + } + val headersBuilder = request.headers.newBuilder() + repository?.headers?.let { + headersBuilder.mergeWith(it, replaceExisting = false) + } + if (headersBuilder[CommonHeaders.USER_AGENT] == null) { + headersBuilder[CommonHeaders.USER_AGENT] = userAgentFallback + } + if (headersBuilder[CommonHeaders.REFERER] == null && repository != null) { + headersBuilder[CommonHeaders.REFERER] = "https://${repository.domain}/" + } + val newRequest = request.newBuilder().headers(headersBuilder.build()).build() + return repository?.intercept(ProxyChain(chain, newRequest)) ?: chain.proceed(newRequest) + } + + private class ProxyChain( + private val delegate: Interceptor.Chain, + private val request: Request, + ) : Interceptor.Chain by delegate { + + override fun request(): Request = request + } + + companion object { + + val userAgentFallback + get() = "Kotatsu/%s (Android %s; %s; %s %s; %s)".format( + BuildConfig.VERSION_NAME, + Build.VERSION.RELEASE, + Build.MODEL, + Build.BRAND, + Build.DEVICE, + Locale.getDefault().language, + ) + + val userAgentChrome + get() = ( + "Mozilla/5.0 (Linux; Android %s; %s) AppleWebKit/537.36 (KHTML, like Gecko) " + + "Chrome/100.0.4896.127 Mobile Safari/537.36" + ).format( + Build.VERSION.RELEASE, + Build.MODEL, + ) + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt deleted file mode 100644 index a0b46d309..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt +++ /dev/null @@ -1,59 +0,0 @@ -package org.koitharu.kotatsu.core.network - -import android.os.Build -import dagger.Lazy -import okhttp3.Interceptor -import okhttp3.Request -import okhttp3.Response -import org.koitharu.kotatsu.BuildConfig -import org.koitharu.kotatsu.core.parser.MangaRepository -import org.koitharu.kotatsu.core.parser.RemoteMangaRepository -import org.koitharu.kotatsu.parsers.model.MangaSource -import java.util.Locale -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class UserAgentInterceptor @Inject constructor( - private val mangaRepositoryFactoryLazy: Lazy, -) : Interceptor { - - override fun intercept(chain: Interceptor.Chain): Response { - val request = chain.request() - return chain.proceed( - if (request.header(CommonHeaders.USER_AGENT) == null) { - request.newBuilder() - .addHeader(CommonHeaders.USER_AGENT, getUserAgent(request)) - .build() - } else request, - ) - } - - private fun getUserAgent(request: Request): String { - val source = request.tag(MangaSource::class.java) ?: return userAgent - val repository = mangaRepositoryFactoryLazy.get().create(source) as? RemoteMangaRepository - return repository?.userAgent ?: userAgent - } - - companion object { - - val userAgent - get() = "Kotatsu/%s (Android %s; %s; %s %s; %s)".format( - BuildConfig.VERSION_NAME, - Build.VERSION.RELEASE, - Build.MODEL, - Build.BRAND, - Build.DEVICE, - Locale.getDefault().language, - ) // TODO Decide what to do with this afterwards - - val userAgentChrome - get() = ( - "Mozilla/5.0 (Linux; Android %s; %s) AppleWebKit/537.36 (KHTML, like Gecko) " + - "Chrome/100.0.4896.127 Mobile Safari/537.36" - ).format( - Build.VERSION.RELEASE, - Build.MODEL, - ) - } -} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt index ba4da8fa9..389898767 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt @@ -6,9 +6,11 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainCoroutineDispatcher import kotlinx.coroutines.async import kotlinx.coroutines.currentCoroutineContext +import okhttp3.Headers +import okhttp3.Interceptor +import okhttp3.Response import org.koitharu.kotatsu.core.cache.ContentCache import org.koitharu.kotatsu.core.cache.SafeDeferred -import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.prefs.SourceSettings import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParserAuthProvider @@ -20,13 +22,14 @@ import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.util.domain import org.koitharu.kotatsu.utils.ext.processLifecycleScope import org.koitharu.kotatsu.utils.ext.runCatchingCancellable class RemoteMangaRepository( private val parser: MangaParser, private val cache: ContentCache, -) : MangaRepository { +) : MangaRepository, Interceptor { override val source: MangaSource get() = parser.source @@ -40,8 +43,19 @@ class RemoteMangaRepository( getConfig().defaultSortOrder = value } - val userAgent: String? - get() = parser.headers?.get(CommonHeaders.USER_AGENT) + val domain: String + get() = parser.domain + + val headers: Headers? + get() = parser.headers + + override fun intercept(chain: Interceptor.Chain): Response { + return if (parser is Interceptor) { + parser.intercept(chain) + } else { + chain.proceed(chain.request()) + } + } override suspend fun getList(offset: Int, query: String): List { return parser.getList(offset, query) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt index 0fdd23162..ab403a43c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt @@ -20,7 +20,6 @@ import okhttp3.Response import okhttp3.ResponseBody import okhttp3.internal.closeQuietly import org.koitharu.kotatsu.core.model.MangaSource -import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.local.data.CacheDir @@ -53,7 +52,7 @@ class FaviconFetcher( options.size.height.pxOrElse { FALLBACK_SIZE }, ) val icon = checkNotNull(favicons.find(sizePx)) { "No favicons found" } - val response = loadIcon(icon.url, repo.userAgent, favicons.referer) + val response = loadIcon(icon.url, mangaSource) val responseBody = response.requireBody() val source = writeToDiskCache(responseBody)?.toImageSource() ?: responseBody.toImageSource() return SourceResult( @@ -63,14 +62,11 @@ class FaviconFetcher( ) } - private suspend fun loadIcon(url: String, userAgent: String?, referer: String): Response { + private suspend fun loadIcon(url: String, source: MangaSource): Response { val request = Request.Builder() .url(url) .get() - .header(CommonHeaders.REFERER, referer) - if (userAgent != null) { - request.header(CommonHeaders.USER_AGENT, userAgent) - } + .tag(MangaSource::class.java, source) @Suppress("UNCHECKED_CAST") options.tags.asMap().forEach { request.tag(it.key as Class, it.value) } val response = okHttpClient.newCall(request.build()).await() diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt index 5211d9320..2ceeb60a6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt @@ -50,7 +50,6 @@ import org.koitharu.kotatsu.utils.ext.drawableTop import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.ifNullOrEmpty import org.koitharu.kotatsu.utils.ext.measureHeight -import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.resolveDp import org.koitharu.kotatsu.utils.ext.scaleUpActivityOptionsOf import org.koitharu.kotatsu.utils.ext.textAndVisible @@ -343,7 +342,6 @@ class DetailsFragment : .data(imageUrl) .tag(manga.source) .crossfade(context) - .referer(manga.publicUrl) .lifecycle(viewLifecycleOwner) .placeholderMemoryCacheKey(manga.coverUrl) val previousDrawable = lastResult?.drawable 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 e534f9120..a0a2da35e 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 @@ -38,7 +38,6 @@ import org.koitharu.kotatsu.parsers.util.await import org.koitharu.kotatsu.utils.ext.copyToSuspending import org.koitharu.kotatsu.utils.ext.deleteAwait import org.koitharu.kotatsu.utils.ext.printStackTraceDebug -import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.runCatchingCancellable import org.koitharu.kotatsu.utils.progress.PausingProgressJob import java.io.File @@ -220,7 +219,7 @@ class DownloadManager @AssistedInject constructor( val request = Request.Builder() .url(url) .header(CommonHeaders.REFERER, referer) - .tag(source) + .tag(MangaSource::class.java, source) .cacheControl(CommonHeaders.CACHE_CONTROL_DISABLED) .get() .build() @@ -250,7 +249,6 @@ class DownloadManager @AssistedInject constructor( imageLoader.execute( ImageRequest.Builder(context) .data(manga.coverUrl) - .referer(manga.publicUrl) .tag(manga.source) .size(coverWidth, coverHeight) .scale(Scale.FILL) diff --git a/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadItemAD.kt index 81554a088..27feeb00f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/download/ui/DownloadItemAD.kt @@ -17,7 +17,6 @@ import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.newImageRequest import org.koitharu.kotatsu.utils.ext.onFirst -import org.koitharu.kotatsu.utils.ext.referer fun downloadItemAD( scope: CoroutineScope, @@ -45,7 +44,6 @@ fun downloadItemAD( job?.cancel() job = item.progressAsFlow().onFirst { state -> binding.imageViewCover.newImageRequest(state.manga.coverUrl, state.manga.source)?.run { - referer(state.manga.publicUrl) placeholder(state.cover) fallback(R.drawable.ic_placeholder) error(R.drawable.ic_error_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt index 6e65e6f83..dd63900c0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt @@ -15,7 +15,6 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.utils.ext.disposeImageRequest import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.newImageRequest -import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.image.CoverSizeResolver fun mangaGridItemAD( @@ -40,7 +39,6 @@ fun mangaGridItemAD( binding.textViewTitle.text = item.title binding.progressView.setPercent(item.progress, MangaListAdapter.PAYLOAD_PROGRESS in payloads) binding.imageViewCover.newImageRequest(item.coverUrl, item.source)?.run { - referer(item.manga.publicUrl) size(CoverSizeResolver(binding.imageViewCover)) placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt index 6f7412361..14c06c6ec 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt @@ -17,7 +17,6 @@ import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.utils.ext.disposeImageRequest import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.newImageRequest -import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.textAndVisible import org.koitharu.kotatsu.utils.image.CoverSizeResolver @@ -53,7 +52,6 @@ fun mangaListDetailedItemAD( binding.textViewSubtitle.textAndVisible = item.subtitle binding.progressView.setPercent(item.progress, MangaListAdapter.PAYLOAD_PROGRESS in payloads) binding.imageViewCover.newImageRequest(item.coverUrl, item.source)?.run { - referer(item.manga.publicUrl) size(CoverSizeResolver(binding.imageViewCover)) placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt index dea006ed6..d2e5c9530 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt @@ -13,7 +13,6 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.utils.ext.disposeImageRequest import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.newImageRequest -import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.textAndVisible fun mangaListItemAD( @@ -36,7 +35,6 @@ fun mangaListItemAD( binding.textViewTitle.text = item.title binding.textViewSubtitle.textAndVisible = item.subtitle binding.imageViewCover.newImageRequest(item.coverUrl, item.source)?.run { - referer(item.manga.publicUrl) placeholder(R.drawable.ic_placeholder) fallback(R.drawable.ic_placeholder) error(R.drawable.ic_error_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/local/ui/ImportService.kt b/app/src/main/java/org/koitharu/kotatsu/local/ui/ImportService.kt index ae7d5aa60..f2efd9d27 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/ui/ImportService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/ui/ImportService.kt @@ -26,7 +26,6 @@ import org.koitharu.kotatsu.utils.PendingIntentCompat import org.koitharu.kotatsu.utils.ext.asArrayList import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.printStackTraceDebug -import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.report import org.koitharu.kotatsu.utils.ext.toBitmapOrNull import javax.inject.Inject @@ -102,7 +101,6 @@ class ImportService : CoroutineIntentService() { ImageRequest.Builder(applicationContext) .data(manga.coverUrl) .tag(manga.source) - .referer(manga.publicUrl) .build(), ).toBitmapOrNull(), ) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt b/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt index f90f8397b..3fa7c48dd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt @@ -191,7 +191,7 @@ class PageLoader @Inject constructor( .header(CommonHeaders.REFERER, page.referer) .header(CommonHeaders.ACCEPT, "image/webp,image/png;q=0.9,image/jpeg,*/*;q=0.8") .cacheControl(CommonHeaders.CACHE_CONTROL_DISABLED) - .tag(page.source) + .tag(MangaSource::class.java, page.source) .build() okHttp.newCall(request).await().use { response -> check(response.isSuccessful) { diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt index 2696f393f..ec57f5847 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt @@ -29,7 +29,6 @@ import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.decodeRegion import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.getParcelableExtraCompat -import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.setValueRounded import javax.inject.Inject import com.google.android.material.R as materialR @@ -117,7 +116,6 @@ class ColorFilterConfigActivity : if (preview == null) return ImageRequest.Builder(this@ColorFilterConfigActivity) .data(preview.url) - .referer(preview.referer) .scale(Scale.FILL) .decodeRegion() .tag(preview.source) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/adapter/PageThumbnailAD.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/adapter/PageThumbnailAD.kt index fd881846f..96534ea8a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/adapter/PageThumbnailAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/adapter/PageThumbnailAD.kt @@ -19,7 +19,6 @@ import org.koitharu.kotatsu.reader.domain.PageLoader import org.koitharu.kotatsu.reader.ui.thumbnails.PageThumbnail import org.koitharu.kotatsu.utils.ext.decodeRegion import org.koitharu.kotatsu.utils.ext.isLowRamDevice -import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.runCatchingCancellable import org.koitharu.kotatsu.utils.ext.setTextColorAttr import com.google.android.material.R as materialR @@ -44,7 +43,6 @@ fun pageThumbnailAD( coil.execute( ImageRequest.Builder(context) .data(url) - .referer(item.page.referer) .tag(item.page.source) .size(thumbSize) .scale(Scale.FILL) diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt b/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt index f9ce14a07..b70ba4497 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt @@ -17,7 +17,8 @@ import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.browser.BrowserCallback import org.koitharu.kotatsu.browser.BrowserClient import org.koitharu.kotatsu.browser.ProgressChromeClient -import org.koitharu.kotatsu.core.network.UserAgentInterceptor +import org.koitharu.kotatsu.core.network.CommonHeaders +import org.koitharu.kotatsu.core.network.CommonHeadersInterceptor import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.databinding.ActivityBrowserBinding @@ -60,7 +61,8 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba } with(binding.webView.settings) { javaScriptEnabled = true - userAgentString = repository.userAgent ?: UserAgentInterceptor.userAgent + userAgentString = repository.headers?.get(CommonHeaders.USER_AGENT) + ?: CommonHeadersInterceptor.userAgentFallback } binding.webView.webViewClient = BrowserClient(this) binding.webView.webChromeClient = ProgressChromeClient(binding.progressBar) diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt index 4aa0b5a86..7aaca6808 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt @@ -46,7 +46,6 @@ import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.tracker.domain.Tracker import org.koitharu.kotatsu.tracker.domain.model.MangaUpdates import org.koitharu.kotatsu.utils.PendingIntentCompat -import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.runCatchingCancellable import org.koitharu.kotatsu.utils.ext.toBitmapOrNull import org.koitharu.kotatsu.utils.ext.trySetForeground @@ -157,7 +156,6 @@ class TrackWorker @AssistedInject constructor( coil.execute( ImageRequest.Builder(applicationContext) .data(manga.coverUrl) - .referer(manga.publicUrl) .tag(manga.source) .build(), ).toBitmapOrNull(), diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt index 28716c743..fb2109524 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt @@ -10,9 +10,7 @@ import coil.request.ImageResult import coil.request.SuccessResult import coil.util.CoilUtils import com.google.android.material.progressindicator.BaseProgressIndicator -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.image.RegionBitmapDecoder import org.koitharu.kotatsu.utils.progress.ImageRequestIndicatorListener @@ -51,21 +49,6 @@ fun ImageResult.toBitmapOrNull() = when (this) { is ErrorResult -> null } -fun ImageRequest.Builder.referer(referer: String): ImageRequest.Builder { - if (referer.isEmpty()) { - return this - } - try { - setHeader(CommonHeaders.REFERER, referer) - } catch (e: IllegalArgumentException) { - val baseUrl = referer.baseUrl() - if (baseUrl != null) { - setHeader(CommonHeaders.REFERER, baseUrl) - } - } - return this -} - fun ImageRequest.Builder.indicator(indicator: BaseProgressIndicator<*>): ImageRequest.Builder { return listener(ImageRequestIndicatorListener(indicator)) } @@ -83,11 +66,3 @@ fun ImageRequest.Builder.crossfade(context: Context?): ImageRequest.Builder { val duration = context.resources.getInteger(R.integer.config_defaultAnimTime) * context.animatorDurationScale return crossfade(duration.toInt()) } - -private fun String.baseUrl(): String? { - return (this.toHttpUrlOrNull()?.newBuilder("/") ?: return null) - .username("") - .password("") - .build() - .toString() -} diff --git a/app/src/release/java/org/koitharu/kotatsu/core/parser/DummyParser.kt b/app/src/release/java/org/koitharu/kotatsu/core/parser/DummyParser.kt index 10779ef79..12722988c 100644 --- a/app/src/release/java/org/koitharu/kotatsu/core/parser/DummyParser.kt +++ b/app/src/release/java/org/koitharu/kotatsu/core/parser/DummyParser.kt @@ -12,7 +12,7 @@ import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.SortOrder import java.util.EnumSet -class DummyParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.DUMMY) { +class DummyParser(context: MangaLoaderContext) : MangaParser(context, MangaSource.DUMMY) { override val configKeyDomain: ConfigKey.Domain get() = ConfigKey.Domain("localhost", null) From c5ae9fb087407422d9b4265a70acfeeaefad0265 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 8 Feb 2023 20:19:01 +0200 Subject: [PATCH 7/8] Use relative date format --- .../koitharu/kotatsu/core/prefs/AppSettings.kt | 9 --------- .../kotatsu/details/ui/DetailsViewModel.kt | 1 - .../kotatsu/details/ui/MangaDetailsDelegate.kt | 8 -------- .../kotatsu/details/ui/model/ChapterListItem.kt | 13 +++++++------ .../details/ui/model/ListModelConversionExt.kt | 3 --- .../kotatsu/reader/ui/ChaptersBottomSheet.kt | 6 ++---- .../settings/AppearanceSettingsFragment.kt | 15 --------------- app/src/main/res/values/constants.xml | 8 -------- app/src/main/res/xml/pref_appearance.xml | 4 ---- 9 files changed, 9 insertions(+), 58 deletions(-) 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 9b5929c31..348f0dc20 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 @@ -22,8 +22,6 @@ import org.koitharu.kotatsu.utils.ext.observe import org.koitharu.kotatsu.utils.ext.putEnumValue import org.koitharu.kotatsu.utils.ext.toUriOrNull import java.io.File -import java.text.DateFormat -import java.text.SimpleDateFormat import java.util.Collections import java.util.EnumSet import java.util.Locale @@ -263,12 +261,6 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { return policy.isNetworkAllowed(connectivityManager) } - fun getDateFormat(format: String = prefs.getString(KEY_DATE_FORMAT, "").orEmpty()): DateFormat = - when (format) { - "" -> DateFormat.getDateInstance(DateFormat.SHORT) - else -> SimpleDateFormat(format, Locale.getDefault()) - } - fun getSuggestionsTagsBlacklistRegex(): Regex? { val string = prefs.getString(KEY_SUGGESTIONS_EXCLUDE_TAGS, null)?.trimEnd(' ', ',') if (string.isNullOrEmpty()) { @@ -317,7 +309,6 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { const val KEY_THEME = "theme" const val KEY_COLOR_THEME = "color_theme" const val KEY_THEME_AMOLED = "amoled_theme" - const val KEY_DATE_FORMAT = "date_format" const val KEY_SOURCES_ORDER = "sources_order_2" const val KEY_SOURCES_HIDDEN = "sources_hidden" const val KEY_TRAFFIC_WARNING = "traffic_warning" diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt index e7c98d27e..28215a004 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt @@ -72,7 +72,6 @@ class DetailsViewModel @AssistedInject constructor( private val delegate = MangaDetailsDelegate( intent = intent, - settings = settings, mangaDataRepository = mangaDataRepository, historyRepository = historyRepository, localMangaRepository = localMangaRepository, diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/MangaDetailsDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/MangaDetailsDelegate.kt index dd386bfad..93a13b52e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/MangaDetailsDelegate.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/MangaDetailsDelegate.kt @@ -7,7 +7,6 @@ import org.koitharu.kotatsu.base.domain.MangaIntent import org.koitharu.kotatsu.core.model.MangaHistory import org.koitharu.kotatsu.core.model.getPreferredBranch import org.koitharu.kotatsu.core.parser.MangaRepository -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.details.ui.model.toListItem import org.koitharu.kotatsu.history.domain.HistoryRepository @@ -21,7 +20,6 @@ import org.koitharu.kotatsu.utils.ext.runCatchingCancellable class MangaDetailsDelegate( private val intent: MangaIntent, - private val settings: AppSettings, private val mangaDataRepository: MangaDataRepository, private val historyRepository: HistoryRepository, private val localMangaRepository: LocalMangaRepository, @@ -82,7 +80,6 @@ class MangaDetailsDelegate( branch: String?, ): List { val result = ArrayList(chapters.size) - val dateFormat = settings.getDateFormat() val currentIndex = chapters.indexOfFirst { it.id == currentId } val firstNewIndex = chapters.size - newCount val downloadedIds = downloadedChapters?.mapTo(HashSet(downloadedChapters.size)) { it.id } @@ -97,7 +94,6 @@ class MangaDetailsDelegate( isNew = i >= firstNewIndex, isMissing = false, isDownloaded = downloadedIds?.contains(chapter.id) == true, - dateFormat = dateFormat, ) } if (result.size < chapters.size / 2) { @@ -117,7 +113,6 @@ class MangaDetailsDelegate( val result = ArrayList(sourceChapters.size) val currentIndex = sourceChapters.indexOfFirst { it.id == currentId } val firstNewIndex = sourceChapters.size - newCount - val dateFormat = settings.getDateFormat() for (i in sourceChapters.indices) { val chapter = sourceChapters[i] val localChapter = chaptersMap.remove(chapter.id) @@ -130,14 +125,12 @@ class MangaDetailsDelegate( isNew = i >= firstNewIndex, isMissing = false, isDownloaded = false, - dateFormat = dateFormat, ) ?: chapter.toListItem( isCurrent = i == currentIndex, isUnread = i > currentIndex, isNew = i >= firstNewIndex, isMissing = true, isDownloaded = false, - dateFormat = dateFormat, ) } if (chaptersMap.isNotEmpty()) { // some chapters on device but not online source @@ -150,7 +143,6 @@ class MangaDetailsDelegate( isNew = false, isMissing = false, isDownloaded = false, - dateFormat = dateFormat, ) } else { null diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/model/ChapterListItem.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/model/ChapterListItem.kt index 7de226908..35b7aec12 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/model/ChapterListItem.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/model/ChapterListItem.kt @@ -1,21 +1,24 @@ package org.koitharu.kotatsu.details.ui.model -import java.text.DateFormat +import android.text.format.DateUtils import org.koitharu.kotatsu.parsers.model.MangaChapter class ChapterListItem( val chapter: MangaChapter, val flags: Int, private val uploadDateMs: Long, - private val dateFormat: DateFormat, ) { - var uploadDate: String? = null + var uploadDate: CharSequence? = null private set get() { if (field != null) return field if (uploadDateMs == 0L) return null - field = dateFormat.format(uploadDateMs) + field = DateUtils.getRelativeTimeSpanString( + uploadDateMs, + System.currentTimeMillis(), + DateUtils.DAY_IN_MILLIS, + ) return field } @@ -44,7 +47,6 @@ class ChapterListItem( if (chapter != other.chapter) return false if (flags != other.flags) return false if (uploadDateMs != other.uploadDateMs) return false - if (dateFormat != other.dateFormat) return false return true } @@ -53,7 +55,6 @@ class ChapterListItem( var result = chapter.hashCode() result = 31 * result + flags result = 31 * result + uploadDateMs.hashCode() - result = 31 * result + dateFormat.hashCode() return result } diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/model/ListModelConversionExt.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/model/ListModelConversionExt.kt index 9e57bd7f0..8f555e39c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/model/ListModelConversionExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/model/ListModelConversionExt.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.details.ui.model -import java.text.DateFormat import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_CURRENT import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_DOWNLOADED import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_MISSING @@ -14,7 +13,6 @@ fun MangaChapter.toListItem( isNew: Boolean, isMissing: Boolean, isDownloaded: Boolean, - dateFormat: DateFormat, ): ChapterListItem { var flags = 0 if (isCurrent) flags = flags or FLAG_CURRENT @@ -26,6 +24,5 @@ fun MangaChapter.toListItem( chapter = this, flags = flags, uploadDateMs = uploadDate, - dateFormat = dateFormat, ) } diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersBottomSheet.kt index 9efa3cef6..87b92acd5 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersBottomSheet.kt @@ -6,8 +6,6 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.FragmentManager import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject -import kotlin.math.roundToInt import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseBottomSheet import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener @@ -21,6 +19,8 @@ import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.utils.RecyclerViewScrollCallback import org.koitharu.kotatsu.utils.ext.getParcelableCompat import org.koitharu.kotatsu.utils.ext.withArgs +import javax.inject.Inject +import kotlin.math.roundToInt @AndroidEntryPoint class ChaptersBottomSheet : BaseBottomSheet(), OnListItemClickListener { @@ -41,7 +41,6 @@ class ChaptersBottomSheet : BaseBottomSheet(), OnListItemC } val currentId = requireArguments().getLong(ARG_CURRENT_ID, 0L) val currentPosition = chapters.indexOfFirst { it.id == currentId } - val dateFormat = settings.getDateFormat() val items = chapters.mapIndexed { index, chapter -> chapter.toListItem( isCurrent = index == currentPosition, @@ -49,7 +48,6 @@ class ChaptersBottomSheet : BaseBottomSheet(), OnListItemC isNew = false, isMissing = false, isDownloaded = false, - dateFormat = dateFormat, ) } binding.recyclerView.adapter = ChaptersAdapter(this).also { adapter -> diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/AppearanceSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/AppearanceSettingsFragment.kt index 5c1c6ff27..d46df2738 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/AppearanceSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/AppearanceSettingsFragment.kt @@ -29,7 +29,6 @@ import org.koitharu.kotatsu.utils.ext.getLocalesConfig import org.koitharu.kotatsu.utils.ext.map import org.koitharu.kotatsu.utils.ext.setDefaultValueCompat import org.koitharu.kotatsu.utils.ext.toList -import java.util.Date import java.util.Locale import javax.inject.Inject @@ -55,20 +54,6 @@ class AppearanceSettingsFragment : entryValues = ListMode.values().names() setDefaultValueCompat(ListMode.GRID.name) } - findPreference(AppSettings.KEY_DATE_FORMAT)?.run { - entryValues = resources.getStringArray(R.array.date_formats) - val now = Date().time - entries = entryValues.map { value -> - val formattedDate = settings.getDateFormat(value.toString()).format(now) - if (value == "") { - getString(R.string.default_s, formattedDate) - } else { - formattedDate - } - }.toTypedArray() - setDefaultValueCompat("") - summary = "%s" - } findPreference(AppSettings.KEY_PROTECT_APP) ?.isChecked = !settings.appPassword.isNullOrEmpty() findPreference(AppSettings.KEY_APP_LOCALE)?.run { diff --git a/app/src/main/res/values/constants.xml b/app/src/main/res/values/constants.xml index 14108882b..a7f1cd3ff 100644 --- a/app/src/main/res/values/constants.xml +++ b/app/src/main/res/values/constants.xml @@ -38,12 +38,4 @@ 2 0 - - - MM/dd/yy - dd/MM/yy - yyyy-MM-dd - dd MMM yyyy - MMM dd, yyyy - diff --git a/app/src/main/res/xml/pref_appearance.xml b/app/src/main/res/xml/pref_appearance.xml index 65c2469ea..fa5236c23 100644 --- a/app/src/main/res/xml/pref_appearance.xml +++ b/app/src/main/res/xml/pref_appearance.xml @@ -27,10 +27,6 @@ android:title="@string/language" app:allowDividerAbove="true" /> - - Date: Wed, 8 Feb 2023 20:50:10 +0200 Subject: [PATCH 8/8] Add more color schemes --- .../kotatsu/core/prefs/ColorScheme.kt | 7 +- app/src/main/res/layout/item_color_scheme.xml | 2 +- app/src/main/res/layout/preference_theme.xml | 1 - .../res/values-night-v23/color_themes.xml | 91 ++++++++++++++++++- app/src/main/res/values-v23/color_themes.xml | 91 ++++++++++++++++++- app/src/main/res/values/color_themes.xml | 10 +- app/src/main/res/values/strings.xml | 7 +- 7 files changed, 197 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/ColorScheme.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/ColorScheme.kt index d0933d422..d67ab311d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/prefs/ColorScheme.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/prefs/ColorScheme.kt @@ -12,8 +12,11 @@ enum class ColorScheme( DEFAULT(R.style.Theme_Kotatsu, R.string.system_default), MONET(R.style.Theme_Kotatsu_Monet, R.string.theme_name_dynamic), - MINT(R.style.Theme_Kotatsu_Mint, R.string.theme_name_mint), - OCTOBER(R.style.Theme_Kotatsu_October, R.string.theme_name_october), + MIKU(R.style.Theme_Kotatsu_Miku, R.string.theme_name_miku), + RENA(R.style.Theme_Kotatsu_Asuka, R.string.theme_name_asuka), + FROG(R.style.Theme_Kotatsu_Mion, R.string.theme_name_mion), + BLUEBERRY(R.style.Theme_Kotatsu_Rikka, R.string.theme_name_rikka), + NAME2(R.style.Theme_Kotatsu_Sakura, R.string.theme_name_sakura), ; companion object { diff --git a/app/src/main/res/layout/item_color_scheme.xml b/app/src/main/res/layout/item_color_scheme.xml index d02b92e89..80b62babb 100644 --- a/app/src/main/res/layout/item_color_scheme.xml +++ b/app/src/main/res/layout/item_color_scheme.xml @@ -8,7 +8,7 @@ android:background="?selectableItemBackground" android:orientation="vertical" android:padding="6dp" - tools:theme="@style/Theme.Kotatsu.Mint"> + tools:theme="@style/Theme.Kotatsu.Miku"> diff --git a/app/src/main/res/values-night-v23/color_themes.xml b/app/src/main/res/values-night-v23/color_themes.xml index cb3b95d88..7994430e2 100644 --- a/app/src/main/res/values-night-v23/color_themes.xml +++ b/app/src/main/res/values-night-v23/color_themes.xml @@ -1,7 +1,7 @@ - - + + + + + + diff --git a/app/src/main/res/values-v23/color_themes.xml b/app/src/main/res/values-v23/color_themes.xml index ee818955e..bc43a62c3 100644 --- a/app/src/main/res/values-v23/color_themes.xml +++ b/app/src/main/res/values-v23/color_themes.xml @@ -1,7 +1,7 @@ - - + + + + + + diff --git a/app/src/main/res/values/color_themes.xml b/app/src/main/res/values/color_themes.xml index a3c29c58b..95b07bb67 100644 --- a/app/src/main/res/values/color_themes.xml +++ b/app/src/main/res/values/color_themes.xml @@ -1,7 +1,13 @@ -