Fixes and refactor
This commit is contained in:
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="1.8" />
|
<bytecodeTargetLevel target="11" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
3
.idea/gradle.xml
generated
3
.idea/gradle.xml
generated
@@ -4,7 +4,7 @@
|
|||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
<option name="testRunner" value="PLATFORM" />
|
<option name="testRunner" value="GRADLE" />
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="gradleJvm" value="1.8" />
|
<option name="gradleJvm" value="1.8" />
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
<option name="resolveModulePerSourceSet" value="false" />
|
<option name="resolveModulePerSourceSet" value="false" />
|
||||||
<option name="useQualifiedModuleNames" value="true" />
|
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
2
.idea/inspectionProfiles/Project_Default.xml
generated
2
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -2,6 +2,8 @@
|
|||||||
<profile version="1.0">
|
<profile version="1.0">
|
||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
<inspection_tool class="BooleanLiteralArgument" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
<inspection_tool class="BooleanLiteralArgument" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="KeySetIterationMayUseEntrySet" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
<inspection_tool class="TrailingComma" enabled="true" level="INFORMATION" enabled_by_default="true" />
|
<inspection_tool class="TrailingComma" enabled="true" level="INFORMATION" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ZeroLengthArrayInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
</profile>
|
</profile>
|
||||||
</component>
|
</component>
|
||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ dependencies {
|
|||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
|
||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.5.0-alpha05'
|
implementation 'androidx.core:core-ktx:1.5.0-beta01'
|
||||||
implementation 'androidx.activity:activity-ktx:1.2.0-rc01'
|
implementation 'androidx.activity:activity-ktx:1.2.0-rc01'
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.3.0-rc01'
|
implementation 'androidx.fragment:fragment-ktx:1.3.0-rc01'
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-rc01'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-rc01'
|
||||||
@@ -79,8 +79,8 @@ dependencies {
|
|||||||
implementation 'androidx.recyclerview:recyclerview:1.2.0-beta01'
|
implementation 'androidx.recyclerview:recyclerview:1.2.0-beta01'
|
||||||
implementation 'androidx.viewpager2:viewpager2:1.1.0-alpha01'
|
implementation 'androidx.viewpager2:viewpager2:1.1.0-alpha01'
|
||||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||||
implementation 'androidx.work:work-runtime-ktx:2.5.0-beta02'
|
implementation 'androidx.work:work-runtime-ktx:2.5.0-rc01'
|
||||||
implementation 'com.google.android.material:material:1.3.0-beta01'
|
implementation 'com.google.android.material:material:1.3.0-rc01'
|
||||||
//noinspection LifecycleAnnotationProcessorWithJava8
|
//noinspection LifecycleAnnotationProcessorWithJava8
|
||||||
kapt 'androidx.lifecycle:lifecycle-compiler:2.3.0-rc01'
|
kapt 'androidx.lifecycle:lifecycle-compiler:2.3.0-rc01'
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ dependencies {
|
|||||||
kapt 'androidx.room:room-compiler:2.2.6'
|
kapt 'androidx.room:room-compiler:2.2.6'
|
||||||
|
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
|
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
|
||||||
implementation 'com.squareup.okio:okio:2.9.0'
|
implementation 'com.squareup.okio:okio:2.10.0'
|
||||||
implementation 'org.jsoup:jsoup:1.13.1'
|
implementation 'org.jsoup:jsoup:1.13.1'
|
||||||
|
|
||||||
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.0'
|
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.0'
|
||||||
@@ -97,7 +97,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'org.koin:koin-android:2.2.2'
|
implementation 'org.koin:koin-android:2.2.2'
|
||||||
implementation 'org.koin:koin-androidx-viewmodel:2.2.2'
|
implementation 'org.koin:koin-androidx-viewmodel:2.2.2'
|
||||||
implementation 'io.coil-kt:coil-base:1.1.0'
|
implementation 'io.coil-kt:coil-base:1.1.1'
|
||||||
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
|
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
|
||||||
implementation 'com.tomclaw.cache:cache:1.0'
|
implementation 'com.tomclaw.cache:cache:1.0'
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import okhttp3.OkHttpClient
|
|||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import org.koitharu.kotatsu.utils.ext.safe
|
|
||||||
|
|
||||||
class BrowserClient(private val callback: BrowserCallback) : WebViewClient(), KoinComponent {
|
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)
|
return request?.url?.toString()?.let(::doRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doRequest(url: String): WebResourceResponse? = safe {
|
private fun doRequest(url: String): WebResourceResponse? = runCatching {
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
.build()
|
.build()
|
||||||
@@ -56,5 +55,5 @@ class BrowserClient(private val callback: BrowserCallback) : WebViewClient(), Ko
|
|||||||
ct?.charset()?.name() ?: "utf-8",
|
ct?.charset()?.name() ?: "utf-8",
|
||||||
response.body?.byteStream()
|
response.body?.byteStream()
|
||||||
)
|
)
|
||||||
}
|
}.getOrNull()
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,9 @@ import okhttp3.CookieJar
|
|||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.core.qualifier.named
|
import org.koin.core.qualifier.named
|
||||||
|
import org.koin.dsl.bind
|
||||||
import org.koin.dsl.module
|
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.PersistentCookieJar
|
||||||
import org.koitharu.kotatsu.core.network.cookies.cache.SetCookieCache
|
import org.koitharu.kotatsu.core.network.cookies.cache.SetCookieCache
|
||||||
import org.koitharu.kotatsu.core.network.cookies.persistence.SharedPrefsCookiePersistor
|
import org.koitharu.kotatsu.core.network.cookies.persistence.SharedPrefsCookiePersistor
|
||||||
@@ -18,7 +20,7 @@ val networkModule
|
|||||||
SetCookieCache(),
|
SetCookieCache(),
|
||||||
SharedPrefsCookiePersistor(androidContext())
|
SharedPrefsCookiePersistor(androidContext())
|
||||||
)
|
)
|
||||||
}
|
} bind ClearableCookieJar::class
|
||||||
single(named(CacheUtils.QUALIFIER_HTTP)) { CacheUtils.createHttpCache(androidContext()) }
|
single(named(CacheUtils.QUALIFIER_HTTP)) { CacheUtils.createHttpCache(androidContext()) }
|
||||||
single {
|
single {
|
||||||
OkHttpClient.Builder().apply {
|
OkHttpClient.Builder().apply {
|
||||||
|
|||||||
@@ -2,16 +2,22 @@ package org.koitharu.kotatsu.core.network
|
|||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.Response
|
||||||
import org.koitharu.kotatsu.BuildConfig
|
import org.koitharu.kotatsu.BuildConfig
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class UserAgentInterceptor : Interceptor {
|
class UserAgentInterceptor : Interceptor {
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain) = chain.proceed(
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
chain.request().newBuilder()
|
val request = chain.request()
|
||||||
.header(HEADER_USER_AGENT, userAgent)
|
return chain.proceed(
|
||||||
.build()
|
if (request.header(HEADER_USER_AGENT) == null) {
|
||||||
)
|
request.newBuilder()
|
||||||
|
.header(HEADER_USER_AGENT, userAgent)
|
||||||
|
.build()
|
||||||
|
} else request
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class PersistentCookieJar(
|
|||||||
cache.addAll(persistor.loadAll())
|
cache.addAll(persistor.loadAll())
|
||||||
}
|
}
|
||||||
cache.addAll(cookies)
|
cache.addAll(cookies)
|
||||||
persistor.saveAll(filterPersistentCookies(cookies))
|
persistor.saveAll(cookies.filter { it.persistent })
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@@ -48,11 +48,14 @@ class PersistentCookieJar(
|
|||||||
val it = cache.iterator()
|
val it = cache.iterator()
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
val currentCookie = it.next()
|
val currentCookie = it.next()
|
||||||
if (isCookieExpired(currentCookie)) {
|
when {
|
||||||
cookiesToRemove.add(currentCookie)
|
currentCookie.isExpired() -> {
|
||||||
it.remove()
|
cookiesToRemove.add(currentCookie)
|
||||||
} else if (currentCookie.matches(url)) {
|
it.remove()
|
||||||
validCookies.add(currentCookie)
|
}
|
||||||
|
currentCookie.matches(url) -> {
|
||||||
|
validCookies.add(currentCookie)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
persistor.removeAll(cookiesToRemove)
|
persistor.removeAll(cookiesToRemove)
|
||||||
@@ -73,18 +76,8 @@ class PersistentCookieJar(
|
|||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
|
||||||
fun filterPersistentCookies(cookies: List<Cookie>): List<Cookie> {
|
fun Cookie.isExpired(): Boolean {
|
||||||
val persistentCookies: MutableList<Cookie> = ArrayList()
|
return expiresAt < System.currentTimeMillis()
|
||||||
for (cookie in cookies) {
|
|
||||||
if (cookie.persistent) {
|
|
||||||
persistentCookies.add(cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return persistentCookies
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isCookieExpired(cookie: Cookie): Boolean {
|
|
||||||
return cookie.expiresAt < System.currentTimeMillis()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ val parserModule
|
|||||||
factory<MangaRepository>(named(MangaSource.HENCHAN)) { HenChanRepository(get()) }
|
factory<MangaRepository>(named(MangaSource.HENCHAN)) { HenChanRepository(get()) }
|
||||||
factory<MangaRepository>(named(MangaSource.YAOICHAN)) { YaoiChanRepository(get()) }
|
factory<MangaRepository>(named(MangaSource.YAOICHAN)) { YaoiChanRepository(get()) }
|
||||||
factory<MangaRepository>(named(MangaSource.MANGATOWN)) { MangaTownRepository(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.NUDEMOON)) { NudeMoonRepository(get()) }
|
||||||
factory<MangaRepository>(named(MangaSource.MANGAREAD)) { MangareadRepository(get()) }
|
factory<MangaRepository>(named(MangaSource.MANGAREAD)) { MangareadRepository(get()) }
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
|||||||
).firstOrNull()?.text(),
|
).firstOrNull()?.text(),
|
||||||
coverUrl = row.selectFirst("div.manga_images")?.selectFirst("img")
|
coverUrl = row.selectFirst("div.manga_images")?.selectFirst("img")
|
||||||
?.attr("src")?.withDomain(domain).orEmpty(),
|
?.attr("src")?.withDomain(domain).orEmpty(),
|
||||||
tags = safe {
|
tags = runCatching {
|
||||||
row.selectFirst("div.genre")?.select("a")?.mapToSet {
|
row.selectFirst("div.genre")?.select("a")?.mapToSet {
|
||||||
MangaTag(
|
MangaTag(
|
||||||
title = it.text(),
|
title = it.text(),
|
||||||
@@ -64,7 +64,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
|||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.orEmpty(),
|
}.getOrNull().orEmpty(),
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.koitharu.kotatsu.core.parser.site
|
package org.koitharu.kotatsu.core.parser.site
|
||||||
|
|
||||||
import androidx.collection.arraySetOf
|
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.base.domain.MangaLoaderContext
|
||||||
import org.koitharu.kotatsu.core.exceptions.ParseException
|
import org.koitharu.kotatsu.core.exceptions.ParseException
|
||||||
import org.koitharu.kotatsu.core.model.*
|
import org.koitharu.kotatsu.core.model.*
|
||||||
@@ -53,7 +53,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
|||||||
}.parseHtml()
|
}.parseHtml()
|
||||||
val root = doc.body().getElementById("mangaBox")
|
val root = doc.body().getElementById("mangaBox")
|
||||||
?.selectFirst("div.tiles.row") ?: throw ParseException("Cannot find root")
|
?.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 ->
|
return root.select("div.tile").mapNotNull { node ->
|
||||||
val imgDiv = node.selectFirst("div.img") ?: return@mapNotNull null
|
val imgDiv = node.selectFirst("div.img") ?: return@mapNotNull null
|
||||||
val descDiv = node.selectFirst("div.desc") ?: 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
|
return@mapNotNull null //skip author
|
||||||
}
|
}
|
||||||
val href = imgDiv.selectFirst("a").attr("href")?.inContextOf(node)
|
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
|
return@mapNotNull null // skip external links
|
||||||
}
|
}
|
||||||
val title = descDiv.selectFirst("h3")?.selectFirst("a")?.text()
|
val title = descDiv.selectFirst("h3")?.selectFirst("a")?.text()
|
||||||
@@ -73,15 +73,15 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
|||||||
title = title,
|
title = title,
|
||||||
altTitle = descDiv.selectFirst("h4")?.text(),
|
altTitle = descDiv.selectFirst("h4")?.text(),
|
||||||
coverUrl = imgDiv.selectFirst("img.lazy")?.attr("data-original").orEmpty(),
|
coverUrl = imgDiv.selectFirst("img.lazy")?.attr("data-original").orEmpty(),
|
||||||
rating = safe {
|
rating = runCatching {
|
||||||
node.selectFirst("div.rating")
|
node.selectFirst("div.rating")
|
||||||
?.attr("title")
|
?.attr("title")
|
||||||
?.substringBefore(' ')
|
?.substringBefore(' ')
|
||||||
?.toFloatOrNull()
|
?.toFloatOrNull()
|
||||||
?.div(10f)
|
?.div(10f)
|
||||||
} ?: Manga.NO_RATING,
|
}.getOrNull() ?: Manga.NO_RATING,
|
||||||
author = tileInfo?.selectFirst("a.person-link")?.text(),
|
author = tileInfo?.selectFirst("a.person-link")?.text(),
|
||||||
tags = safe {
|
tags = runCatching {
|
||||||
tileInfo?.select("a.element-link")
|
tileInfo?.select("a.element-link")
|
||||||
?.mapToSet {
|
?.mapToSet {
|
||||||
MangaTag(
|
MangaTag(
|
||||||
@@ -90,7 +90,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
|||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.orEmpty(),
|
}.getOrNull().orEmpty(),
|
||||||
state = when {
|
state = when {
|
||||||
node.selectFirst("div.tags")
|
node.selectFirst("div.tags")
|
||||||
?.selectFirst("span.mangaCompleted") != null -> MangaState.FINISHED
|
?.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 title = root.selectFirst("div.media-header__wrap")?.children()
|
||||||
val info = root.selectFirst("div.media-content")
|
val info = root.selectFirst("div.media-content")
|
||||||
val chaptersDoc = loaderContext.httpGet(manga.url + "?section=chapters").parseHtml()
|
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
|
var chapters: ArrayList<MangaChapter>? = null
|
||||||
scripts@ for (script in scripts) {
|
scripts@ for (script in scripts) {
|
||||||
val raw = script.html().lines()
|
val raw = script.html().lines()
|
||||||
for (line in raw) {
|
for (line in raw) {
|
||||||
if (line.startsWith("window.__CHAPTERS_DATA__")) {
|
if (line.startsWith("window.__DATA__")) {
|
||||||
val json = JSONObject(line.substringAfter('=').substringBeforeLast(';'))
|
val json = JSONObject(line.substringAfter('=').substringBeforeLast(';'))
|
||||||
val list = json.getJSONArray("list")
|
val list = json.getJSONObject("chapters").getJSONArray("list")
|
||||||
val total = list.length()
|
val total = list.length()
|
||||||
chapters = ArrayList(total)
|
chapters = ArrayList(total)
|
||||||
for (i in 0 until total) {
|
for (i in 0 until total) {
|
||||||
@@ -99,7 +99,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
|||||||
append("/c")
|
append("/c")
|
||||||
append(item.getString("chapter_number"))
|
append(item.getString("chapter_number"))
|
||||||
append('/')
|
append('/')
|
||||||
append(item.getJSONArray("teams").getJSONObject(0).optString("slug"))
|
append(item.optString("chapter_string"))
|
||||||
}
|
}
|
||||||
var name = item.getString("chapter_name")
|
var name = item.getString("chapter_name")
|
||||||
if (name.isNullOrBlank() || name == "null") {
|
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_SOURCES_HIDDEN = "sources_hidden"
|
||||||
const val KEY_TRAFFIC_WARNING = "traffic_warning"
|
const val KEY_TRAFFIC_WARNING = "traffic_warning"
|
||||||
const val KEY_PAGES_CACHE_CLEAR = "pages_cache_clear"
|
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_THUMBS_CACHE_CLEAR = "thumbs_cache_clear"
|
||||||
const val KEY_SEARCH_HISTORY_CLEAR = "search_history_clear"
|
const val KEY_SEARCH_HISTORY_CLEAR = "search_history_clear"
|
||||||
const val KEY_UPDATES_FEED_CLEAR = "updates_feed_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.local.domain.LocalMangaRepository
|
||||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||||
import org.koitharu.kotatsu.utils.ext.safe
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class DetailsViewModel(
|
class DetailsViewModel(
|
||||||
@@ -93,7 +92,7 @@ class DetailsViewModel(
|
|||||||
launchLoadingJob(Dispatchers.Default) {
|
launchLoadingJob(Dispatchers.Default) {
|
||||||
val original = localMangaRepository.getRemoteManga(manga)
|
val original = localMangaRepository.getRemoteManga(manga)
|
||||||
localMangaRepository.delete(manga) || throw IOException("Unable to delete file")
|
localMangaRepository.delete(manga) || throw IOException("Unable to delete file")
|
||||||
safe {
|
runCatching {
|
||||||
historyRepository.deleteOrSwap(manga, original)
|
historyRepository.deleteOrSwap(manga, original)
|
||||||
}
|
}
|
||||||
onMangaRemoved.postCall(manga)
|
onMangaRemoved.postCall(manga)
|
||||||
|
|||||||
@@ -92,13 +92,13 @@ class DownloadService : BaseService() {
|
|||||||
var output: MangaZip? = null
|
var output: MangaZip? = null
|
||||||
try {
|
try {
|
||||||
val repo = manga.source.repository
|
val repo = manga.source.repository
|
||||||
val cover = safe {
|
val cover = runCatching {
|
||||||
imageLoader.execute(
|
imageLoader.execute(
|
||||||
ImageRequest.Builder(this@DownloadService)
|
ImageRequest.Builder(this@DownloadService)
|
||||||
.data(manga.coverUrl)
|
.data(manga.coverUrl)
|
||||||
.build()
|
.build()
|
||||||
).drawable
|
).drawable
|
||||||
}
|
}.getOrNull()
|
||||||
notification.setLargeIcon(cover)
|
notification.setLargeIcon(cover)
|
||||||
notification.update()
|
notification.update()
|
||||||
val data = if (manga.chapters == null) repo.getDetails(manga) else manga
|
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.core.model.MangaTag
|
||||||
import org.koitharu.kotatsu.utils.ext.getStringOrNull
|
import org.koitharu.kotatsu.utils.ext.getStringOrNull
|
||||||
import org.koitharu.kotatsu.utils.ext.mapToSet
|
import org.koitharu.kotatsu.utils.ext.mapToSet
|
||||||
import org.koitharu.kotatsu.utils.ext.safe
|
|
||||||
|
|
||||||
class MangaIndex(source: String?) {
|
class MangaIndex(source: String?) {
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ class MangaIndex(source: String?) {
|
|||||||
json.put("app_version", BuildConfig.VERSION_CODE)
|
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"))
|
val source = MangaSource.valueOf(json.getString("source"))
|
||||||
Manga(
|
Manga(
|
||||||
id = json.getLong("id"),
|
id = json.getLong("id"),
|
||||||
@@ -60,7 +59,7 @@ class MangaIndex(source: String?) {
|
|||||||
},
|
},
|
||||||
chapters = getChapters(json.getJSONObject("chapters"), source)
|
chapters = getChapters(json.getJSONObject("chapters"), source)
|
||||||
)
|
)
|
||||||
}
|
}.getOrNull()
|
||||||
|
|
||||||
fun getCoverEntry(): String? = json.optString("cover_entry")
|
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.AlphanumComparator
|
||||||
import org.koitharu.kotatsu.utils.ext.longHashCode
|
import org.koitharu.kotatsu.utils.ext.longHashCode
|
||||||
import org.koitharu.kotatsu.utils.ext.readText
|
import org.koitharu.kotatsu.utils.ext.readText
|
||||||
import org.koitharu.kotatsu.utils.ext.safe
|
|
||||||
import org.koitharu.kotatsu.utils.ext.sub
|
import org.koitharu.kotatsu.utils.ext.sub
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -37,7 +36,7 @@ class LocalMangaRepository(private val context: Context) : MangaRepository {
|
|||||||
}
|
}
|
||||||
val files = getAvailableStorageDirs(context)
|
val files = getAvailableStorageDirs(context)
|
||||||
.flatMap { x -> x.listFiles(filenameFilter)?.toList().orEmpty() }
|
.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) {
|
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? {
|
fun getRemoteManga(localManga: Manga): Manga? {
|
||||||
val file = safe {
|
val file = runCatching {
|
||||||
Uri.parse(localManga.url).toFile()
|
Uri.parse(localManga.url).toFile()
|
||||||
} ?: return null
|
}.getOrNull() ?: return null
|
||||||
val zip = ZipFile(file)
|
val zip = ZipFile(file)
|
||||||
val entry = zip.getEntry(MangaZip.INDEX_ENTRY)
|
val entry = zip.getEntry(MangaZip.INDEX_ENTRY)
|
||||||
val index = entry?.let(zip::readText)?.let(::MangaIndex) ?: return null
|
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.MediaStoreCompat
|
||||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||||
import org.koitharu.kotatsu.utils.ext.safe
|
|
||||||
import org.koitharu.kotatsu.utils.ext.sub
|
import org.koitharu.kotatsu.utils.ext.sub
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
@@ -49,7 +48,10 @@ class LocalListViewModel(
|
|||||||
list.isEmpty() -> listOf(EmptyState(R.string.text_local_holder))
|
list.isEmpty() -> listOf(EmptyState(R.string.text_local_holder))
|
||||||
else -> list.toUi(mode)
|
else -> list.toUi(mode)
|
||||||
}
|
}
|
||||||
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))
|
}.asLiveDataDistinct(
|
||||||
|
viewModelScope.coroutineContext + Dispatchers.Default,
|
||||||
|
listOf(LoadingState)
|
||||||
|
)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
onRefresh()
|
onRefresh()
|
||||||
@@ -94,7 +96,7 @@ class LocalListViewModel(
|
|||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
val original = repository.getRemoteManga(manga)
|
val original = repository.getRemoteManga(manga)
|
||||||
repository.delete(manga) || throw IOException("Unable to delete file")
|
repository.delete(manga) || throw IOException("Unable to delete file")
|
||||||
safe {
|
runCatching {
|
||||||
historyRepository.deleteOrSwap(manga, original)
|
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.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
@@ -5,7 +5,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
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>(
|
abstract class BasePageHolder<B : ViewBinding>(
|
||||||
protected val binding: B,
|
protected val binding: B,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import androidx.viewbinding.ViewBinding
|
|||||||
import org.koin.android.ext.android.get
|
import org.koin.android.ext.android.get
|
||||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
||||||
import org.koitharu.kotatsu.base.ui.BaseFragment
|
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.ReaderState
|
||||||
import org.koitharu.kotatsu.reader.ui.ReaderViewModel
|
import org.koitharu.kotatsu.reader.ui.ReaderViewModel
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import androidx.recyclerview.widget.DiffUtil
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
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 org.koitharu.kotatsu.utils.ext.resetTransformations
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
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.MangaPage
|
||||||
import org.koitharu.kotatsu.core.model.ZoomMode
|
import org.koitharu.kotatsu.core.model.ZoomMode
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
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.launchAfter
|
||||||
import org.koitharu.kotatsu.utils.ext.launchInstead
|
import org.koitharu.kotatsu.utils.ext.launchInstead
|
||||||
import java.io.File
|
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.model.ZoomMode
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.databinding.ItemPageBinding
|
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
|
import org.koitharu.kotatsu.reader.ui.pager.standard.PageHolder
|
||||||
|
|
||||||
class ReversedPageHolder(
|
class ReversedPageHolder(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import android.view.ViewGroup
|
|||||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.databinding.ItemPageBinding
|
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
|
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||||
|
|
||||||
class ReversedPagesAdapter(
|
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.model.ZoomMode
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.databinding.ItemPageBinding
|
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.BasePageHolder
|
||||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
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.exceptions.resolve.ExceptionResolver
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.databinding.ItemPageBinding
|
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
|
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||||
|
|
||||||
class PagesAdapter(
|
class PagesAdapter(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import android.view.ViewGroup
|
|||||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding
|
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
|
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||||
|
|
||||||
class WebtoonAdapter(
|
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.model.ZoomMode
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding
|
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.BasePageHolder
|
||||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import android.view.MenuItem
|
|||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.search.ui.global.GlobalSearchActivity
|
import org.koitharu.kotatsu.search.ui.global.GlobalSearchActivity
|
||||||
import org.koitharu.kotatsu.utils.ext.safe
|
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
|
||||||
object SearchHelper {
|
object SearchHelper {
|
||||||
@@ -43,10 +42,10 @@ object SearchHelper {
|
|||||||
override fun onSuggestionSelect(position: Int) = false
|
override fun onSuggestionSelect(position: Int) = false
|
||||||
|
|
||||||
override fun onSuggestionClick(position: Int): Boolean {
|
override fun onSuggestionClick(position: Int): Boolean {
|
||||||
val query = safe {
|
val query = runCatching {
|
||||||
val c = view.suggestionsAdapter.getItem(position) as? Cursor
|
val c = view.suggestionsAdapter.getItem(position) as? Cursor
|
||||||
c?.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY))
|
c?.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY))
|
||||||
} ?: return false
|
}.getOrNull() ?: return false
|
||||||
view.setQuery(query, true)
|
view.setQuery(query, true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ import com.google.android.material.snackbar.Snackbar
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.koin.android.ext.android.get
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
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.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.local.data.Cache
|
import org.koitharu.kotatsu.local.data.Cache
|
||||||
import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider
|
import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider
|
||||||
@@ -71,6 +73,20 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
|
|||||||
clearCache(preference, Cache.THUMBS)
|
clearCache(preference, Cache.THUMBS)
|
||||||
true
|
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 -> {
|
AppSettings.KEY_SEARCH_HISTORY_CLEAR -> {
|
||||||
viewLifecycleScope.launch {
|
viewLifecycleScope.launch {
|
||||||
MangaSuggestionsProvider.clearHistory(preference.context)
|
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.model.MangaSource
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.utils.ext.mapToSet
|
import org.koitharu.kotatsu.utils.ext.mapToSet
|
||||||
import org.koitharu.kotatsu.utils.ext.safe
|
|
||||||
|
|
||||||
class SourcesAdapter(
|
class SourcesAdapter(
|
||||||
private val settings: AppSettings,
|
private val settings: AppSettings,
|
||||||
private val onItemClickListener: OnListItemClickListener<MangaSource>,
|
private val onItemClickListener: OnListItemClickListener<MangaSource>,
|
||||||
) : RecyclerView.Adapter<SourceViewHolder>() {
|
) : 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 {
|
private val hiddenItems = settings.hiddenSources.mapNotNull {
|
||||||
safe {
|
runCatching {
|
||||||
MangaSource.valueOf(it)
|
MangaSource.valueOf(it)
|
||||||
}
|
}.getOrNull()
|
||||||
}.toMutableSet()
|
}.toMutableSet()
|
||||||
|
|
||||||
override fun onCreateViewHolder(
|
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.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
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.toBitmapOrNull
|
||||||
import org.koitharu.kotatsu.utils.ext.toUriOrNull
|
import org.koitharu.kotatsu.utils.ext.toUriOrNull
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@@ -37,23 +36,23 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
|||||||
private val repository by inject<TrackingRepository>()
|
private val repository by inject<TrackingRepository>()
|
||||||
private val settings by inject<AppSettings>()
|
private val settings by inject<AppSettings>()
|
||||||
|
|
||||||
override suspend fun doWork(): Result = withContext(Dispatchers.Default) {
|
override suspend fun doWork(): Result {
|
||||||
val trackSources = settings.trackSources
|
val trackSources = settings.trackSources
|
||||||
if (trackSources.isEmpty()) {
|
if (trackSources.isEmpty()) {
|
||||||
return@withContext Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
val tracks = repository.getAllTracks(
|
val tracks = repository.getAllTracks(
|
||||||
useFavourites = AppSettings.TRACK_FAVOURITES in trackSources,
|
useFavourites = AppSettings.TRACK_FAVOURITES in trackSources,
|
||||||
useHistory = AppSettings.TRACK_HISTORY in trackSources
|
useHistory = AppSettings.TRACK_HISTORY in trackSources
|
||||||
)
|
)
|
||||||
if (tracks.isEmpty()) {
|
if (tracks.isEmpty()) {
|
||||||
return@withContext Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
var success = 0
|
var success = 0
|
||||||
for (track in tracks) {
|
for (track in tracks) {
|
||||||
val details = safe {
|
val details = runCatching {
|
||||||
track.manga.source.repository.getDetails(track.manga)
|
track.manga.source.repository.getDetails(track.manga)
|
||||||
}
|
}.getOrNull()
|
||||||
val chapters = details?.chapters ?: continue
|
val chapters = details?.chapters ?: continue
|
||||||
when {
|
when {
|
||||||
track.knownChaptersCount == -1 -> { //first check
|
track.knownChaptersCount == -1 -> { //first check
|
||||||
@@ -125,7 +124,7 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
|||||||
success++
|
success++
|
||||||
}
|
}
|
||||||
repository.cleanup()
|
repository.cleanup()
|
||||||
if (success == 0) {
|
return if (success == 0) {
|
||||||
Result.retry()
|
Result.retry()
|
||||||
} else {
|
} else {
|
||||||
Result.success()
|
Result.success()
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.utils.ext
|
|||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import org.koitharu.kotatsu.BuildConfig
|
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||||
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
|
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
|
||||||
@@ -12,15 +11,6 @@ import org.koitharu.kotatsu.core.exceptions.WrongPasswordException
|
|||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.net.SocketTimeoutException
|
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 {
|
suspend inline fun <T, R> T.retryUntilSuccess(maxAttempts: Int, action: T.() -> R): R {
|
||||||
var attempts = maxAttempts
|
var attempts = maxAttempts
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ inline fun File.findParent(predicate: (File) -> Boolean): File? {
|
|||||||
return current
|
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
|
val manager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
manager.getStorageVolume(this)?.getDescription(context)?.let {
|
manager.getStorageVolume(this)?.getDescription(context)?.let {
|
||||||
return@safe it
|
return@runCatching it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when {
|
when {
|
||||||
@@ -47,6 +47,6 @@ fun File.getStorageName(context: Context): String = safe {
|
|||||||
Environment.isExternalStorageRemovable(this) -> context.getString(R.string.external_storage)
|
Environment.isExternalStorageRemovable(this) -> context.getString(R.string.external_storage)
|
||||||
else -> null
|
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
|
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="silent">Без звука</string>
|
||||||
<string name="captcha_required">Необходимо пройти CAPTCHA</string>
|
<string name="captcha_required">Необходимо пройти CAPTCHA</string>
|
||||||
<string name="resolve">Resolve</string>
|
<string name="resolve">Resolve</string>
|
||||||
|
<string name="clear_cookies">Очистить куки</string>
|
||||||
|
<string name="cookies_cleared">Все куки удалены</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -192,4 +192,6 @@
|
|||||||
<string name="silent">Silent</string>
|
<string name="silent">Silent</string>
|
||||||
<string name="captcha_required">CAPTCHA is required</string>
|
<string name="captcha_required">CAPTCHA is required</string>
|
||||||
<string name="resolve">Resolve</string>
|
<string name="resolve">Resolve</string>
|
||||||
|
<string name="clear_cookies">Clear cookies</string>
|
||||||
|
<string name="cookies_cleared">All cookies was removed</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -8,13 +8,6 @@
|
|||||||
<item name="android:paddingBottom">10dp</item>
|
<item name="android:paddingBottom">10dp</item>
|
||||||
</style>
|
</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="AppPopupTheme" parent="ThemeOverlay.MaterialComponents.Light" />
|
||||||
|
|
||||||
<style name="AppToolbarTheme" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar">
|
<style name="AppToolbarTheme" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar">
|
||||||
@@ -32,8 +25,4 @@
|
|||||||
<item name="background">@color/grey</item>
|
<item name="background">@color/grey</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppBadge" parent="Widget.MaterialComponents.Badge">
|
|
||||||
<item name="backgroundColor">?attr/colorAccent</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -16,8 +16,8 @@
|
|||||||
app:iconSpaceReserved="false" />
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
app:iconSpaceReserved="false"
|
android:title="@string/cache"
|
||||||
android:title="@string/cache">
|
app:iconSpaceReserved="false">
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="thumbs_cache_clear"
|
android:key="thumbs_cache_clear"
|
||||||
@@ -33,4 +33,10 @@
|
|||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="cookies_clear"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/clear_cookies"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
Reference in New Issue
Block a user