Fixes and refactor
This commit is contained in:
@@ -9,7 +9,6 @@ import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import org.koitharu.kotatsu.utils.ext.safe
|
||||
|
||||
class BrowserClient(private val callback: BrowserCallback) : WebViewClient(), KoinComponent {
|
||||
|
||||
@@ -45,7 +44,7 @@ class BrowserClient(private val callback: BrowserCallback) : WebViewClient(), Ko
|
||||
return request?.url?.toString()?.let(::doRequest)
|
||||
}
|
||||
|
||||
private fun doRequest(url: String): WebResourceResponse? = safe {
|
||||
private fun doRequest(url: String): WebResourceResponse? = runCatching {
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.build()
|
||||
@@ -56,5 +55,5 @@ class BrowserClient(private val callback: BrowserCallback) : WebViewClient(), Ko
|
||||
ct?.charset()?.name() ?: "utf-8",
|
||||
response.body?.byteStream()
|
||||
)
|
||||
}
|
||||
}.getOrNull()
|
||||
}
|
||||
@@ -4,7 +4,9 @@ import okhttp3.CookieJar
|
||||
import okhttp3.OkHttpClient
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
import org.koitharu.kotatsu.core.network.cookies.ClearableCookieJar
|
||||
import org.koitharu.kotatsu.core.network.cookies.PersistentCookieJar
|
||||
import org.koitharu.kotatsu.core.network.cookies.cache.SetCookieCache
|
||||
import org.koitharu.kotatsu.core.network.cookies.persistence.SharedPrefsCookiePersistor
|
||||
@@ -18,7 +20,7 @@ val networkModule
|
||||
SetCookieCache(),
|
||||
SharedPrefsCookiePersistor(androidContext())
|
||||
)
|
||||
}
|
||||
} bind ClearableCookieJar::class
|
||||
single(named(CacheUtils.QUALIFIER_HTTP)) { CacheUtils.createHttpCache(androidContext()) }
|
||||
single {
|
||||
OkHttpClient.Builder().apply {
|
||||
|
||||
@@ -2,16 +2,22 @@ package org.koitharu.kotatsu.core.network
|
||||
|
||||
import android.os.Build
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import java.util.*
|
||||
|
||||
class UserAgentInterceptor : Interceptor {
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain) = chain.proceed(
|
||||
chain.request().newBuilder()
|
||||
.header(HEADER_USER_AGENT, userAgent)
|
||||
.build()
|
||||
)
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
return chain.proceed(
|
||||
if (request.header(HEADER_USER_AGENT) == null) {
|
||||
request.newBuilder()
|
||||
.header(HEADER_USER_AGENT, userAgent)
|
||||
.build()
|
||||
} else request
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class PersistentCookieJar(
|
||||
cache.addAll(persistor.loadAll())
|
||||
}
|
||||
cache.addAll(cookies)
|
||||
persistor.saveAll(filterPersistentCookies(cookies))
|
||||
persistor.saveAll(cookies.filter { it.persistent })
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@@ -48,11 +48,14 @@ class PersistentCookieJar(
|
||||
val it = cache.iterator()
|
||||
while (it.hasNext()) {
|
||||
val currentCookie = it.next()
|
||||
if (isCookieExpired(currentCookie)) {
|
||||
cookiesToRemove.add(currentCookie)
|
||||
it.remove()
|
||||
} else if (currentCookie.matches(url)) {
|
||||
validCookies.add(currentCookie)
|
||||
when {
|
||||
currentCookie.isExpired() -> {
|
||||
cookiesToRemove.add(currentCookie)
|
||||
it.remove()
|
||||
}
|
||||
currentCookie.matches(url) -> {
|
||||
validCookies.add(currentCookie)
|
||||
}
|
||||
}
|
||||
}
|
||||
persistor.removeAll(cookiesToRemove)
|
||||
@@ -73,18 +76,8 @@ class PersistentCookieJar(
|
||||
|
||||
private companion object {
|
||||
|
||||
fun filterPersistentCookies(cookies: List<Cookie>): List<Cookie> {
|
||||
val persistentCookies: MutableList<Cookie> = ArrayList()
|
||||
for (cookie in cookies) {
|
||||
if (cookie.persistent) {
|
||||
persistentCookies.add(cookie)
|
||||
}
|
||||
}
|
||||
return persistentCookies
|
||||
}
|
||||
|
||||
fun isCookieExpired(cookie: Cookie): Boolean {
|
||||
return cookie.expiresAt < System.currentTimeMillis()
|
||||
fun Cookie.isExpired(): Boolean {
|
||||
return expiresAt < System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ val parserModule
|
||||
factory<MangaRepository>(named(MangaSource.HENCHAN)) { HenChanRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.YAOICHAN)) { YaoiChanRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.MANGATOWN)) { MangaTownRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.MANGALIB)) { MangaLibRepository(get()) }
|
||||
single<MangaRepository>(named(MangaSource.MANGALIB)) { MangaLibRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.NUDEMOON)) { NudeMoonRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.MANGAREAD)) { MangareadRepository(get()) }
|
||||
}
|
||||
@@ -56,7 +56,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
||||
).firstOrNull()?.text(),
|
||||
coverUrl = row.selectFirst("div.manga_images")?.selectFirst("img")
|
||||
?.attr("src")?.withDomain(domain).orEmpty(),
|
||||
tags = safe {
|
||||
tags = runCatching {
|
||||
row.selectFirst("div.genre")?.select("a")?.mapToSet {
|
||||
MangaTag(
|
||||
title = it.text(),
|
||||
@@ -64,7 +64,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
||||
source = source
|
||||
)
|
||||
}
|
||||
}.orEmpty(),
|
||||
}.getOrNull().orEmpty(),
|
||||
source = source
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.koitharu.kotatsu.core.parser.site
|
||||
|
||||
import androidx.collection.arraySetOf
|
||||
import androidx.core.net.toUri
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.core.exceptions.ParseException
|
||||
import org.koitharu.kotatsu.core.model.*
|
||||
@@ -53,7 +53,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
}.parseHtml()
|
||||
val root = doc.body().getElementById("mangaBox")
|
||||
?.selectFirst("div.tiles.row") ?: throw ParseException("Cannot find root")
|
||||
val baseHost = root.baseUri().toUri().host
|
||||
val baseHost = root.baseUri().toHttpUrl().host
|
||||
return root.select("div.tile").mapNotNull { node ->
|
||||
val imgDiv = node.selectFirst("div.img") ?: return@mapNotNull null
|
||||
val descDiv = node.selectFirst("div.desc") ?: return@mapNotNull null
|
||||
@@ -61,7 +61,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
return@mapNotNull null //skip author
|
||||
}
|
||||
val href = imgDiv.selectFirst("a").attr("href")?.inContextOf(node)
|
||||
if (href == null || href.toUri().host != baseHost) {
|
||||
if (href == null || href.toHttpUrl().host != baseHost) {
|
||||
return@mapNotNull null // skip external links
|
||||
}
|
||||
val title = descDiv.selectFirst("h3")?.selectFirst("a")?.text()
|
||||
@@ -73,15 +73,15 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
title = title,
|
||||
altTitle = descDiv.selectFirst("h4")?.text(),
|
||||
coverUrl = imgDiv.selectFirst("img.lazy")?.attr("data-original").orEmpty(),
|
||||
rating = safe {
|
||||
rating = runCatching {
|
||||
node.selectFirst("div.rating")
|
||||
?.attr("title")
|
||||
?.substringBefore(' ')
|
||||
?.toFloatOrNull()
|
||||
?.div(10f)
|
||||
} ?: Manga.NO_RATING,
|
||||
}.getOrNull() ?: Manga.NO_RATING,
|
||||
author = tileInfo?.selectFirst("a.person-link")?.text(),
|
||||
tags = safe {
|
||||
tags = runCatching {
|
||||
tileInfo?.select("a.element-link")
|
||||
?.mapToSet {
|
||||
MangaTag(
|
||||
@@ -90,7 +90,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
source = source
|
||||
)
|
||||
}
|
||||
}.orEmpty(),
|
||||
}.getOrNull().orEmpty(),
|
||||
state = when {
|
||||
node.selectFirst("div.tags")
|
||||
?.selectFirst("span.mangaCompleted") != null -> MangaState.FINISHED
|
||||
|
||||
@@ -80,14 +80,14 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
val title = root.selectFirst("div.media-header__wrap")?.children()
|
||||
val info = root.selectFirst("div.media-content")
|
||||
val chaptersDoc = loaderContext.httpGet(manga.url + "?section=chapters").parseHtml()
|
||||
val scripts = chaptersDoc.body().select("script")
|
||||
val scripts = chaptersDoc.select("script")
|
||||
var chapters: ArrayList<MangaChapter>? = null
|
||||
scripts@ for (script in scripts) {
|
||||
val raw = script.html().lines()
|
||||
for (line in raw) {
|
||||
if (line.startsWith("window.__CHAPTERS_DATA__")) {
|
||||
if (line.startsWith("window.__DATA__")) {
|
||||
val json = JSONObject(line.substringAfter('=').substringBeforeLast(';'))
|
||||
val list = json.getJSONArray("list")
|
||||
val list = json.getJSONObject("chapters").getJSONArray("list")
|
||||
val total = list.length()
|
||||
chapters = ArrayList(total)
|
||||
for (i in 0 until total) {
|
||||
@@ -99,7 +99,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
append("/c")
|
||||
append(item.getString("chapter_number"))
|
||||
append('/')
|
||||
append(item.getJSONArray("teams").getJSONObject(0).optString("slug"))
|
||||
append(item.optString("chapter_string"))
|
||||
}
|
||||
var name = item.getString("chapter_name")
|
||||
if (name.isNullOrBlank() || name == "null") {
|
||||
|
||||
@@ -149,6 +149,7 @@ class AppSettings private constructor(private val prefs: SharedPreferences) :
|
||||
const val KEY_SOURCES_HIDDEN = "sources_hidden"
|
||||
const val KEY_TRAFFIC_WARNING = "traffic_warning"
|
||||
const val KEY_PAGES_CACHE_CLEAR = "pages_cache_clear"
|
||||
const val KEY_COOKIES_CLEAR = "cookies_clear"
|
||||
const val KEY_THUMBS_CACHE_CLEAR = "thumbs_cache_clear"
|
||||
const val KEY_SEARCH_HISTORY_CLEAR = "search_history_clear"
|
||||
const val KEY_UPDATES_FEED_CLEAR = "updates_feed_clear"
|
||||
|
||||
@@ -17,7 +17,6 @@ import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||
import org.koitharu.kotatsu.utils.ext.safe
|
||||
import java.io.IOException
|
||||
|
||||
class DetailsViewModel(
|
||||
@@ -93,7 +92,7 @@ class DetailsViewModel(
|
||||
launchLoadingJob(Dispatchers.Default) {
|
||||
val original = localMangaRepository.getRemoteManga(manga)
|
||||
localMangaRepository.delete(manga) || throw IOException("Unable to delete file")
|
||||
safe {
|
||||
runCatching {
|
||||
historyRepository.deleteOrSwap(manga, original)
|
||||
}
|
||||
onMangaRemoved.postCall(manga)
|
||||
|
||||
@@ -92,13 +92,13 @@ class DownloadService : BaseService() {
|
||||
var output: MangaZip? = null
|
||||
try {
|
||||
val repo = manga.source.repository
|
||||
val cover = safe {
|
||||
val cover = runCatching {
|
||||
imageLoader.execute(
|
||||
ImageRequest.Builder(this@DownloadService)
|
||||
.data(manga.coverUrl)
|
||||
.build()
|
||||
).drawable
|
||||
}
|
||||
}.getOrNull()
|
||||
notification.setLargeIcon(cover)
|
||||
notification.update()
|
||||
val data = if (manga.chapters == null) repo.getDetails(manga) else manga
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.model.MangaTag
|
||||
import org.koitharu.kotatsu.utils.ext.getStringOrNull
|
||||
import org.koitharu.kotatsu.utils.ext.mapToSet
|
||||
import org.koitharu.kotatsu.utils.ext.safe
|
||||
|
||||
class MangaIndex(source: String?) {
|
||||
|
||||
@@ -40,7 +39,7 @@ class MangaIndex(source: String?) {
|
||||
json.put("app_version", BuildConfig.VERSION_CODE)
|
||||
}
|
||||
|
||||
fun getMangaInfo(): Manga? = if (json.length() == 0) null else safe {
|
||||
fun getMangaInfo(): Manga? = if (json.length() == 0) null else runCatching {
|
||||
val source = MangaSource.valueOf(json.getString("source"))
|
||||
Manga(
|
||||
id = json.getLong("id"),
|
||||
@@ -60,7 +59,7 @@ class MangaIndex(source: String?) {
|
||||
},
|
||||
chapters = getChapters(json.getJSONObject("chapters"), source)
|
||||
)
|
||||
}
|
||||
}.getOrNull()
|
||||
|
||||
fun getCoverEntry(): String? = json.optString("cover_entry")
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import org.koitharu.kotatsu.local.data.MangaZip
|
||||
import org.koitharu.kotatsu.utils.AlphanumComparator
|
||||
import org.koitharu.kotatsu.utils.ext.longHashCode
|
||||
import org.koitharu.kotatsu.utils.ext.readText
|
||||
import org.koitharu.kotatsu.utils.ext.safe
|
||||
import org.koitharu.kotatsu.utils.ext.sub
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
@@ -37,7 +36,7 @@ class LocalMangaRepository(private val context: Context) : MangaRepository {
|
||||
}
|
||||
val files = getAvailableStorageDirs(context)
|
||||
.flatMap { x -> x.listFiles(filenameFilter)?.toList().orEmpty() }
|
||||
return files.mapNotNull { x -> safe { getFromFile(x) } }
|
||||
return files.mapNotNull { x -> runCatching { getFromFile(x) }.getOrNull() }
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga) = if (manga.chapters == null) {
|
||||
@@ -128,9 +127,9 @@ class LocalMangaRepository(private val context: Context) : MangaRepository {
|
||||
}
|
||||
|
||||
fun getRemoteManga(localManga: Manga): Manga? {
|
||||
val file = safe {
|
||||
val file = runCatching {
|
||||
Uri.parse(localManga.url).toFile()
|
||||
} ?: return null
|
||||
}.getOrNull() ?: return null
|
||||
val zip = ZipFile(file)
|
||||
val entry = zip.getEntry(MangaZip.INDEX_ENTRY)
|
||||
val index = entry?.let(zip::readText)?.let(::MangaIndex) ?: return null
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.utils.MediaStoreCompat
|
||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||
import org.koitharu.kotatsu.utils.ext.safe
|
||||
import org.koitharu.kotatsu.utils.ext.sub
|
||||
import java.io.IOException
|
||||
|
||||
@@ -49,7 +48,10 @@ class LocalListViewModel(
|
||||
list.isEmpty() -> listOf(EmptyState(R.string.text_local_holder))
|
||||
else -> list.toUi(mode)
|
||||
}
|
||||
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))
|
||||
}.asLiveDataDistinct(
|
||||
viewModelScope.coroutineContext + Dispatchers.Default,
|
||||
listOf(LoadingState)
|
||||
)
|
||||
|
||||
init {
|
||||
onRefresh()
|
||||
@@ -94,7 +96,7 @@ class LocalListViewModel(
|
||||
withContext(Dispatchers.Default) {
|
||||
val original = repository.getRemoteManga(manga)
|
||||
repository.delete(manga) || throw IOException("Unable to delete file")
|
||||
safe {
|
||||
runCatching {
|
||||
historyRepository.deleteOrSwap(manga, original)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
package org.koitharu.kotatsu.reader.domain
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
@@ -5,7 +5,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.reader.ui.PageLoader
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
|
||||
abstract class BasePageHolder<B : ViewBinding>(
|
||||
protected val binding: B,
|
||||
|
||||
@@ -8,7 +8,7 @@ import androidx.viewbinding.ViewBinding
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
||||
import org.koitharu.kotatsu.base.ui.BaseFragment
|
||||
import org.koitharu.kotatsu.reader.ui.PageLoader
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderViewModel
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.reader.ui.PageLoader
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.utils.ext.resetTransformations
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.core.model.ZoomMode
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.reader.ui.PageLoader
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.utils.ext.launchAfter
|
||||
import org.koitharu.kotatsu.utils.ext.launchInstead
|
||||
import java.io.File
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.model.ZoomMode
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.databinding.ItemPageBinding
|
||||
import org.koitharu.kotatsu.reader.ui.PageLoader
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PageHolder
|
||||
|
||||
class ReversedPageHolder(
|
||||
|
||||
@@ -5,7 +5,7 @@ import android.view.ViewGroup
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.databinding.ItemPageBinding
|
||||
import org.koitharu.kotatsu.reader.ui.PageLoader
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
|
||||
class ReversedPagesAdapter(
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
|
||||
import org.koitharu.kotatsu.core.model.ZoomMode
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.databinding.ItemPageBinding
|
||||
import org.koitharu.kotatsu.reader.ui.PageLoader
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BasePageHolder
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
|
||||
@@ -5,7 +5,7 @@ import android.view.ViewGroup
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.databinding.ItemPageBinding
|
||||
import org.koitharu.kotatsu.reader.ui.PageLoader
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
|
||||
class PagesAdapter(
|
||||
|
||||
@@ -5,7 +5,7 @@ import android.view.ViewGroup
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding
|
||||
import org.koitharu.kotatsu.reader.ui.PageLoader
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
|
||||
class WebtoonAdapter(
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
|
||||
import org.koitharu.kotatsu.core.model.ZoomMode
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding
|
||||
import org.koitharu.kotatsu.reader.ui.PageLoader
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BasePageHolder
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.view.MenuItem
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.search.ui.global.GlobalSearchActivity
|
||||
import org.koitharu.kotatsu.utils.ext.safe
|
||||
import java.io.Closeable
|
||||
|
||||
object SearchHelper {
|
||||
@@ -43,10 +42,10 @@ object SearchHelper {
|
||||
override fun onSuggestionSelect(position: Int) = false
|
||||
|
||||
override fun onSuggestionClick(position: Int): Boolean {
|
||||
val query = safe {
|
||||
val query = runCatching {
|
||||
val c = view.suggestionsAdapter.getItem(position) as? Cursor
|
||||
c?.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY))
|
||||
} ?: return false
|
||||
}.getOrNull() ?: return false
|
||||
view.setQuery(query, true)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
||||
import org.koitharu.kotatsu.core.network.cookies.ClearableCookieJar
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.local.data.Cache
|
||||
import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider
|
||||
@@ -71,6 +73,20 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
|
||||
clearCache(preference, Cache.THUMBS)
|
||||
true
|
||||
}
|
||||
AppSettings.KEY_COOKIES_CLEAR -> {
|
||||
viewLifecycleScope.launch {
|
||||
val cookieJar = get<ClearableCookieJar>()
|
||||
withContext(Dispatchers.IO) {
|
||||
cookieJar.clear()
|
||||
}
|
||||
Snackbar.make(
|
||||
listView ?: return@launch,
|
||||
R.string.cookies_cleared,
|
||||
Snackbar.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
true
|
||||
}
|
||||
AppSettings.KEY_SEARCH_HISTORY_CLEAR -> {
|
||||
viewLifecycleScope.launch {
|
||||
MangaSuggestionsProvider.clearHistory(preference.context)
|
||||
|
||||
@@ -9,18 +9,18 @@ import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.utils.ext.mapToSet
|
||||
import org.koitharu.kotatsu.utils.ext.safe
|
||||
|
||||
class SourcesAdapter(
|
||||
private val settings: AppSettings,
|
||||
private val onItemClickListener: OnListItemClickListener<MangaSource>,
|
||||
) : RecyclerView.Adapter<SourceViewHolder>() {
|
||||
|
||||
private val dataSet = MangaProviderFactory.getSources(settings, includeHidden = true).toMutableList()
|
||||
private val dataSet =
|
||||
MangaProviderFactory.getSources(settings, includeHidden = true).toMutableList()
|
||||
private val hiddenItems = settings.hiddenSources.mapNotNull {
|
||||
safe {
|
||||
runCatching {
|
||||
MangaSource.valueOf(it)
|
||||
}
|
||||
}.getOrNull()
|
||||
}.toMutableSet()
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.koitharu.kotatsu.core.model.MangaChapter
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.utils.ext.safe
|
||||
import org.koitharu.kotatsu.utils.ext.toBitmapOrNull
|
||||
import org.koitharu.kotatsu.utils.ext.toUriOrNull
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -37,23 +36,23 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
||||
private val repository by inject<TrackingRepository>()
|
||||
private val settings by inject<AppSettings>()
|
||||
|
||||
override suspend fun doWork(): Result = withContext(Dispatchers.Default) {
|
||||
override suspend fun doWork(): Result {
|
||||
val trackSources = settings.trackSources
|
||||
if (trackSources.isEmpty()) {
|
||||
return@withContext Result.success()
|
||||
return Result.success()
|
||||
}
|
||||
val tracks = repository.getAllTracks(
|
||||
useFavourites = AppSettings.TRACK_FAVOURITES in trackSources,
|
||||
useHistory = AppSettings.TRACK_HISTORY in trackSources
|
||||
)
|
||||
if (tracks.isEmpty()) {
|
||||
return@withContext Result.success()
|
||||
return Result.success()
|
||||
}
|
||||
var success = 0
|
||||
for (track in tracks) {
|
||||
val details = safe {
|
||||
val details = runCatching {
|
||||
track.manga.source.repository.getDetails(track.manga)
|
||||
}
|
||||
}.getOrNull()
|
||||
val chapters = details?.chapters ?: continue
|
||||
when {
|
||||
track.knownChaptersCount == -1 -> { //first check
|
||||
@@ -125,7 +124,7 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
||||
success++
|
||||
}
|
||||
repository.cleanup()
|
||||
if (success == 0) {
|
||||
return if (success == 0) {
|
||||
Result.retry()
|
||||
} else {
|
||||
Result.success()
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.utils.ext
|
||||
import android.content.res.Resources
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.delay
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
|
||||
@@ -12,15 +11,6 @@ import org.koitharu.kotatsu.core.exceptions.WrongPasswordException
|
||||
import java.io.FileNotFoundException
|
||||
import java.net.SocketTimeoutException
|
||||
|
||||
inline fun <T, R> T.safe(action: T.() -> R?) = try {
|
||||
this.action()
|
||||
} catch (e: Throwable) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
suspend inline fun <T, R> T.retryUntilSuccess(maxAttempts: Int, action: T.() -> R): R {
|
||||
var attempts = maxAttempts
|
||||
while (true) {
|
||||
|
||||
@@ -35,11 +35,11 @@ inline fun File.findParent(predicate: (File) -> Boolean): File? {
|
||||
return current
|
||||
}
|
||||
|
||||
fun File.getStorageName(context: Context): String = safe {
|
||||
fun File.getStorageName(context: Context): String = runCatching {
|
||||
val manager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
manager.getStorageVolume(this)?.getDescription(context)?.let {
|
||||
return@safe it
|
||||
return@runCatching it
|
||||
}
|
||||
}
|
||||
when {
|
||||
@@ -47,6 +47,6 @@ fun File.getStorageName(context: Context): String = safe {
|
||||
Environment.isExternalStorageRemovable(this) -> context.getString(R.string.external_storage)
|
||||
else -> null
|
||||
}
|
||||
} ?: context.getString(R.string.other_storage)
|
||||
}.getOrNull() ?: context.getString(R.string.other_storage)
|
||||
|
||||
fun Uri.toFileOrNull() = if (scheme == "file") path?.let(::File) else null
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_list_mode"
|
||||
android:orderInCategory="20"
|
||||
android:title="@string/list_mode"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
@@ -190,4 +190,6 @@
|
||||
<string name="silent">Без звука</string>
|
||||
<string name="captcha_required">Необходимо пройти CAPTCHA</string>
|
||||
<string name="resolve">Resolve</string>
|
||||
<string name="clear_cookies">Очистить куки</string>
|
||||
<string name="cookies_cleared">Все куки удалены</string>
|
||||
</resources>
|
||||
@@ -192,4 +192,6 @@
|
||||
<string name="silent">Silent</string>
|
||||
<string name="captcha_required">CAPTCHA is required</string>
|
||||
<string name="resolve">Resolve</string>
|
||||
<string name="clear_cookies">Clear cookies</string>
|
||||
<string name="cookies_cleared">All cookies was removed</string>
|
||||
</resources>
|
||||
@@ -8,13 +8,6 @@
|
||||
<item name="android:paddingBottom">10dp</item>
|
||||
</style>
|
||||
|
||||
<style name="AppToggleButton.Vertical" parent="Widget.MaterialComponents.Button.OutlinedButton">
|
||||
<item name="android:checkable">true</item>
|
||||
<item name="android:gravity">center_horizontal</item>
|
||||
<item name="iconPadding">6dp</item>
|
||||
<item name="iconGravity">top</item>
|
||||
</style>
|
||||
|
||||
<style name="AppPopupTheme" parent="ThemeOverlay.MaterialComponents.Light" />
|
||||
|
||||
<style name="AppToolbarTheme" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar">
|
||||
@@ -32,8 +25,4 @@
|
||||
<item name="background">@color/grey</item>
|
||||
</style>
|
||||
|
||||
<style name="AppBadge" parent="Widget.MaterialComponents.Badge">
|
||||
<item name="backgroundColor">?attr/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -16,8 +16,8 @@
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
android:title="@string/cache">
|
||||
android:title="@string/cache"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<Preference
|
||||
android:key="thumbs_cache_clear"
|
||||
@@ -33,4 +33,10 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<Preference
|
||||
android:key="cookies_clear"
|
||||
android:persistent="false"
|
||||
android:title="@string/clear_cookies"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
</PreferenceScreen>
|
||||
Reference in New Issue
Block a user