Optimize imports, disallow wildcard imports because of klint, run linter

This commit is contained in:
jobobby04 2020-04-04 16:30:05 -04:00 committed by Jobobby04
parent f18891a07e
commit 23ac3d18e5
138 changed files with 1192 additions and 1027 deletions

View File

@ -43,7 +43,6 @@ class CategoryPutResolver : DefaultPutResolver<Category>() {
put(COL_FLAGS, obj.flags)
val orderString = obj.mangaOrder.joinToString("/")
put(COL_MANGA_ORDER, orderString)
}
}

View File

@ -29,6 +29,4 @@ class MangaUrlPutResolver : PutResolver<Manga>() {
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
put(MangaTable.COL_URL, manga.url)
}
}

View File

@ -24,7 +24,6 @@ object CategoryTable {
$COL_MANGA_ORDER TEXT NOT NULL
)"""
val addMangaOrder: String
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_MANGA_ORDER TEXT"
}

View File

@ -9,11 +9,11 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata
import kotlin.reflect.KClass
import rx.Completable
import rx.Single
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.reflect.KClass
/**
* LEWD!

View File

@ -3,7 +3,12 @@ package eu.kanade.tachiyomi.source.online.all
import android.content.Context
import android.net.Uri
import com.elvishew.xlog.XLog
import com.github.salomonbrys.kotson.*
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.obj
import com.github.salomonbrys.kotson.set
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser
@ -13,7 +18,12 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.asObservableWithAsyncStacktrace
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
@ -36,23 +46,30 @@ import exh.util.UriFilter
import exh.util.UriGroup
import exh.util.ignore
import exh.util.urlImportFetchSearchManga
import java.net.URLEncoder
import java.util.ArrayList
import kotlinx.coroutines.runBlocking
import okhttp3.*
import okhttp3.CacheControl
import okhttp3.CookieJar
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.nodes.TextNode
import rx.Observable
import rx.Single
import uy.kohesive.injekt.injectLazy
import java.net.URLEncoder
import java.util.*
// TODO Consider gallery updating when doing tabbed browsing
class EHentai(override val id: Long,
class EHentai(
override val id: Long,
val exh: Boolean,
val context: Context) : HttpSource(), LewdSource<EHentaiSearchMetadata, Document>, UrlImportableSource {
val context: Context
) : HttpSource(), LewdSource<EHentaiSearchMetadata, Document>, UrlImportableSource {
override val metaClass = EHentaiSearchMetadata::class
val schema: String
@ -111,8 +128,8 @@ class EHentai(override val id: Long,
val parsedLocation = doc.location().toHttpUrlOrNull()
// Add to page if required
val hasNextPage = if (parsedLocation == null
|| !parsedLocation.queryParameterNames.contains(REVERSE_PARAM)) {
val hasNextPage = if (parsedLocation == null ||
!parsedLocation.queryParameterNames.contains(REVERSE_PARAM)) {
select("a[onclick=return false]").last()?.let {
it.text() == ">"
} ?: false
@ -212,8 +229,11 @@ class EHentai(override val id: Long,
}
}!!
private fun fetchChapterPage(chapter: SChapter, np: String,
pastUrls: List<String> = emptyList()): Observable<List<String>> {
private fun fetchChapterPage(
chapter: SChapter,
np: String,
pastUrls: List<String> = emptyList()
): Observable<List<String>> {
val urls = ArrayList(pastUrls)
return chapterPageCall(np).flatMap {
val jsoup = it.asJsoup()
@ -407,8 +427,8 @@ class EHentai(override val id: Long,
}
lastUpdateCheck = System.currentTimeMillis()
if (datePosted != null
&& lastUpdateCheck - datePosted!! > EHentaiUpdateWorkerConstants.GALLERY_AGE_TIME) {
if (datePosted != null &&
lastUpdateCheck - datePosted!! > EHentaiUpdateWorkerConstants.GALLERY_AGE_TIME) {
aged = true
XLog.d("aged %s - too old", title)
}
@ -713,7 +733,6 @@ class EHentai(override val id: Long,
return "${uri.scheme}://${uri.host}/g/${obj["gid"].int}/${obj["token"].string}/"
}
companion object {
private const val QUERY_PREFIX = "?f_apply=Apply+Filter"
private const val TR_SUFFIX = "TR"
@ -738,6 +757,5 @@ class EHentai(override val id: Long,
fun buildCookies(cookies: Map<String, String>) = cookies.entries.joinToString(separator = "; ") {
"${URLEncoder.encode(it.key, "UTF-8")}=${URLEncoder.encode(it.value, "UTF-8")}"
}
}
}

View File

@ -10,7 +10,11 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
@ -24,6 +28,8 @@ import exh.metadata.metadata.HitomiSearchMetadata.Companion.TAG_TYPE_DEFAULT
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.metadata.base.RaisedTag
import exh.util.urlImportFetchSearchManga
import java.text.SimpleDateFormat
import java.util.Locale
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
@ -32,8 +38,6 @@ import rx.Observable
import rx.Single
import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.*
/**
* Man, I hate this source :(
@ -61,8 +65,8 @@ class Hitomi : HttpSource(), LewdSource<HitomiSearchMetadata, Document>, UrlImpo
private var tagIndexVersionCacheTime: Long = 0
private fun tagIndexVersion(): Single<Long> {
val sCachedTagIndexVersion = cachedTagIndexVersion
return if (sCachedTagIndexVersion == null
|| tagIndexVersionCacheTime + INDEX_VERSION_CACHE_TIME_MS < System.currentTimeMillis()) {
return if (sCachedTagIndexVersion == null ||
tagIndexVersionCacheTime + INDEX_VERSION_CACHE_TIME_MS < System.currentTimeMillis()) {
HitomiNozomi.getIndexVersion(client, "tagindex").subscribeOn(Schedulers.io()).doOnNext {
cachedTagIndexVersion = it
tagIndexVersionCacheTime = System.currentTimeMillis()
@ -76,8 +80,8 @@ class Hitomi : HttpSource(), LewdSource<HitomiSearchMetadata, Document>, UrlImpo
private var galleryIndexVersionCacheTime: Long = 0
private fun galleryIndexVersion(): Single<Long> {
val sCachedGalleryIndexVersion = cachedGalleryIndexVersion
return if (sCachedGalleryIndexVersion == null
|| galleryIndexVersionCacheTime + INDEX_VERSION_CACHE_TIME_MS < System.currentTimeMillis()) {
return if (sCachedGalleryIndexVersion == null ||
galleryIndexVersionCacheTime + INDEX_VERSION_CACHE_TIME_MS < System.currentTimeMillis()) {
HitomiNozomi.getIndexVersion(client, "galleriesindex").subscribeOn(Schedulers.io()).doOnNext {
cachedGalleryIndexVersion = it
galleryIndexVersionCacheTime = System.currentTimeMillis()
@ -307,7 +311,6 @@ class Hitomi : HttpSource(), LewdSource<HitomiSearchMetadata, Document>, UrlImpo
}
}
/**
* Returns an observable with the updated details for a manga. Normally it's not needed to
* override this method.
@ -423,5 +426,4 @@ class Hitomi : HttpSource(), LewdSource<HitomiSearchMetadata, Document>, UrlImpo
SimpleDateFormat("yyyy-MM-dd HH:mm:ss'-05'", Locale.US)
}
}
}

View File

@ -2,13 +2,22 @@ package eu.kanade.tachiyomi.source.online.all
import android.content.Context
import android.net.Uri
import com.github.salomonbrys.kotson.*
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.nullArray
import com.github.salomonbrys.kotson.nullLong
import com.github.salomonbrys.kotson.nullObj
import com.github.salomonbrys.kotson.nullString
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
@ -305,7 +314,6 @@ class NHentai(context: Context) : HttpSource(), LewdSource<NHentaiSearchMetadata
Pair("Chinese", " chinese")
)
val jsonParser by lazy {
JsonParser()
}

View File

@ -3,7 +3,12 @@ package eu.kanade.tachiyomi.source.online.all
import android.net.Uri
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
@ -17,14 +22,15 @@ import exh.metadata.metadata.base.RaisedTag
import exh.util.UriFilter
import exh.util.UriGroup
import exh.util.urlImportFetchSearchManga
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.nodes.TextNode
import rx.Observable
import java.text.SimpleDateFormat
import java.util.*
// TODO Transform into delegated source
class PervEden(override val id: Long, val pvLang: PervEdenLang) : ParsedHttpSource(),

View File

@ -2,10 +2,14 @@ package eu.kanade.tachiyomi.source.online.english
import android.net.Uri
import com.kizitonwose.time.hours
import hu.akarnokd.rxjava.interop.RxJavaInterop
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
@ -17,13 +21,18 @@ import exh.util.CachedField
import exh.util.NakedTrie
import exh.util.await
import exh.util.urlImportFetchSearchManga
import hu.akarnokd.rxjava.interop.RxJavaInterop
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.rx2.asSingle
import kotlinx.coroutines.withContext
import okhttp3.*
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element

View File

@ -4,12 +4,16 @@ import android.net.Uri
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import hu.akarnokd.rxjava.interop.RxJavaInterop
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
@ -23,7 +27,9 @@ import exh.search.Text
import exh.util.await
import exh.util.dropBlank
import exh.util.urlImportFetchSearchManga
import hu.akarnokd.rxjava.interop.RxJavaInterop
import info.debatty.java.stringsimilarity.Levenshtein
import kotlin.math.ceil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
@ -36,7 +42,6 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import rx.schedulers.Schedulers
import kotlin.math.ceil
class HBrowse : HttpSource(), LewdSource<HBrowseSearchMetadata, Document>, UrlImportableSource {
/**
@ -182,7 +187,6 @@ class HBrowse : HttpSource(), LewdSource<HBrowseSearchMetadata, Document>, UrlIm
}
}
"/result"
} else {
"/search"

View File

@ -4,29 +4,25 @@ import android.net.Uri
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.util.system.asJsoup
import exh.metadata.metadata.TsuminoSearchMetadata
import exh.metadata.metadata.TsuminoSearchMetadata.Companion.BASE_URL
import exh.metadata.metadata.TsuminoSearchMetadata.Companion.TAG_TYPE_DEFAULT
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource
import exh.util.dropBlank
import exh.util.trimAll
import exh.util.urlImportFetchSearchManga
import java.text.SimpleDateFormat
import java.util.Locale
import org.jsoup.nodes.Document
import rx.Observable
import java.text.SimpleDateFormat
import java.util.*
class Tsumino(delegate: HttpSource) : DelegatedHttpSource(delegate),
LewdSource<TsuminoSearchMetadata, Document>, UrlImportableSource {
override val metaClass = TsuminoSearchMetadata::class;
override val metaClass = TsuminoSearchMetadata::class
override val lang = "en"
// Support direct URL importing

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.source.filter
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem
import eu.davidea.flexibleadapter.items.IFlexible

View File

@ -5,21 +5,24 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.ui.category.CategoryAdapter
import exh.isLewdSource
import exh.metadata.sql.tables.SearchMetadataTable
import exh.search.SearchEngine
import exh.util.await
import exh.util.cancellable
import kotlinx.coroutines.*
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import uy.kohesive.injekt.injectLazy
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.SelectableAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.ui.category.CategoryAdapter
/**
* Adapter storing a list of manga in a certain category.

View File

@ -1,8 +1,8 @@
package eu.kanade.tachiyomi.ui.library
import android.view.View
import eu.davidea.flexibleadapter.FlexibleAdapter
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
@ -26,7 +26,6 @@ abstract class LibraryHolder(
*/
abstract fun onSetValues(item: LibraryItem)
/**
* Called when an item is released.
*
@ -36,5 +35,4 @@ abstract class LibraryHolder(
super.onItemReleased(position)
(adapter as? LibraryCategoryAdapter)?.onItemReleaseListener?.onItemReleased(position)
}
}

View File

@ -235,4 +235,3 @@ class MangaInfoPresenter(
return toInsert
}
}

View File

@ -17,7 +17,17 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.preference.PreferenceKeys
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.util.preference.*
import eu.kanade.tachiyomi.util.preference.defaultValue
import eu.kanade.tachiyomi.util.preference.entriesRes
import eu.kanade.tachiyomi.util.preference.intListPreference
import eu.kanade.tachiyomi.util.preference.listPreference
import eu.kanade.tachiyomi.util.preference.multiSelectListPreference
import eu.kanade.tachiyomi.util.preference.onChange
import eu.kanade.tachiyomi.util.preference.onClick
import eu.kanade.tachiyomi.util.preference.preference
import eu.kanade.tachiyomi.util.preference.preferenceCategory
import eu.kanade.tachiyomi.util.preference.summaryRes
import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.system.toast
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
@ -34,6 +44,7 @@ import exh.ui.login.LoginController
import exh.util.await
import exh.util.trans
import humanize.Humanize
import java.util.Date
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@ -41,7 +52,6 @@ import kotlinx.coroutines.withContext
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy
import java.util.*
/**
* EH Settings fragment

View File

@ -53,7 +53,7 @@ private val lewdDelegatedSourceIds = SourceManager.DELEGATED_SOURCES.filter {
}.map { it.value.sourceId }.sorted()
// This method MUST be fast!
fun isLewdSource(source: Long) = source in 6900..6999
|| lewdDelegatedSourceIds.binarySearch(source) >= 0
fun isLewdSource(source: Long) = source in 6900..6999 ||
lewdDelegatedSourceIds.binarySearch(source) >= 0
fun Source.isEhBasedSource() = id == EH_SOURCE_ID || id == EXH_SOURCE_ID

View File

@ -2,6 +2,8 @@ package exh
import eu.kanade.tachiyomi.source.model.FilterList
data class EXHSavedSearch(val name: String,
data class EXHSavedSearch(
val name: String,
val query: String,
val filterList: FilterList)
val filterList: FilterList
)

View File

@ -16,10 +16,12 @@ class GalleryAdder {
private val sourceManager: SourceManager by injectLazy()
fun addGallery(url: String,
fun addGallery(
url: String,
fav: Boolean = false,
forceSource: UrlImportableSource? = null,
throttleFunc: () -> Unit = {}): GalleryAddEvent {
throttleFunc: () -> Unit = {}
): GalleryAddEvent {
XLog.d("Importing gallery (url: %s, fav: %s, forceSource: %s)...", url, fav, forceSource)
try {
val uri = Uri.parse(url)
@ -120,8 +122,10 @@ sealed class GalleryAddEvent {
abstract val galleryUrl: String
open val galleryTitle: String? = null
class Success(override val galleryUrl: String,
val manga: Manga): GalleryAddEvent() {
class Success(
override val galleryUrl: String,
val manga: Manga
) : GalleryAddEvent() {
override val galleryTitle = manga.title
override val logMessage = "Added gallery: $galleryTitle"
}
@ -131,8 +135,10 @@ sealed class GalleryAddEvent {
override val logMessage = "Unknown gallery type for gallery: $galleryUrl"
}
open class Error(override val galleryUrl: String,
override val logMessage: String): Fail()
open class Error(
override val galleryUrl: String,
override val logMessage: String
) : Fail()
class NotFound(galleryUrl: String) :
Error(galleryUrl, "Gallery does not exist: $galleryUrl")

View File

@ -9,20 +9,19 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.system.jobScheduler
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.EXHMigrations
import exh.EXH_SOURCE_ID
import exh.eh.EHentaiUpdateWorker
import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata
import exh.util.await
import exh.util.cancellable
import uy.kohesive.injekt.injectLazy
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.toList
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.runBlocking
import uy.kohesive.injekt.injectLazy
object DebugFunctions {
val app: Application by injectLazy()

View File

@ -8,8 +8,12 @@ import android.widget.HorizontalScrollView
import android.widget.TextView
import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog
import eu.kanade.tachiyomi.ui.setting.*
import eu.kanade.tachiyomi.util.preference.*
import eu.kanade.tachiyomi.ui.setting.SettingsController
import eu.kanade.tachiyomi.util.preference.defaultValue
import eu.kanade.tachiyomi.util.preference.onClick
import eu.kanade.tachiyomi.util.preference.preference
import eu.kanade.tachiyomi.util.preference.preferenceCategory
import eu.kanade.tachiyomi.util.preference.switchPreference
import kotlin.reflect.KVisibility
import kotlin.reflect.full.declaredFunctions

View File

@ -1,7 +1,9 @@
package exh.eh
class EHentaiThrottleManager(private val max: Int = THROTTLE_MAX,
private val inc: Int = THROTTLE_INC) {
class EHentaiThrottleManager(
private val max: Int = THROTTLE_MAX,
private val inc: Int = THROTTLE_INC
) {
private var lastThrottleTime: Long = 0
var throttleTime: Long = 0
private set

View File

@ -8,10 +8,10 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga
import java.io.File
import rx.Observable
import rx.Single
import uy.kohesive.injekt.injectLazy
import java.io.File
data class ChapterChain(val manga: Manga, val chapters: List<Chapter>)
@ -75,9 +75,9 @@ class EHentaiUpdateHelper(context: Context) {
chain.chapters.map { chapter ->
// Convert old style chapters to new style chapters if possible
if(chapter.date_upload <= 0
&& meta?.datePosted != null
&& meta?.title != null) {
if (chapter.date_upload <= 0 &&
meta?.datePosted != null &&
meta?.title != null) {
chapter.name = meta!!.title!!
chapter.date_upload = meta!!.datePosted!!
}

View File

@ -20,8 +20,8 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.util.system.jobScheduler
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.system.jobScheduler
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.debug.DebugToggles
@ -31,15 +31,20 @@ import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata
import exh.util.await
import exh.util.cancellable
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import kotlin.coroutines.CoroutineContext
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class EHentaiUpdateWorker : JobService(), CoroutineScope {
@ -215,8 +220,8 @@ class EHentaiUpdateWorker: JobService(), CoroutineScope {
val (acceptedRoot, discardedRoots, hasNew) =
updateHelper.findAcceptedRootAndDiscardOthers(manga.source, chapters).await()
if((new.isNotEmpty() && manga.id == acceptedRoot.manga.id)
|| (hasNew && updatedManga.none { it.id == acceptedRoot.manga.id })) {
if ((new.isNotEmpty() && manga.id == acceptedRoot.manga.id) ||
(hasNew && updatedManga.none { it.id == acceptedRoot.manga.id })) {
updatedManga += acceptedRoot.manga
}
@ -290,9 +295,11 @@ class EHentaiUpdateWorker: JobService(), CoroutineScope {
else JOB_ID_UPDATE_BACKGROUND, componentName())
}
private fun Context.periodicBackgroundJobInfo(period: Long,
private fun Context.periodicBackgroundJobInfo(
period: Long,
requireCharging: Boolean,
requireUnmetered: Boolean): JobInfo {
requireUnmetered: Boolean
): JobInfo {
return baseBackgroundJobInfo(false)
.setPeriodic(period)
.setPersisted(true)

View File

@ -3,9 +3,6 @@ package exh.eh
import android.util.SparseArray
import androidx.core.util.AtomicFile
import com.elvishew.xlog.XLog
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.io.Closeable
import java.io.File
import java.io.FileNotFoundException
@ -13,6 +10,18 @@ import java.io.InputStream
import java.nio.ByteBuffer
import kotlin.concurrent.thread
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
/**
* In memory Int -> Obj lookup table implementation that

View File

@ -5,7 +5,7 @@ import io.realm.RealmObject
import io.realm.annotations.Index
import io.realm.annotations.PrimaryKey
import io.realm.annotations.RealmClass
import java.util.*
import java.util.UUID
@RealmClass
open class FavoriteEntry : RealmObject() {

View File

@ -17,18 +17,21 @@ import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.powerManager
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.system.wifiManager
import exh.*
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.GalleryAddEvent
import exh.GalleryAdder
import exh.eh.EHentaiThrottleManager
import exh.eh.EHentaiUpdateWorker
import exh.util.ignore
import exh.util.trans
import kotlin.concurrent.thread
import okhttp3.FormBody
import okhttp3.Request
import rx.subjects.BehaviorSubject
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import kotlin.concurrent.thread
class FavoritesSyncHelper(val context: Context) {
private val db: DatabaseHelper by injectLazy()
@ -385,8 +388,8 @@ class FavoritesSyncHelper(val context: Context) {
}
}
fun needWarnThrottle()
= throttleManager.throttleTime >= THROTTLE_WARN
fun needWarnThrottle() =
throttleManager.throttleTime >= THROTTLE_WARN
class IgnoredException : RuntimeException()
@ -399,8 +402,10 @@ sealed class FavoritesSyncStatus(val message: String) {
class Error(message: String) : FavoritesSyncStatus(message)
class Idle : FavoritesSyncStatus("Waiting for sync to start")
sealed class BadLibraryState(message: String) : FavoritesSyncStatus(message) {
class MangaInMultipleCategories(val manga: Manga,
val categories: List<Category>):
class MangaInMultipleCategories(
val manga: Manga,
val categories: List<Category>
) :
BadLibraryState("The gallery: ${manga.title} is in more than one category (${categories.joinToString { it.name }})!")
}
class Initializing : FavoritesSyncStatus("Initializing sync")

View File

@ -20,8 +20,8 @@ class LocalFavoritesStorage {
fun getRealm() = Realm.getInstance(realmConfig)
fun getChangedDbEntries(realm: Realm)
= getChangedEntries(realm,
fun getChangedDbEntries(realm: Realm) =
getChangedEntries(realm,
parseToFavoriteEntries(
loadDbCategories(
db.getFavoriteMangas()
@ -31,8 +31,8 @@ class LocalFavoritesStorage {
)
)
fun getChangedRemoteEntries(realm: Realm, entries: List<EHentai.ParsedManga>)
= getChangedEntries(realm,
fun getChangedRemoteEntries(realm: Realm, entries: List<EHentai.ParsedManga>) =
getChangedEntries(realm,
parseToFavoriteEntries(
entries.asSequence().map {
Pair(it.fav, it.manga.apply {
@ -80,18 +80,18 @@ class LocalFavoritesStorage {
return ChangeSet(added, removed)
}
private fun Realm.queryRealmForEntry(entry: FavoriteEntry)
= where(FavoriteEntry::class.java)
private fun Realm.queryRealmForEntry(entry: FavoriteEntry) =
where(FavoriteEntry::class.java)
.equalTo(FavoriteEntry::gid.name, entry.gid)
.equalTo(FavoriteEntry::token.name, entry.token)
.equalTo(FavoriteEntry::category.name, entry.category)
.findFirst()
private fun queryListForEntry(list: List<FavoriteEntry>, entry: FavoriteEntry)
= list.find {
it.gid == entry.gid
&& it.token == entry.token
&& it.category == entry.category
private fun queryListForEntry(list: List<FavoriteEntry>, entry: FavoriteEntry) =
list.find {
it.gid == entry.gid &&
it.token == entry.token &&
it.category == entry.category
}
private fun loadDbCategories(manga: Sequence<Manga>): Sequence<Pair<Int, Manga>> {
@ -105,8 +105,8 @@ class LocalFavoritesStorage {
}
}
private fun parseToFavoriteEntries(manga: Sequence<Pair<Int, Manga>>)
= manga.filter {
private fun parseToFavoriteEntries(manga: Sequence<Pair<Int, Manga>>) =
manga.filter {
validateDbManga(it.second)
}.mapNotNull {
FavoriteEntry().apply {
@ -120,13 +120,15 @@ class LocalFavoritesStorage {
}
}
private fun validateDbManga(manga: Manga)
= manga.favorite && (manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID)
private fun validateDbManga(manga: Manga) =
manga.favorite && (manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID)
companion object {
const val MAX_CATEGORIES = 9
}
}
data class ChangeSet(val added: List<FavoriteEntry>,
val removed: List<FavoriteEntry>)
data class ChangeSet(
val added: List<FavoriteEntry>,
val removed: List<FavoriteEntry>
)

View File

@ -4,28 +4,32 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.network.asObservableSuccess
import exh.metadata.metadata.HitomiSearchMetadata.Companion.LTN_BASE_URL
import java.security.MessageDigest
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import org.vepta.vdm.ByteCursor
import rx.Observable
import rx.Single
import java.security.MessageDigest
private typealias HashedTerm = ByteArray
private data class DataPair(val offset: Long, val length: Int)
private data class Node(val keys: List<ByteArray>,
private data class Node(
val keys: List<ByteArray>,
val datas: List<DataPair>,
val subnodeAddresses: List<Long>)
val subnodeAddresses: List<Long>
)
/**
* Kotlin port of the hitomi.la search algorithm
* @author NerdNumber9
*/
class HitomiNozomi(private val client: OkHttpClient,
class HitomiNozomi(
private val client: OkHttpClient,
private val tagIndexVersion: Long,
private val galleriesIndexVersion: Long) {
private val galleriesIndexVersion: Long
) {
fun getGalleryIdsForQuery(query: String): Single<List<Int>> {
val replacedQuery = query.replace('_', ' ')
@ -90,9 +94,9 @@ class HitomiNozomi(private val client: OkHttpClient,
val expectedLength = numberOfGalleryIds * 4 + 4
if(numberOfGalleryIds > 10000000
|| numberOfGalleryIds <= 0
|| inbuf.size != expectedLength) {
if (numberOfGalleryIds > 10000000 ||
numberOfGalleryIds <= 0 ||
inbuf.size != expectedLength) {
return@map emptyList<Int>()
}
@ -234,7 +238,6 @@ class HitomiNozomi(private val client: OkHttpClient,
.build())
}
fun getIndexVersion(httpClient: OkHttpClient, name: String): Observable<Long> {
return httpClient.newCall(GET("$LTN_BASE_URL/$name/version?_=${System.currentTimeMillis()}"))
.asObservableSuccess()

View File

@ -4,11 +4,11 @@ import android.content.Context
import android.text.Html
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import com.ms_square.debugoverlay.DataObserver
import com.ms_square.debugoverlay.OverlayModule
import eu.kanade.tachiyomi.BuildConfig
import android.widget.LinearLayout
import android.widget.TextView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault

View File

@ -1,7 +1,7 @@
package exh.metadata
import java.text.SimpleDateFormat
import java.util.*
import java.util.Locale
/**
* Metadata utils
@ -35,7 +35,6 @@ fun parseHumanReadableByteCount(arg0: String): Double? {
return null
}
fun String?.nullIfBlank(): String? = if (isNullOrBlank())
null
else

View File

@ -4,12 +4,14 @@ import android.net.Uri
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.*
import exh.metadata.EX_DATE_FORMAT
import exh.metadata.ONGOING_SUFFIX
import exh.metadata.humanReadableByteCount
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.plusAssign
import java.util.Date
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.*
class EHentaiSearchMetadata : RaisedSearchMetadata() {
var gId: String?
@ -114,8 +116,8 @@ class EHentaiSearchMetadata : RaisedSearchMetadata() {
const val EH_GENRE_NAMESPACE = "genre"
private const val EH_ARTIST_NAMESPACE = "artist"
private fun splitGalleryUrl(url: String)
= url.let {
private fun splitGalleryUrl(url: String) =
url.let {
// Only parse URL if is full URL
val pathSegments = if (it.startsWith("http"))
Uri.parse(it).pathSegments
@ -129,10 +131,10 @@ class EHentaiSearchMetadata : RaisedSearchMetadata() {
fun galleryToken(url: String) =
splitGalleryUrl(url)[2]
fun normalizeUrl(url: String)
= idAndTokenToUrl(galleryId(url), galleryToken(url))
fun normalizeUrl(url: String) =
idAndTokenToUrl(galleryId(url), galleryToken(url))
fun idAndTokenToUrl(id: String, token: String)
= "/g/$id/$token/?nw=always"
fun idAndTokenToUrl(id: String, token: String) =
"/g/$id/$token/?nw=always"
}
}

View File

@ -34,7 +34,6 @@ class EightMusesSearchMetadata : RaisedSearchMetadata() {
manga.description = listOf(titleDesc.toString(), tagsDesc.toString())
.filter(String::isNotBlank)
.joinToString(separator = "\n")
}
companion object {

View File

@ -49,7 +49,7 @@ class HentaiCafeSearchMetadata : RaisedSearchMetadata() {
const val BASE_URL = "https://hentai.cafe"
fun hcIdFromUrl(url: String)
= url.split("/").last { it.isNotBlank() }
fun hcIdFromUrl(url: String) =
url.split("/").last { it.isNotBlank() }
}
}

View File

@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.plusAssign
import java.util.*
import java.util.Date
class HitomiSearchMetadata : RaisedSearchMetadata() {
var url get() = hlId?.let { urlFromHlId(it) }
@ -92,10 +92,10 @@ class HitomiSearchMetadata: RaisedSearchMetadata() {
const val LTN_BASE_URL = "https://ltn.hitomi.la"
const val BASE_URL = "https://hitomi.la"
fun hlIdFromUrl(url: String)
= url.split('/').last().split('-').last().substringBeforeLast('.')
fun hlIdFromUrl(url: String) =
url.split('/').last().split('-').last().substringBeforeLast('.')
fun urlFromHlId(id: String)
= "$BASE_URL/galleries/$id.html"
fun urlFromHlId(id: String) =
"$BASE_URL/galleries/$id.html"
}
}

View File

@ -3,12 +3,14 @@ package exh.metadata.metadata
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.*
import exh.metadata.EX_DATE_FORMAT
import exh.metadata.ONGOING_SUFFIX
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.nullIfBlank
import exh.plusAssign
import java.util.Date
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.*
class NHentaiSearchMetadata : RaisedSearchMetadata() {
var url get() = nhId?.let { BASE_URL + nhIdToPath(it) }
@ -112,8 +114,8 @@ class NHentaiSearchMetadata : RaisedSearchMetadata() {
else -> null
}
fun nhUrlToId(url: String)
= url.split("/").last { it.isNotBlank() }.toLong()
fun nhUrlToId(url: String) =
url.split("/").last { it.isNotBlank() }.toLong()
fun nhIdToPath(id: Long) = "/g/$id/"
}

View File

@ -80,15 +80,14 @@ class PervEdenSearchMetadata : RaisedSearchMetadata() {
.joinToString(separator = "\n")
}
companion object {
private const val TITLE_TYPE_MAIN = 0
private const val TITLE_TYPE_ALT = 1
const val TAG_TYPE_DEFAULT = 0
private fun splitGalleryUrl(url: String)
= url.let {
private fun splitGalleryUrl(url: String) =
url.let {
Uri.parse(it).pathSegments.filterNot(String::isNullOrBlank)
}
@ -102,8 +101,8 @@ enum class PervEdenLang(val id: Long) {
it(PERV_EDEN_IT_SOURCE_ID);
companion object {
fun source(id: Long)
= values().find { it.id == id }
fun source(id: Long) =
values().find { it.id == id }
?: throw IllegalArgumentException("Unknown source ID: $id!")
}
}

View File

@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.plusAssign
import java.util.*
import java.util.Date
class TsuminoSearchMetadata : RaisedSearchMetadata() {
var tmId: Int? = null
@ -76,8 +76,8 @@ class TsuminoSearchMetadata : RaisedSearchMetadata() {
val BASE_URL = "https://www.tsumino.com"
fun tmIdFromUrl(url: String)
= Uri.parse(url).lastPathSegment
fun tmIdFromUrl(url: String) =
Uri.parse(url).lastPathSegment
fun mangaUrlFromId(id: String) = "/Book/Info/$id"

View File

@ -5,9 +5,9 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.models.SearchTag
import exh.metadata.sql.models.SearchTitle
import kotlin.reflect.KClass
import rx.Completable
import rx.Single
import kotlin.reflect.KClass
data class FlatMetadata(
val metadata: SearchMetadata,
@ -16,8 +16,8 @@ data class FlatMetadata(
) {
inline fun <reified T : RaisedSearchMetadata> raise(): T = raise(T::class)
fun <T : RaisedSearchMetadata> raise(clazz: KClass<T>)
= RaisedSearchMetadata.raiseFlattenGson
fun <T : RaisedSearchMetadata> raise(clazz: KClass<T>) =
RaisedSearchMetadata.raiseFlattenGson
.fromJson(metadata.extra, clazz.java).apply {
fillBaseFields(this@FlatMetadata)
}

View File

@ -35,12 +35,12 @@ abstract class RaisedSearchMetadata {
abstract fun copyTo(manga: SManga)
fun tagsToGenreString()
= tags.filter { it.type != TAG_TYPE_VIRTUAL }
fun tagsToGenreString() =
tags.filter { it.type != TAG_TYPE_VIRTUAL }
.joinToString { (if (it.namespace != null) "${it.namespace}: " else "") + it.name }
fun tagsToDescription()
= StringBuilder("Tags:\n").apply {
fun tagsToDescription() =
StringBuilder("Tags:\n").apply {
// BiConsumer only available in Java 8, don't bother calling forEach directly on 'tags'
val groupedTags = tags.filter { it.type != TAG_TYPE_VIRTUAL }.groupBy {
it.namespace
@ -125,8 +125,8 @@ abstract class RaisedSearchMetadata {
* @param property the metadata for the property.
* @return the property value.
*/
override fun getValue(thisRef: RaisedSearchMetadata, property: KProperty<*>)
= thisRef.getTitleOfType(type)
override fun getValue(thisRef: RaisedSearchMetadata, property: KProperty<*>) =
thisRef.getTitleOfType(type)
/**
* Sets the value of the property for the given object.
@ -134,8 +134,8 @@ abstract class RaisedSearchMetadata {
* @param property the metadata for the property.
* @param value the value to set.
*/
override fun setValue(thisRef: RaisedSearchMetadata, property: KProperty<*>, value: String?)
= thisRef.replaceTitleOfType(type, value)
override fun setValue(thisRef: RaisedSearchMetadata, property: KProperty<*>, value: String?) =
thisRef.replaceTitleOfType(type, value)
}
}
}

View File

@ -1,5 +1,7 @@
package exh.metadata.metadata.base
data class RaisedTag(val namespace: String?,
data class RaisedTag(
val namespace: String?,
val name: String,
val type: Int)
val type: Int
)

View File

@ -3,7 +3,6 @@ package exh.metadata.sql.queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.Manga
import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.tables.SearchMetadataTable

View File

@ -4,8 +4,6 @@ import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.inTransaction
import eu.kanade.tachiyomi.data.database.models.Manga
import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.models.SearchTitle
import exh.metadata.sql.tables.SearchTitleTable

View File

@ -1,4 +1,6 @@
package exh.search
class Namespace(var namespace: String,
var tag: Text? = null) : QueryComponent()
class Namespace(
var namespace: String,
var tag: Text? = null
) : QueryComponent()

View File

@ -7,8 +7,10 @@ import exh.metadata.sql.tables.SearchTitleTable
class SearchEngine {
private val queryCache = mutableMapOf<String, List<QueryComponent>>()
fun textToSubQueries(namespace: String?,
component: Text?): Pair<String, List<String>>? {
fun textToSubQueries(
namespace: String?,
component: Text?
): Pair<String, List<String>>? {
val maybeLenientComponent = component?.let {
if (!it.exact)
it.asLenientTagQueries()
@ -97,7 +99,6 @@ class SearchEngine {
completeParams += pair.second
}
exclude.forEach {
wheres += """
(meta.${SearchMetadataTable.COL_MANGA_ID} NOT IN ${it.first})
@ -197,7 +198,6 @@ class SearchEngine {
return string.replace("\\", "\\\\")
.replace("_", "\\_")
.replace("%", "\\%")
}
}
}

View File

@ -8,13 +8,19 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.ui.smartsearch.SmartSearchPresenter
import exh.util.await
import info.debatty.java.stringsimilarity.NormalizedLevenshtein
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.supervisorScope
import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy
import kotlin.coroutines.CoroutineContext
class SmartSearchEngine(parentContext: CoroutineContext,
val extraSearchParams: String? = null): CoroutineScope {
class SmartSearchEngine(
parentContext: CoroutineContext,
val extraSearchParams: String? = null
) : CoroutineScope {
override val coroutineContext: CoroutineContext = parentContext + Job() + Dispatchers.Default
private val db: DatabaseHelper by injectLazy()

View File

@ -1,12 +1,15 @@
package exh.source
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import rx.Observable
import java.lang.RuntimeException
abstract class DelegatedHttpSource(val delegate: HttpSource) : HttpSource() {
/**
@ -14,16 +17,16 @@ abstract class DelegatedHttpSource(val delegate: HttpSource): HttpSource() {
*
* @param page the page number to retrieve.
*/
override fun popularMangaRequest(page: Int)
= throw UnsupportedOperationException("Should never be called!")
override fun popularMangaRequest(page: Int) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun popularMangaParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun popularMangaParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Returns the request for the search manga given the page.
@ -32,64 +35,64 @@ abstract class DelegatedHttpSource(val delegate: HttpSource): HttpSource() {
* @param query the search query.
* @param filters the list of filters to apply.
*/
override fun searchMangaRequest(page: Int, query: String, filters: FilterList)
= throw UnsupportedOperationException("Should never be called!")
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun searchMangaParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun searchMangaParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Returns the request for latest manga given the page.
*
* @param page the page number to retrieve.
*/
override fun latestUpdatesRequest(page: Int)
= throw UnsupportedOperationException("Should never be called!")
override fun latestUpdatesRequest(page: Int) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun latestUpdatesParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun latestUpdatesParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns the details of a manga.
*
* @param response the response from the site.
*/
override fun mangaDetailsParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun mangaDetailsParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns a list of chapters.
*
* @param response the response from the site.
*/
override fun chapterListParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun chapterListParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns a list of pages.
*
* @param response the response from the site.
*/
override fun pageListParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun pageListParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns the absolute url to the source image.
*
* @param response the response from the site.
*/
override fun imageUrlParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun imageUrlParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Base url of the website without the trailing slash, like: http://mysite.com
@ -236,8 +239,8 @@ abstract class DelegatedHttpSource(val delegate: HttpSource): HttpSource() {
override fun getFilterList() = delegate.getFilterList()
private fun ensureDelegateCompatible() {
if(versionId != delegate.versionId
|| lang != delegate.lang) {
if (versionId != delegate.versionId ||
lang != delegate.lang) {
throw IncompatibleDelegateException("Delegate source is not compatible (versionId: $versionId <=> ${delegate.versionId}, lang: $lang <=> ${delegate.lang})!")
}
}

View File

@ -2,13 +2,19 @@ package exh.source
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import okhttp3.Response
import uy.kohesive.injekt.injectLazy
class EnhancedHttpSource(val originalSource: HttpSource,
val enchancedSource: HttpSource): HttpSource() {
class EnhancedHttpSource(
val originalSource: HttpSource,
val enchancedSource: HttpSource
) : HttpSource() {
private val prefs: PreferencesHelper by injectLazy()
/**
@ -16,16 +22,16 @@ class EnhancedHttpSource(val originalSource: HttpSource,
*
* @param page the page number to retrieve.
*/
override fun popularMangaRequest(page: Int)
= throw UnsupportedOperationException("Should never be called!")
override fun popularMangaRequest(page: Int) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun popularMangaParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun popularMangaParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Returns the request for the search manga given the page.
@ -34,64 +40,64 @@ class EnhancedHttpSource(val originalSource: HttpSource,
* @param query the search query.
* @param filters the list of filters to apply.
*/
override fun searchMangaRequest(page: Int, query: String, filters: FilterList)
= throw UnsupportedOperationException("Should never be called!")
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun searchMangaParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun searchMangaParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Returns the request for latest manga given the page.
*
* @param page the page number to retrieve.
*/
override fun latestUpdatesRequest(page: Int)
= throw UnsupportedOperationException("Should never be called!")
override fun latestUpdatesRequest(page: Int) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun latestUpdatesParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun latestUpdatesParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns the details of a manga.
*
* @param response the response from the site.
*/
override fun mangaDetailsParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun mangaDetailsParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns a list of chapters.
*
* @param response the response from the site.
*/
override fun chapterListParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun chapterListParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns a list of pages.
*
* @param response the response from the site.
*/
override fun pageListParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun pageListParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Parses the response from the site and returns the absolute url to the source image.
*
* @param response the response from the site.
*/
override fun imageUrlParse(response: Response)
= throw UnsupportedOperationException("Should never be called!")
override fun imageUrlParse(response: Response) =
throw UnsupportedOperationException("Should never be called!")
/**
* Base url of the website without the trailing slash, like: http://mysite.com
@ -146,8 +152,8 @@ class EnhancedHttpSource(val originalSource: HttpSource,
* @param query the search query.
* @param filters the list of filters to apply.
*/
override fun fetchSearchManga(page: Int, query: String, filters: FilterList)
= source().fetchSearchManga(page, query, filters)
override fun fetchSearchManga(page: Int, query: String, filters: FilterList) =
source().fetchSearchManga(page, query, filters)
/**
* Returns an observable containing a page with a list of latest manga updates.
@ -202,8 +208,8 @@ class EnhancedHttpSource(val originalSource: HttpSource,
* @param chapter the chapter to be added.
* @param manga the manga of the chapter.
*/
override fun prepareNewChapter(chapter: SChapter, manga: SManga)
= source().prepareNewChapter(chapter, manga)
override fun prepareNewChapter(chapter: SChapter, manga: SManga) =
source().prepareNewChapter(chapter, manga)
/**
* Returns the list of filters for the source.

View File

@ -7,8 +7,8 @@ import com.afollestad.materialdialogs.MaterialDialog
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.toast
import timber.log.Timber
import kotlin.concurrent.thread
import timber.log.Timber
class ConfiguringDialogController : DialogController() {
private var materialDialog: MaterialDialog? = null
@ -62,4 +62,3 @@ class ConfiguringDialogController : DialogController() {
router.popController(this)
}
}

View File

@ -24,11 +24,13 @@ class EHConfigurator {
private fun EHentai.requestWithCreds(sp: Int = 1) = Request.Builder()
.addHeader("Cookie", cookiesHeader(sp))
private fun EHentai.execProfileActions(action: String,
private fun EHentai.execProfileActions(
action: String,
name: String,
set: String,
sp: Int)
= configuratorClient.newCall(requestWithCreds(sp)
sp: Int
) =
configuratorClient.newCall(requestWithCreds(sp)
.url(uconfigUrl)
.post(FormBody.Builder()
.add("profile_action", action)

View File

@ -9,7 +9,6 @@ class EHHathPerksResponse {
var pagingEnlargementII = false
var pagingEnlargementIII = false
override fun toString()
= "EHHathPerksResponse(moreThumbs=$moreThumbs, thumbsUp=$thumbsUp, allThumbs=$allThumbs, pagingEnlargementI=$pagingEnlargementI, pagingEnlargementII=$pagingEnlargementII, pagingEnlargementIII=$pagingEnlargementIII)"
override fun toString() =
"EHHathPerksResponse(moreThumbs=$moreThumbs, thumbsUp=$thumbsUp, allThumbs=$allThumbs, pagingEnlargementI=$pagingEnlargementI, pagingEnlargementII=$pagingEnlargementII, pagingEnlargementIII=$pagingEnlargementIII)"
}

View File

@ -1,11 +1,11 @@
package exh.ui
import java.util.UUID
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.*
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
typealias LoadingHandle = String

View File

@ -6,11 +6,11 @@ import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import eu.kanade.tachiyomi.ui.base.controller.BaseController
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlin.coroutines.CoroutineContext
abstract class BaseExhController(bundle: Bundle? = null) : BaseController(bundle), CoroutineScope {
abstract val layoutId: Int

View File

@ -1,6 +1,5 @@
package exh.ui.batchadd
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

View File

@ -7,16 +7,18 @@ import android.webkit.WebView
import androidx.annotation.RequiresApi
import eu.kanade.tachiyomi.util.system.asJsoup
import exh.ui.captcha.BrowserActionActivity.Companion.CROSS_WINDOW_SCRIPT_INNER
import java.nio.charset.Charset
import org.jsoup.nodes.DataNode
import org.jsoup.nodes.Element
import java.nio.charset.Charset
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class AutoSolvingWebViewClient(activity: BrowserActionActivity,
class AutoSolvingWebViewClient(
activity: BrowserActionActivity,
verifyComplete: (String) -> Boolean,
injectScript: String?,
headers: Map<String, String>)
: HeadersInjectingWebViewClient(activity, verifyComplete, injectScript, headers) {
headers: Map<String, String>
) :
HeadersInjectingWebViewClient(activity, verifyComplete, injectScript, headers) {
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
// Inject our custom script into the recaptcha iframes

View File

@ -4,9 +4,11 @@ import android.os.Build
import android.webkit.WebView
import android.webkit.WebViewClient
open class BasicWebViewClient(protected val activity: BrowserActionActivity,
open class BasicWebViewClient(
protected val activity: BrowserActionActivity,
protected val verifyComplete: (String) -> Boolean,
private val injectScript: String?) : WebViewClient() {
private val injectScript: String?
) : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)

View File

@ -6,7 +6,12 @@ import android.os.Build
import android.os.Bundle
import android.os.SystemClock
import android.view.MotionEvent
import android.webkit.*
import android.webkit.CookieManager
import android.webkit.CookieSyncManager
import android.webkit.JavascriptInterface
import android.webkit.JsResult
import android.webkit.WebChromeClient
import android.webkit.WebView
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import com.afollestad.materialdialogs.MaterialDialog
@ -22,6 +27,9 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource
import exh.source.DelegatedHttpSource
import exh.util.melt
import java.io.Serializable
import java.net.URL
import java.util.UUID
import kotlinx.android.synthetic.main.eh_activity_captcha.*
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull
@ -33,10 +41,6 @@ import rx.Single
import rx.schedulers.Schedulers
import timber.log.Timber
import uy.kohesive.injekt.injectLazy
import java.io.Serializable
import java.net.URL
import java.util.*
import kotlin.collections.HashMap
class BrowserActionActivity : AppCompatActivity() {
private val sourceManager: SourceManager by injectLazy()
@ -72,8 +76,8 @@ class BrowserActionActivity : AppCompatActivity() {
it.value.joinToString(",")
} ?: emptyMap()) + (intent.getSerializableExtra(HEADERS_EXTRA) as? HashMap<String, String> ?: emptyMap())
val cookies: HashMap<String, String>?
= intent.getSerializableExtra(COOKIES_EXTRA) as? HashMap<String, String>
val cookies: HashMap<String, String>? =
intent.getSerializableExtra(COOKIES_EXTRA) as? HashMap<String, String>
val script: String? = intent.getStringExtra(SCRIPT_EXTRA)
val url: String? = intent.getStringExtra(URL_EXTRA)
val actionName = intent.getStringExtra(ACTION_NAME_EXTRA)
@ -120,8 +124,8 @@ class BrowserActionActivity : AppCompatActivity() {
// Wait for both inner scripts to be loaded
if (loadedInners >= 2) {
// Attempt to autosolve captcha
if(preferencesHelper.eh_autoSolveCaptchas().getOrDefault()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (preferencesHelper.eh_autoSolveCaptchas().getOrDefault() &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webview.post {
// 10 seconds to auto-solve captcha
strictValidationStartTime = System.currentTimeMillis() + 1000 * 10
@ -415,7 +419,6 @@ class BrowserActionActivity : AppCompatActivity() {
doStageCheckbox(loopId)
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
@JavascriptInterface
fun validateCaptchaCallback(result: Boolean, loopId: String) {
@ -434,8 +437,8 @@ class BrowserActionActivity : AppCompatActivity() {
}
} else {
val savedStrictValidationStartTime = strictValidationStartTime
if(savedStrictValidationStartTime != null
&& System.currentTimeMillis() > savedStrictValidationStartTime) {
if (savedStrictValidationStartTime != null &&
System.currentTimeMillis() > savedStrictValidationStartTime) {
captchaSolveFail()
} else {
webview.postDelayed({
@ -624,12 +627,14 @@ class BrowserActionActivity : AppCompatActivity() {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
fun launchCaptcha(context: Context,
fun launchCaptcha(
context: Context,
source: ActionCompletionVerifier,
cookies: Map<String, String>,
script: String?,
url: String,
autoSolveSubmitBtnSelector: String? = null) {
autoSolveSubmitBtnSelector: String? = null
) {
val intent = baseIntent(context).apply {
putExtra(SOURCE_ID_EXTRA, source.id)
putExtra(COOKIES_EXTRA, HashMap(cookies))
@ -641,9 +646,11 @@ class BrowserActionActivity : AppCompatActivity() {
context.startActivity(intent)
}
fun launchUniversal(context: Context,
fun launchUniversal(
context: Context,
source: HttpSource,
url: String) {
url: String
) {
val intent = baseIntent(context).apply {
putExtra(SOURCE_ID_EXTRA, source.id)
putExtra(URL_EXTRA, url)
@ -652,9 +659,11 @@ class BrowserActionActivity : AppCompatActivity() {
context.startActivity(intent)
}
fun launchUniversal(context: Context,
fun launchUniversal(
context: Context,
sourceId: Long,
url: String) {
url: String
) {
val intent = baseIntent(context).apply {
putExtra(SOURCE_ID_EXTRA, sourceId)
putExtra(URL_EXTRA, url)
@ -663,11 +672,13 @@ class BrowserActionActivity : AppCompatActivity() {
context.startActivity(intent)
}
fun launchAction(context: Context,
fun launchAction(
context: Context,
completionVerifier: ActionCompletionVerifier,
script: String?,
url: String,
actionName: String) {
actionName: String
) {
val intent = baseIntent(context).apply {
putExtra(SOURCE_ID_EXTRA, completionVerifier.id)
putExtra(SCRIPT_EXTRA, script)
@ -678,12 +689,14 @@ class BrowserActionActivity : AppCompatActivity() {
context.startActivity(intent)
}
fun launchAction(context: Context,
fun launchAction(
context: Context,
completionVerifier: (String) -> Boolean,
script: String?,
url: String,
actionName: String,
headers: Map<String, String>? = emptyMap()) {
headers: Map<String, String>? = emptyMap()
) {
val intent = baseIntent(context).apply {
putExtra(HEADERS_EXTRA, HashMap(headers))
putExtra(VERIFY_LAMBDA_EXTRA, completionVerifier as Serializable)
@ -708,4 +721,3 @@ class NoopActionCompletionVerifier(private val source: HttpSource): DelegatedHtt
interface ActionCompletionVerifier : Source {
fun verifyComplete(url: String): Boolean
}

View File

@ -7,11 +7,13 @@ import android.webkit.WebView
import androidx.annotation.RequiresApi
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
open class HeadersInjectingWebViewClient(activity: BrowserActionActivity,
open class HeadersInjectingWebViewClient(
activity: BrowserActionActivity,
verifyComplete: (String) -> Boolean,
injectScript: String?,
private val headers: Map<String, String>)
: BasicWebViewClient(activity, verifyComplete, injectScript) {
private val headers: Map<String, String>
) :
BasicWebViewClient(activity, verifyComplete, injectScript) {
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
// Temp disabled as it's unreliable

View File

@ -3,8 +3,8 @@ package exh.ui.intercept
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import exh.GalleryAddEvent
import exh.GalleryAdder
import rx.subjects.BehaviorSubject
import kotlin.concurrent.thread
import rx.subjects.BehaviorSubject
class InterceptActivityPresenter : BasePresenter<InterceptActivity>() {
private val galleryAdder = GalleryAdder()

View File

@ -28,13 +28,13 @@ class FingerLockPreference @JvmOverloads constructor(context: Context, attrs: At
val prefs: PreferencesHelper by injectLazy()
val fingerprintSupported
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& Reprint.isHardwarePresent()
&& Reprint.hasFingerprintRegistered()
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
Reprint.isHardwarePresent() &&
Reprint.hasFingerprintRegistered()
val useFingerprint
get() = fingerprintSupported
&& prefs.eh_lockUseFingerprint().getOrDefault()
get() = fingerprintSupported &&
prefs.eh_lockUseFingerprint().getOrDefault()
@SuppressLint("NewApi")
override fun onAttached() {

View File

@ -1,6 +1,5 @@
package exh.ui.lock
import android.content.Intent
import android.view.WindowManager
import androidx.fragment.app.FragmentActivity
import com.bluelinelabs.conductor.Router
@ -8,7 +7,6 @@ import com.bluelinelabs.conductor.RouterTransaction
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import uy.kohesive.injekt.injectLazy
import java.util.Date
object LockActivityDelegate {
private val preferences by injectLazy<PreferencesHelper>()
@ -20,7 +18,6 @@ object LockActivityDelegate {
.popChangeHandler(LockChangeHandler(animate)))
}
fun onCreate(activity: FragmentActivity) {
preferences.secureScreen().asObservable()
.subscribe {
@ -42,5 +39,4 @@ object LockActivityDelegate {
private fun isAppLocked(router: Router): Boolean {
return router.backstack.lastOrNull()?.controller() is LockController
}
}

View File

@ -7,7 +7,7 @@ import android.view.View
import android.view.ViewGroup
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.changehandler.AnimatorChangeHandler
import java.util.*
import java.util.ArrayList
class LockChangeHandler : AnimatorChangeHandler {
constructor() : super()
@ -36,6 +36,4 @@ class LockChangeHandler : AnimatorChangeHandler {
override fun copy(): ControllerChangeHandler =
LockChangeHandler(animationDuration, removesFromViewOnPush())
}

View File

@ -22,8 +22,8 @@ class LockController : NucleusController<LockPresenter>() {
val prefs: PreferencesHelper by injectLazy()
override fun inflateView(inflater: LayoutInflater, container: ViewGroup)
= inflater.inflate(R.layout.activity_lock, container, false)!!
override fun inflateView(inflater: LayoutInflater, container: ViewGroup) =
inflater.inflate(R.layout.activity_lock, container, false)!!
override fun createPresenter() = LockPresenter()

View File

@ -8,12 +8,12 @@ import com.afollestad.materialdialogs.MaterialDialog
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.util.preference.onChange
import java.math.BigInteger
import java.security.SecureRandom
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy
import java.math.BigInteger
import java.security.SecureRandom
class LockPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
SwitchPreferenceCompat(context, attrs) {

View File

@ -11,9 +11,8 @@ class LockPresenter: BasePresenter<LockController>() {
val prefs: PreferencesHelper by injectLazy()
val useFingerprint
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& Reprint.isHardwarePresent()
&& Reprint.hasFingerprintRegistered()
&& prefs.eh_lockUseFingerprint().getOrDefault()
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
Reprint.isHardwarePresent() &&
Reprint.hasFingerprintRegistered() &&
prefs.eh_lockUseFingerprint().getOrDefault()
}

View File

@ -13,11 +13,10 @@ import com.elvishew.xlog.XLog
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.security.MessageDigest
import kotlin.experimental.and
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
* Password hashing utils
@ -40,22 +39,24 @@ fun sha512(passwordToHash: String, salt: String): String {
/**
* Check if lock is enabled
*/
fun lockEnabled(prefs: PreferencesHelper = Injekt.get())
= prefs.eh_lockHash().get() != null
&& prefs.eh_lockSalt().get() != null
&& prefs.eh_lockLength().getOrDefault() != -1
fun lockEnabled(prefs: PreferencesHelper = Injekt.get()) =
prefs.eh_lockHash().get() != null &&
prefs.eh_lockSalt().get() != null &&
prefs.eh_lockLength().getOrDefault() != -1
/**
* Check if the lock will function properly
*
* @return true if action is required, false if lock is working properly
*/
fun notifyLockSecurity(context: Context,
prefs: PreferencesHelper = Injekt.get()): Boolean {
fun notifyLockSecurity(
context: Context,
prefs: PreferencesHelper = Injekt.get()
): Boolean {
return false
if (!prefs.eh_lockManually().getOrDefault()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
&& !hasAccessToUsageStats(context)) {
if (!prefs.eh_lockManually().getOrDefault() &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
!hasAccessToUsageStats(context)) {
MaterialDialog.Builder(context)
.title("Permission required")
.content("${context.getString(R.string.app_name)} requires the usage stats permission to detect when you leave the app. " +

View File

@ -12,14 +12,14 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.visible
import exh.uconfig.WarnConfigureDialogController
import java.net.HttpCookie
import kotlinx.android.synthetic.main.eh_activity_login.view.*
import timber.log.Timber
import uy.kohesive.injekt.injectLazy
import java.net.HttpCookie
/**
* LoginController
@ -105,8 +105,8 @@ class LoginController : NucleusController<LoginPresenter>() {
val parsedUrl = Uri.parse(url)
if (parsedUrl.host.equals("forums.e-hentai.org", ignoreCase = true)) {
// Hide distracting content
if(!parsedUrl.queryParameterNames.contains(PARAM_SKIP_INJECT)
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
if (!parsedUrl.queryParameterNames.contains(PARAM_SKIP_INJECT) &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
view.evaluateJavascript(HIDE_JS, null)
// Check login result
@ -138,9 +138,9 @@ class LoginController : NucleusController<LoginPresenter>() {
fun checkLoginCookies(url: String): Boolean {
getCookies(url)?.let { parsed ->
return parsed.filter {
(it.name.equals(MEMBER_ID_COOKIE, ignoreCase = true)
|| it.name.equals(PASS_HASH_COOKIE, ignoreCase = true))
&& it.value.isNotBlank()
(it.name.equals(MEMBER_ID_COOKIE, ignoreCase = true) ||
it.name.equals(PASS_HASH_COOKIE, ignoreCase = true)) &&
it.value.isNotBlank()
}.count() >= 2
}
return false
@ -177,8 +177,8 @@ class LoginController : NucleusController<LoginPresenter>() {
return false
}
fun getCookies(url: String): List<HttpCookie>?
= CookieManager.getInstance().getCookie(url)?.let {
fun getCookies(url: String): List<HttpCookie>? =
CookieManager.getInstance().getCookie(url)?.let {
it.split("; ").flatMap {
HttpCookie.parse(it)
}

View File

@ -2,6 +2,4 @@ package exh.ui.login
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
class LoginPresenter: BasePresenter<LoginController>() {
}
class LoginPresenter : BasePresenter<LoginController>()

View File

@ -12,9 +12,9 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.SourceManager
import exh.EXH_SOURCE_ID
import exh.isLewdSource
import kotlin.concurrent.thread
import timber.log.Timber
import uy.kohesive.injekt.injectLazy
import kotlin.concurrent.thread
class MetadataFetchDialog {
@ -132,7 +132,6 @@ class MetadataFetchDialog {
}
}
}
}
fun adviseMigrationLater(activity: Activity) {

View File

@ -4,8 +4,10 @@ import android.os.Bundle
import eu.davidea.flexibleadapter.FlexibleAdapter
import exh.debug.DebugFunctions.sourceManager
class MigrationSourceAdapter(val items: List<MigrationSourceItem>,
val controller: MigrationDesignController): FlexibleAdapter<MigrationSourceItem>(
class MigrationSourceAdapter(
val items: List<MigrationSourceItem>,
val controller: MigrationDesignController
) : FlexibleAdapter<MigrationSourceItem>(
items,
controller,
true

View File

@ -1,12 +1,12 @@
package exh.ui.migration.manga.design
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
import android.view.View
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.util.view.getRound
import kotlinx.android.synthetic.main.eh_source_item.*
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
class MigrationSourceHolder(view: View, val adapter: FlexibleAdapter<MigrationSourceItem>) :
BaseFlexibleViewHolder(view, adapter) {

View File

@ -25,10 +25,12 @@ class MigrationSourceItem(val source: HttpSource, var sourceEnabled: Boolean): A
* @param position The position of this item in the adapter.
* @param payloads List of partial changes.
*/
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>,
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>,
holder: MigrationSourceHolder,
position: Int,
payloads: List<Any?>?) {
payloads: List<Any?>?
) {
holder.bind(source, sourceEnabled)
}

View File

@ -6,14 +6,17 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import exh.util.DeferredField
import exh.util.await
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
class MigratingManga(private val db: DatabaseHelper,
class MigratingManga(
private val db: DatabaseHelper,
private val sourceManager: SourceManager,
val mangaId: Long,
parentContext: CoroutineContext) {
parentContext: CoroutineContext
) {
val searchResult = DeferredField<Long?>()
// <MAX, PROGRESS>

View File

@ -22,20 +22,27 @@ import eu.kanade.tachiyomi.util.view.inflate
import eu.kanade.tachiyomi.util.view.visible
import exh.MERGED_SOURCE_ID
import exh.util.await
import kotlinx.android.synthetic.main.eh_manga_card.view.*
import kotlinx.android.synthetic.main.eh_migration_process_item.view.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import uy.kohesive.injekt.injectLazy
import java.text.DateFormat
import java.text.DecimalFormat
import java.util.*
import java.util.Date
import kotlin.coroutines.CoroutineContext
import kotlinx.android.synthetic.main.eh_manga_card.view.*
import kotlinx.android.synthetic.main.eh_migration_process_item.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.injectLazy
class MigrationProcedureAdapter(val controller: MigrationProcedureController,
class MigrationProcedureAdapter(
val controller: MigrationProcedureController,
val migratingManga: List<MigratingManga>,
override val coroutineContext: CoroutineContext) : androidx.viewpager.widget.PagerAdapter(), CoroutineScope {
override val coroutineContext: CoroutineContext
) : androidx.viewpager.widget.PagerAdapter(), CoroutineScope {
private val db: DatabaseHelper by injectLazy()
private val gson: Gson by injectLazy()
private val sourceManager: SourceManager by injectLazy()
@ -96,9 +103,11 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
}
}
private fun migrateMangaInternal(prevManga: Manga,
private fun migrateMangaInternal(
prevManga: Manga,
manga: Manga,
replace: Boolean) {
replace: Boolean
) {
db.inTransaction {
// Update chapters read
if (MigrationFlags.hasChapters(controller.config.migrationFlags)) {

View File

@ -15,13 +15,21 @@ import eu.kanade.tachiyomi.util.system.toast
import exh.smartsearch.SmartSearchEngine
import exh.ui.base.BaseExhController
import exh.util.await
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.android.synthetic.main.eh_migration_process.*
import kotlinx.coroutines.*
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy
import java.util.concurrent.atomic.AtomicInteger
// TODO Will probably implode if activity is fully destroyed
class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(bundle), CoroutineScope {

View File

@ -14,7 +14,13 @@ import eu.kanade.tachiyomi.ui.catalogue.browse.BrowseCatalogueController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.android.synthetic.main.eh_smart_search.*
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.injectLazy
class SmartSearchController(bundle: Bundle? = null) : NucleusController<SmartSearchPresenter>(), CoroutineScope {

View File

@ -8,8 +8,13 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.catalogue.CatalogueController
import exh.smartsearch.SmartSearchEngine
import kotlinx.coroutines.*
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
class SmartSearchPresenter(private val source: CatalogueSource?, private val config: CatalogueController.SmartSearchConfig?) :
BasePresenter<SmartSearchController>(), CoroutineScope {
@ -48,7 +53,6 @@ class SmartSearchPresenter(private val source: CatalogueSource?, private val con
}
}
override fun onDestroy() {
super.onDestroy()

View File

@ -1,10 +1,10 @@
package exh.util
import kotlin.coroutines.coroutineContext
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.onEach
import kotlin.coroutines.coroutineContext
@FlowPreview
fun <T> Flow<T>.cancellable() = onEach {

View File

@ -112,7 +112,6 @@ private inline class IteratorShim<E>(private val iterator: Iterator<E>) : FakeMu
override fun next() = iterator.next()
}
interface FakeMutableIterator<E> : MutableIterator<E> {
/**
* Removes from the underlying collection the last element returned by this iterator.

View File

@ -1,7 +1,10 @@
package exh.util
import io.realm.*
import java.util.*
import io.realm.Case
import io.realm.RealmModel
import io.realm.RealmQuery
import io.realm.RealmResults
import java.util.Date
/**
* Realm query with logging
@ -9,14 +12,16 @@ import java.util.*
* @author nulldev
*/
inline fun <reified E : RealmModel> RealmQuery<out E>.beginLog(clazz: Class<out E>? =
E::class.java): LoggingRealmQuery<out E>
= LoggingRealmQuery.fromQuery(this, clazz)
inline fun <reified E : RealmModel> RealmQuery<out E>.beginLog(
clazz: Class<out E>? =
E::class.java
): LoggingRealmQuery<out E> =
LoggingRealmQuery.fromQuery(this, clazz)
class LoggingRealmQuery<E : RealmModel>(val query: RealmQuery<E>) {
companion object {
fun <E : RealmModel> fromQuery(q: RealmQuery<out E>, clazz: Class<out E>?)
= LoggingRealmQuery(q).apply {
fun <E : RealmModel> fromQuery(q: RealmQuery<out E>, clazz: Class<out E>?) =
LoggingRealmQuery(q).apply {
log += "SELECT * FROM ${clazz?.name ?: "???"} WHERE"
}
}

View File

@ -1,7 +1,8 @@
package exh.util
import android.util.SparseArray
import java.util.*
import java.util.AbstractMap
import java.util.LinkedList
class NakedTrieNode<T>(val key: Int, var parent: NakedTrieNode<T>?) {
val children = SparseArray<NakedTrieNode<T>>(1)

View File

@ -8,8 +8,8 @@ import org.jsoup.nodes.Document
fun Response.interceptAsHtml(block: (Document) -> Unit): Response {
val body = body
if (body?.contentType()?.type == "text"
&& body.contentType()?.subtype == "html") {
if (body?.contentType()?.type == "text" &&
body.contentType()?.subtype == "html") {
val bodyString = body.string()
val rebuiltResponse = newBuilder()
.body(ResponseBody.create(body.contentType(), bodyString))

View File

@ -3,7 +3,7 @@ package exh.util
import io.realm.Realm
import io.realm.RealmModel
import io.realm.log.RealmLog
import java.util.*
import java.util.UUID
inline fun <T> realmTrans(block: (Realm) -> T): T {
return defRealm {
@ -49,9 +49,8 @@ inline fun <T> Realm.useTrans(block: (Realm) -> T): T {
}
}
fun <T : RealmModel> Realm.createUUIDObj(clazz: Class<T>)
= createObject(clazz, UUID.randomUUID().toString())!!
inline fun <reified T : RealmModel> Realm.createUUIDObj()
= createUUIDObj(T::class.java)
fun <T : RealmModel> Realm.createUUIDObj(clazz: Class<T>) =
createObject(clazz, UUID.randomUUID().toString())!!
inline fun <reified T : RealmModel> Realm.createUUIDObj() =
createUUIDObj(T::class.java)

View File

@ -2,10 +2,14 @@ package exh.util
import com.pushtorefresh.storio.operations.PreparedOperation
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetObject
import kotlinx.coroutines.suspendCancellableCoroutine
import rx.*
import rx.subjects.ReplaySubject
import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.suspendCancellableCoroutine
import rx.Completable
import rx.Observable
import rx.Scheduler
import rx.Single
import rx.Subscription
import rx.subjects.ReplaySubject
/**
* Transform a cold single to a hot single

View File

@ -1,6 +1,17 @@
package xyz.nulldev.ts.api.http.serializer
import com.github.salomonbrys.kotson.*
import com.github.salomonbrys.kotson.bool
import com.github.salomonbrys.kotson.byte
import com.github.salomonbrys.kotson.char
import com.github.salomonbrys.kotson.double
import com.github.salomonbrys.kotson.float
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.long
import com.github.salomonbrys.kotson.obj
import com.github.salomonbrys.kotson.set
import com.github.salomonbrys.kotson.short
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.source.model.Filter