Optimize imports, disallow wildcard imports because of klint, run linter
This commit is contained in:
@@ -1,18 +1,18 @@
|
||||
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
|
||||
|
||||
/**
|
||||
* Class used to manage loader UIs
|
||||
*/
|
||||
class LoaderManager(parentContext: CoroutineContext = EmptyCoroutineContext): CoroutineScope {
|
||||
class LoaderManager(parentContext: CoroutineContext = EmptyCoroutineContext) : CoroutineScope {
|
||||
override val coroutineContext = Dispatchers.Main + parentContext
|
||||
|
||||
private val openLoadingHandles = mutableListOf<LoadingHandle>()
|
||||
@@ -25,7 +25,7 @@ class LoaderManager(parentContext: CoroutineContext = EmptyCoroutineContext): Co
|
||||
handle to (openLoadingHandles.size == 1)
|
||||
}
|
||||
|
||||
if(shouldUpdateLoadingStatus) {
|
||||
if (shouldUpdateLoadingStatus) {
|
||||
launch {
|
||||
updateLoadingStatus(true)
|
||||
}
|
||||
@@ -36,13 +36,13 @@ class LoaderManager(parentContext: CoroutineContext = EmptyCoroutineContext): Co
|
||||
|
||||
@Synchronized
|
||||
fun closeProgressBar(handle: LoadingHandle?) {
|
||||
if(handle == null) return
|
||||
if (handle == null) return
|
||||
|
||||
val shouldUpdateLoadingStatus = synchronized(this) {
|
||||
openLoadingHandles.remove(handle) && openLoadingHandles.isEmpty()
|
||||
}
|
||||
|
||||
if(shouldUpdateLoadingStatus) {
|
||||
if (shouldUpdateLoadingStatus) {
|
||||
launch {
|
||||
updateLoadingStatus(false)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package exh.ui.batchadd
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -44,14 +43,14 @@ class BatchAddController : NucleusController<BatchAddPresenter>() {
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeUntilDestroy {
|
||||
progressSubscriptions.clear()
|
||||
if(it == BatchAddPresenter.STATE_INPUT_TO_PROGRESS) {
|
||||
if (it == BatchAddPresenter.STATE_INPUT_TO_PROGRESS) {
|
||||
showProgress(this)
|
||||
progressSubscriptions += presenter.progressRelay
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.combineLatest(presenter.progressTotalRelay, { progress, total ->
|
||||
//Show hide dismiss button
|
||||
// Show hide dismiss button
|
||||
progress_dismiss_btn.visibility =
|
||||
if(progress == total)
|
||||
if (progress == total)
|
||||
View.VISIBLE
|
||||
else View.GONE
|
||||
|
||||
@@ -79,7 +78,7 @@ class BatchAddController : NucleusController<BatchAddPresenter>() {
|
||||
}?.let {
|
||||
progressSubscriptions += it
|
||||
}
|
||||
} else if(it == BatchAddPresenter.STATE_PROGRESS_TO_INPUT) {
|
||||
} else if (it == BatchAddPresenter.STATE_PROGRESS_TO_INPUT) {
|
||||
hideProgress(this)
|
||||
presenter.currentlyAddingRelay.call(BatchAddPresenter.STATE_IDLE)
|
||||
}
|
||||
@@ -124,8 +123,8 @@ class BatchAddController : NucleusController<BatchAddPresenter>() {
|
||||
private fun formatProgress(progress: Int, total: Int) = "$progress/$total"
|
||||
|
||||
private fun addGalleries(galleries: String) {
|
||||
//Check text box has content
|
||||
if(galleries.isBlank()) {
|
||||
// Check text box has content
|
||||
if (galleries.isBlank()) {
|
||||
noGalleriesSpecified()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import exh.GalleryAdder
|
||||
import exh.metadata.nullIfBlank
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class BatchAddPresenter: BasePresenter<BatchAddController>() {
|
||||
class BatchAddPresenter : BasePresenter<BatchAddController>() {
|
||||
|
||||
private val galleryAdder by lazy { GalleryAdder() }
|
||||
|
||||
@@ -34,7 +34,7 @@ class BatchAddPresenter: BasePresenter<BatchAddController>() {
|
||||
|
||||
splitGalleries.forEachIndexed { i, s ->
|
||||
val result = galleryAdder.addGallery(s, true)
|
||||
if(result is GalleryAddEvent.Success) {
|
||||
if (result is GalleryAddEvent.Success) {
|
||||
succeeded.add(s)
|
||||
} else {
|
||||
failed.add(s)
|
||||
@@ -46,7 +46,7 @@ class BatchAddPresenter: BasePresenter<BatchAddController>() {
|
||||
}) + " " + result.logMessage)
|
||||
}
|
||||
|
||||
//Show report
|
||||
// Show report
|
||||
val summary = "\nSummary:\nAdded: ${succeeded.size} gallerie(s)\nFailed: ${failed.size} gallerie(s)"
|
||||
eventRelay?.call(summary)
|
||||
}
|
||||
|
||||
@@ -7,21 +7,23 @@ 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,
|
||||
verifyComplete: (String) -> Boolean,
|
||||
injectScript: String?,
|
||||
headers: Map<String, String>)
|
||||
: HeadersInjectingWebViewClient(activity, verifyComplete, injectScript, headers) {
|
||||
class AutoSolvingWebViewClient(
|
||||
activity: BrowserActionActivity,
|
||||
verifyComplete: (String) -> Boolean,
|
||||
injectScript: String?,
|
||||
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
|
||||
val lastPathSegment = request.url.pathSegments.lastOrNull()
|
||||
if(lastPathSegment == "anchor" || lastPathSegment == "bframe") {
|
||||
if (lastPathSegment == "anchor" || lastPathSegment == "bframe") {
|
||||
val oReq = request.toOkHttpRequest()
|
||||
val response = activity.httpClient.newCall(oReq).execute()
|
||||
val doc = response.asJsoup()
|
||||
@@ -34,4 +36,4 @@ class AutoSolvingWebViewClient(activity: BrowserActionActivity,
|
||||
}
|
||||
return super.shouldInterceptRequest(view, request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,19 @@ import android.os.Build
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
|
||||
open class BasicWebViewClient(protected val activity: BrowserActionActivity,
|
||||
protected val verifyComplete: (String) -> Boolean,
|
||||
private val injectScript: String?) : WebViewClient() {
|
||||
open class BasicWebViewClient(
|
||||
protected val activity: BrowserActionActivity,
|
||||
protected val verifyComplete: (String) -> Boolean,
|
||||
private val injectScript: String?
|
||||
) : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
super.onPageFinished(view, url)
|
||||
|
||||
if(verifyComplete(url)) {
|
||||
if (verifyComplete(url)) {
|
||||
activity.finish()
|
||||
} else {
|
||||
if(injectScript != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
|
||||
if (injectScript != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
|
||||
view.evaluateJavascript("(function() {$injectScript})();", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
@@ -58,8 +62,8 @@ class BrowserActionActivity : AppCompatActivity() {
|
||||
setContentView(eu.kanade.tachiyomi.R.layout.eh_activity_captcha)
|
||||
|
||||
val sourceId = intent.getLongExtra(SOURCE_ID_EXTRA, -1)
|
||||
val originalSource = if(sourceId != -1L) sourceManager.get(sourceId) else null
|
||||
val source = if(originalSource != null) {
|
||||
val originalSource = if (sourceId != -1L) sourceManager.get(sourceId) else null
|
||||
val source = if (originalSource != null) {
|
||||
originalSource as? ActionCompletionVerifier
|
||||
?: run {
|
||||
(originalSource as? HttpSource)?.let {
|
||||
@@ -72,24 +76,24 @@ 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)
|
||||
|
||||
val verifyComplete = if(source != null) {
|
||||
val verifyComplete = if (source != null) {
|
||||
source::verifyComplete!!
|
||||
} else intent.getSerializableExtra(VERIFY_LAMBDA_EXTRA) as? (String) -> Boolean
|
||||
|
||||
if(verifyComplete == null || url == null) {
|
||||
if (verifyComplete == null || url == null) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
val actionStr = actionName ?: "Solve captcha"
|
||||
|
||||
toolbar.title = if(source != null) {
|
||||
toolbar.title = if (source != null) {
|
||||
"${source.name}: $actionStr"
|
||||
} else actionStr
|
||||
|
||||
@@ -115,13 +119,13 @@ class BrowserActionActivity : AppCompatActivity() {
|
||||
|
||||
webview.webChromeClient = object : WebChromeClient() {
|
||||
override fun onJsAlert(view: WebView?, url: String?, message: String, result: JsResult): Boolean {
|
||||
if(message.startsWith("exh-")) {
|
||||
if (message.startsWith("exh-")) {
|
||||
loadedInners++
|
||||
// Wait for both inner scripts to be loaded
|
||||
if(loadedInners >= 2) {
|
||||
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
|
||||
@@ -141,7 +145,7 @@ class BrowserActionActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
webview.webViewClient = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if(actionName == null && preferencesHelper.eh_autoSolveCaptchas().getOrDefault()) {
|
||||
if (actionName == null && preferencesHelper.eh_autoSolveCaptchas().getOrDefault()) {
|
||||
// Fetch auto-solve credentials early for speed
|
||||
credentialsObservable = httpClient.newCall(Request.Builder()
|
||||
// Rob demo credentials
|
||||
@@ -196,11 +200,11 @@ class BrowserActionActivity : AppCompatActivity() {
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
@JavascriptInterface
|
||||
fun callback(result: String?, loopId: String, stage: Int) {
|
||||
if(loopId != currentLoopId) return
|
||||
if (loopId != currentLoopId) return
|
||||
|
||||
when(stage) {
|
||||
when (stage) {
|
||||
STAGE_CHECKBOX -> {
|
||||
if(result!!.toBoolean()) {
|
||||
if (result!!.toBoolean()) {
|
||||
webview.postDelayed({
|
||||
getAudioButtonLocation(loopId)
|
||||
}, 250)
|
||||
@@ -211,7 +215,7 @@ class BrowserActionActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
STAGE_GET_AUDIO_BTN_LOCATION -> {
|
||||
if(result != null) {
|
||||
if (result != null) {
|
||||
val splitResult = result.split(" ").map { it.toFloat() }
|
||||
val origX = splitResult[0]
|
||||
val origY = splitResult[1]
|
||||
@@ -231,11 +235,11 @@ class BrowserActionActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
STAGE_DOWNLOAD_AUDIO -> {
|
||||
if(result != null) {
|
||||
if (result != null) {
|
||||
Timber.d("Got audio URL: $result")
|
||||
performRecognize(result)
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe ({
|
||||
.subscribe({
|
||||
Timber.d("Got audio transcript: $it")
|
||||
webview.post {
|
||||
typeResult(loopId, it!!
|
||||
@@ -253,7 +257,7 @@ class BrowserActionActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
STAGE_TYPE_RESULT -> {
|
||||
if(result!!.toBoolean()) {
|
||||
if (result!!.toBoolean()) {
|
||||
// Fail if captcha still not solved after 1.5s
|
||||
strictValidationStartTime = System.currentTimeMillis() + 1500
|
||||
} else {
|
||||
@@ -293,7 +297,7 @@ class BrowserActionActivity : AppCompatActivity() {
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
fun doStageCheckbox(loopId: String) {
|
||||
if(loopId != currentLoopId) return
|
||||
if (loopId != currentLoopId) return
|
||||
|
||||
webview.evaluateJavascript("""
|
||||
(function() {
|
||||
@@ -415,27 +419,26 @@ class BrowserActionActivity : AppCompatActivity() {
|
||||
doStageCheckbox(loopId)
|
||||
}
|
||||
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
@JavascriptInterface
|
||||
fun validateCaptchaCallback(result: Boolean, loopId: String) {
|
||||
if(loopId != validateCurrentLoopId) return
|
||||
if (loopId != validateCurrentLoopId) return
|
||||
|
||||
if(result) {
|
||||
if (result) {
|
||||
Timber.d("Captcha solved!")
|
||||
webview.post {
|
||||
webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE, null)
|
||||
}
|
||||
val asbtn = intent.getStringExtra(ASBTN_EXTRA)
|
||||
if(asbtn != null) {
|
||||
if (asbtn != null) {
|
||||
webview.post {
|
||||
webview.evaluateJavascript("(function() {document.querySelector('$asbtn').click();})();", null)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val savedStrictValidationStartTime = strictValidationStartTime
|
||||
if(savedStrictValidationStartTime != null
|
||||
&& System.currentTimeMillis() > savedStrictValidationStartTime) {
|
||||
if (savedStrictValidationStartTime != null &&
|
||||
System.currentTimeMillis() > savedStrictValidationStartTime) {
|
||||
captchaSolveFail()
|
||||
} else {
|
||||
webview.postDelayed({
|
||||
@@ -447,7 +450,7 @@ class BrowserActionActivity : AppCompatActivity() {
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
fun runValidateCaptcha(loopId: String) {
|
||||
if(loopId != validateCurrentLoopId) return
|
||||
if (loopId != validateCurrentLoopId) return
|
||||
|
||||
webview.evaluateJavascript("""
|
||||
(function() {
|
||||
@@ -624,12 +627,14 @@ class BrowserActionActivity : AppCompatActivity() {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
|
||||
fun launchCaptcha(context: Context,
|
||||
source: ActionCompletionVerifier,
|
||||
cookies: Map<String, String>,
|
||||
script: String?,
|
||||
url: String,
|
||||
autoSolveSubmitBtnSelector: String? = null) {
|
||||
fun launchCaptcha(
|
||||
context: Context,
|
||||
source: ActionCompletionVerifier,
|
||||
cookies: Map<String, String>,
|
||||
script: String?,
|
||||
url: String,
|
||||
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,
|
||||
source: HttpSource,
|
||||
url: String) {
|
||||
fun launchUniversal(
|
||||
context: Context,
|
||||
source: HttpSource,
|
||||
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,
|
||||
sourceId: Long,
|
||||
url: String) {
|
||||
fun launchUniversal(
|
||||
context: Context,
|
||||
sourceId: Long,
|
||||
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,
|
||||
completionVerifier: ActionCompletionVerifier,
|
||||
script: String?,
|
||||
url: String,
|
||||
actionName: String) {
|
||||
fun launchAction(
|
||||
context: Context,
|
||||
completionVerifier: ActionCompletionVerifier,
|
||||
script: String?,
|
||||
url: 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,
|
||||
completionVerifier: (String) -> Boolean,
|
||||
script: String?,
|
||||
url: String,
|
||||
actionName: String,
|
||||
headers: Map<String, String>? = emptyMap()) {
|
||||
fun launchAction(
|
||||
context: Context,
|
||||
completionVerifier: (String) -> Boolean,
|
||||
script: String?,
|
||||
url: String,
|
||||
actionName: String,
|
||||
headers: Map<String, String>? = emptyMap()
|
||||
) {
|
||||
val intent = baseIntent(context).apply {
|
||||
putExtra(HEADERS_EXTRA, HashMap(headers))
|
||||
putExtra(VERIFY_LAMBDA_EXTRA, completionVerifier as Serializable)
|
||||
@@ -697,7 +710,7 @@ class BrowserActionActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
class NoopActionCompletionVerifier(private val source: HttpSource): DelegatedHttpSource(source),
|
||||
class NoopActionCompletionVerifier(private val source: HttpSource) : DelegatedHttpSource(source),
|
||||
ActionCompletionVerifier {
|
||||
override val versionId get() = source.versionId
|
||||
override val lang: String get() = source.lang
|
||||
@@ -708,4 +721,3 @@ class NoopActionCompletionVerifier(private val source: HttpSource): DelegatedHtt
|
||||
interface ActionCompletionVerifier : Source {
|
||||
fun verifyComplete(url: String): Boolean
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@ import android.webkit.WebView
|
||||
import androidx.annotation.RequiresApi
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
open class HeadersInjectingWebViewClient(activity: BrowserActionActivity,
|
||||
verifyComplete: (String) -> Boolean,
|
||||
injectScript: String?,
|
||||
private val headers: Map<String, String>)
|
||||
: BasicWebViewClient(activity, verifyComplete, injectScript) {
|
||||
open class HeadersInjectingWebViewClient(
|
||||
activity: BrowserActionActivity,
|
||||
verifyComplete: (String) -> Boolean,
|
||||
injectScript: String?,
|
||||
private val headers: Map<String, String>
|
||||
) :
|
||||
BasicWebViewClient(activity, verifyComplete, injectScript) {
|
||||
|
||||
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
|
||||
// Temp disabled as it's unreliable
|
||||
|
||||
@@ -16,4 +16,4 @@ fun WebResourceRequest.toOkHttpRequest(): Request {
|
||||
}
|
||||
|
||||
return request.build()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class InterceptActivity : BaseRxActivity<InterceptActivityPresenter>() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.eh_activity_intercept)
|
||||
|
||||
//Show back button
|
||||
// Show back button
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
@@ -31,7 +31,7 @@ class InterceptActivity : BaseRxActivity<InterceptActivityPresenter>() {
|
||||
}
|
||||
|
||||
private fun processLink() {
|
||||
if(Intent.ACTION_VIEW == intent.action) {
|
||||
if (Intent.ACTION_VIEW == intent.action) {
|
||||
intercept_progress.visible()
|
||||
intercept_status.text = "Loading gallery..."
|
||||
presenter.loadGallery(intent.dataString)
|
||||
@@ -52,7 +52,7 @@ class InterceptActivity : BaseRxActivity<InterceptActivityPresenter>() {
|
||||
statusSubscription = presenter.status
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
when(it) {
|
||||
when (it) {
|
||||
is InterceptResult.Success -> {
|
||||
intercept_progress.gone()
|
||||
intercept_status.text = "Launching app..."
|
||||
|
||||
@@ -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()
|
||||
@@ -13,11 +13,11 @@ class InterceptActivityPresenter : BasePresenter<InterceptActivity>() {
|
||||
|
||||
@Synchronized
|
||||
fun loadGallery(gallery: String) {
|
||||
//Do not load gallery if already loading
|
||||
if(status.value is InterceptResult.Idle) {
|
||||
// Do not load gallery if already loading
|
||||
if (status.value is InterceptResult.Idle) {
|
||||
status.onNext(InterceptResult.Loading())
|
||||
|
||||
//Load gallery async
|
||||
// Load gallery async
|
||||
thread {
|
||||
val result = galleryAdder.addGallery(gallery)
|
||||
|
||||
@@ -35,6 +35,6 @@ class InterceptActivityPresenter : BasePresenter<InterceptActivity>() {
|
||||
sealed class InterceptResult {
|
||||
class Idle : InterceptResult()
|
||||
class Loading : InterceptResult()
|
||||
data class Success(val mangaId: Long): InterceptResult()
|
||||
data class Failure(val reason: String): InterceptResult()
|
||||
}
|
||||
data class Success(val mangaId: Long) : InterceptResult()
|
||||
data class Failure(val reason: String) : InterceptResult()
|
||||
}
|
||||
|
||||
@@ -28,21 +28,21 @@ 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() {
|
||||
super.onAttached()
|
||||
if(fingerprintSupported) {
|
||||
if (fingerprintSupported) {
|
||||
updateSummary()
|
||||
onChange {
|
||||
if(it as Boolean)
|
||||
if (it as Boolean)
|
||||
tryChange()
|
||||
else
|
||||
prefs.eh_lockUseFingerprint().set(false)
|
||||
@@ -51,7 +51,7 @@ class FingerLockPreference @JvmOverloads constructor(context: Context, attrs: At
|
||||
} else {
|
||||
title = "Fingerprint unsupported"
|
||||
shouldDisableView = true
|
||||
summary = if(!Reprint.hasFingerprintRegistered())
|
||||
summary = if (!Reprint.hasFingerprintRegistered())
|
||||
"No fingerprints enrolled!"
|
||||
else
|
||||
"Fingerprint unlock is unsupported on this device!"
|
||||
@@ -61,7 +61,7 @@ class FingerLockPreference @JvmOverloads constructor(context: Context, attrs: At
|
||||
|
||||
private fun updateSummary() {
|
||||
isChecked = useFingerprint
|
||||
title = if(isChecked)
|
||||
title = if (isChecked)
|
||||
"Fingerprint enabled"
|
||||
else
|
||||
"Fingerprint disabled"
|
||||
@@ -146,4 +146,4 @@ class FingerLockPreference @JvmOverloads constructor(context: Context, attrs: At
|
||||
subscription.unsubscribe()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ 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()
|
||||
constructor() : super()
|
||||
|
||||
constructor(removesFromViewOnPush: Boolean) : super(removesFromViewOnPush)
|
||||
|
||||
@@ -36,6 +36,4 @@ class LockChangeHandler : AnimatorChangeHandler {
|
||||
|
||||
override fun copy(): ControllerChangeHandler =
|
||||
LockChangeHandler(animationDuration, removesFromViewOnPush())
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -32,13 +32,13 @@ class LockController : NucleusController<LockPresenter>() {
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
if(!lockEnabled(prefs)) {
|
||||
if (!lockEnabled(prefs)) {
|
||||
closeLock()
|
||||
return
|
||||
}
|
||||
|
||||
with(view) {
|
||||
//Setup pin lock
|
||||
// Setup pin lock
|
||||
pin_lock_view.attachIndicatorDots(indicator_dots)
|
||||
|
||||
pin_lock_view.pinLength = prefs.eh_lockLength().getOrDefault()
|
||||
@@ -47,7 +47,7 @@ class LockController : NucleusController<LockPresenter>() {
|
||||
|
||||
override fun onComplete(pin: String) {
|
||||
if (sha512(pin, prefs.eh_lockSalt().get()!!) == prefs.eh_lockHash().get()) {
|
||||
//Yay!
|
||||
// Yay!
|
||||
closeLock()
|
||||
} else {
|
||||
MaterialDialog.Builder(context)
|
||||
@@ -72,7 +72,7 @@ class LockController : NucleusController<LockPresenter>() {
|
||||
super.onAttach(view)
|
||||
|
||||
with(view) {
|
||||
//Fingerprint
|
||||
// Fingerprint
|
||||
if (presenter.useFingerprint) {
|
||||
swirl_container.visibility = View.VISIBLE
|
||||
swirl_container.removeAllViews()
|
||||
@@ -90,7 +90,7 @@ class LockController : NucleusController<LockPresenter>() {
|
||||
val lockColor = resolvColor(android.R.attr.windowBackground)
|
||||
setBackgroundColor(lockColor)
|
||||
val bgColor = resolvColor(android.R.attr.colorBackground)
|
||||
//Disable elevation if lock color is same as background color
|
||||
// Disable elevation if lock color is same as background color
|
||||
if (lockColor == bgColor)
|
||||
this@with.swirl_container.cardElevation = 0f
|
||||
setState(SwirlView.State.OFF, true)
|
||||
|
||||
@@ -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) {
|
||||
@@ -33,7 +33,7 @@ class LockPreference @JvmOverloads constructor(context: Context, attrs: Attribut
|
||||
|
||||
private fun updateSummary() {
|
||||
isChecked = lockEnabled(prefs)
|
||||
if(isChecked) {
|
||||
if (isChecked) {
|
||||
title = "Lock enabled"
|
||||
summary = "Tap to disable or change pin code"
|
||||
} else {
|
||||
@@ -43,7 +43,7 @@ class LockPreference @JvmOverloads constructor(context: Context, attrs: Attribut
|
||||
}
|
||||
|
||||
fun tryChange() {
|
||||
if(!notifyLockSecurity(context)) {
|
||||
if (!notifyLockSecurity(context)) {
|
||||
MaterialDialog.Builder(context)
|
||||
.title("Lock application")
|
||||
.content("Enter a pin to lock the application. Enter nothing to disable the pin lock.")
|
||||
@@ -76,7 +76,7 @@ class LockPreference @JvmOverloads constructor(context: Context, attrs: Attribut
|
||||
val salt: String?
|
||||
val hash: String?
|
||||
val length: Int
|
||||
if(password.isEmpty()) {
|
||||
if (password.isEmpty()) {
|
||||
salt = null
|
||||
hash = null
|
||||
length = -1
|
||||
|
||||
@@ -7,13 +7,12 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class LockPresenter: BasePresenter<LockController>() {
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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. " +
|
||||
@@ -66,7 +67,7 @@ fun notifyLockSecurity(context: Context,
|
||||
.onPositive { _, _ ->
|
||||
try {
|
||||
context.startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS))
|
||||
} catch(e: ActivityNotFoundException) {
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
XLog.e("Device does not support USAGE_ACCESS_SETTINGS shortcut!")
|
||||
MaterialDialog.Builder(context)
|
||||
.title("Grant permission manually")
|
||||
|
||||
@@ -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
|
||||
@@ -104,17 +104,17 @@ class LoginController : NucleusController<LoginPresenter>() {
|
||||
Timber.d(url)
|
||||
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)
|
||||
// Hide distracting content
|
||||
if (!parsedUrl.queryParameterNames.contains(PARAM_SKIP_INJECT) &&
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
|
||||
view.evaluateJavascript(HIDE_JS, null)
|
||||
|
||||
//Check login result
|
||||
// Check login result
|
||||
if (parsedUrl.getQueryParameter("code")?.toInt() != 0) {
|
||||
if (checkLoginCookies(url)) view.loadUrl("https://exhentai.org/")
|
||||
}
|
||||
} else if (parsedUrl.host.equals("exhentai.org", ignoreCase = true)) {
|
||||
//At ExHentai, check that everything worked out...
|
||||
// At ExHentai, check that everything worked out...
|
||||
if (applyExHentaiCookies(url)) {
|
||||
preferenceManager.enableExhentai().set(true)
|
||||
finishLogin()
|
||||
@@ -128,7 +128,7 @@ class LoginController : NucleusController<LoginPresenter>() {
|
||||
fun finishLogin() {
|
||||
router.popCurrentController()
|
||||
|
||||
//Upload settings
|
||||
// Upload settings
|
||||
WarnConfigureDialogController.uploadSettings(router)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -164,10 +164,10 @@ class LoginController : NucleusController<LoginPresenter>() {
|
||||
}
|
||||
}
|
||||
|
||||
//Missing a cookie
|
||||
// Missing a cookie
|
||||
if (memberId == null || passHash == null || igneous == null) return false
|
||||
|
||||
//Update prefs
|
||||
// Update prefs
|
||||
preferenceManager.memberIdVal().set(memberId)
|
||||
preferenceManager.passHashVal().set(passHash)
|
||||
preferenceManager.igneousVal().set(igneous)
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,4 @@ package exh.ui.login
|
||||
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
|
||||
class LoginPresenter: BasePresenter<LoginController>() {
|
||||
|
||||
}
|
||||
class LoginPresenter : BasePresenter<LoginController>()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -25,7 +25,7 @@ class MetadataFetchDialog {
|
||||
val preferenceHelper: PreferencesHelper by injectLazy()
|
||||
|
||||
fun show(context: Activity) {
|
||||
//Too lazy to actually deal with orientation changes
|
||||
// Too lazy to actually deal with orientation changes
|
||||
context.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
|
||||
|
||||
var running = true
|
||||
@@ -55,7 +55,7 @@ class MetadataFetchDialog {
|
||||
|
||||
val mangaWithMissingMetadata = libraryMangas
|
||||
.filterIndexed { index, libraryManga ->
|
||||
if(index % 100 == 0) {
|
||||
if (index % 100 == 0) {
|
||||
context.runOnUiThread {
|
||||
progressDialog.setContent("[Stage 1/2] Scanning for missing metadata...")
|
||||
progressDialog.setProgress(index + 1)
|
||||
@@ -69,9 +69,9 @@ class MetadataFetchDialog {
|
||||
progressDialog.maxProgress = mangaWithMissingMetadata.size
|
||||
}
|
||||
|
||||
//Actual metadata fetch code
|
||||
for((i, manga) in mangaWithMissingMetadata.withIndex()) {
|
||||
if(!running) break
|
||||
// Actual metadata fetch code
|
||||
for ((i, manga) in mangaWithMissingMetadata.withIndex()) {
|
||||
if (!running) break
|
||||
context.runOnUiThread {
|
||||
progressDialog.setContent("[Stage 2/2] Processing: ${manga.title}")
|
||||
progressDialog.setProgress(i + 1)
|
||||
@@ -88,10 +88,10 @@ class MetadataFetchDialog {
|
||||
|
||||
context.runOnUiThread {
|
||||
// Ensure activity still exists before we do anything to the activity
|
||||
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 || !context.isDestroyed) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 || !context.isDestroyed) {
|
||||
progressDialog.dismiss()
|
||||
|
||||
//Enable orientation changes again
|
||||
// Enable orientation changes again
|
||||
context.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
|
||||
|
||||
if (running) displayMigrationComplete(context)
|
||||
@@ -103,12 +103,12 @@ class MetadataFetchDialog {
|
||||
fun askMigration(activity: Activity, explicit: Boolean) {
|
||||
var extra = ""
|
||||
db.getLibraryMangas().asRxSingle().subscribe {
|
||||
if(!explicit && it.none { isLewdSource(it.source) }) {
|
||||
if (!explicit && it.none { isLewdSource(it.source) }) {
|
||||
// Do not open dialog on startup if no manga
|
||||
// Also do not check again
|
||||
preferenceHelper.migrateLibraryAsked().set(true)
|
||||
} else {
|
||||
//Not logged in but have ExHentai galleries
|
||||
// Not logged in but have ExHentai galleries
|
||||
if (!preferenceHelper.enableExhentai().getOrDefault()) {
|
||||
it.find { it.source == EXH_SOURCE_ID }?.let {
|
||||
extra = "<b><font color='red'>If you use ExHentai, please log in first before fetching your library metadata!</font></b><br><br>"
|
||||
@@ -132,7 +132,6 @@ class MetadataFetchDialog {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun adviseMigrationLater(activity: Activity) {
|
||||
|
||||
@@ -5,7 +5,7 @@ class MigrationStatus {
|
||||
val NOT_INITIALIZED = -1
|
||||
val COMPLETED = 0
|
||||
|
||||
//Migration process
|
||||
// Migration process
|
||||
val NOTIFY_USER = 1
|
||||
val OPEN_BACKUP_MENU = 2
|
||||
val PERFORM_BACKUP = 3
|
||||
|
||||
@@ -76,16 +76,16 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
||||
updateOptionsState()
|
||||
|
||||
begin_migration_btn.setOnClickListener {
|
||||
if(!showingOptions) {
|
||||
if (!showingOptions) {
|
||||
showingOptions = true
|
||||
updateOptionsState()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
var flags = 0
|
||||
if(mig_chapters.isChecked) flags = flags or MigrationFlags.CHAPTERS
|
||||
if(mig_categories.isChecked) flags = flags or MigrationFlags.CATEGORIES
|
||||
if(mig_categories.isChecked) flags = flags or MigrationFlags.TRACK
|
||||
if (mig_chapters.isChecked) flags = flags or MigrationFlags.CHAPTERS
|
||||
if (mig_categories.isChecked) flags = flags or MigrationFlags.CATEGORIES
|
||||
if (mig_categories.isChecked) flags = flags or MigrationFlags.TRACK
|
||||
|
||||
router.replaceTopController(MigrationProcedureController.create(
|
||||
MigrationProcedureConfig(
|
||||
@@ -97,7 +97,7 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
||||
enableLenientSearch = use_smart_search.isChecked,
|
||||
migrationFlags = flags,
|
||||
copy = copy_manga.isChecked,
|
||||
extraSearchParams = if(extra_search_param.isChecked && extra_search_param_text.text.isNotBlank()) {
|
||||
extraSearchParams = if (extra_search_param.isChecked && extra_search_param_text.text.isNotBlank()) {
|
||||
extra_search_param_text.text.toString()
|
||||
} else null
|
||||
)
|
||||
@@ -109,7 +109,7 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
||||
if (showingOptions) {
|
||||
begin_migration_btn.text = "Begin migration"
|
||||
options_group.visible()
|
||||
if(extra_search_param.isChecked) {
|
||||
if (extra_search_param.isChecked) {
|
||||
extra_search_param_text.visible()
|
||||
} else {
|
||||
extra_search_param_text.gone()
|
||||
@@ -122,7 +122,7 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
||||
}
|
||||
|
||||
override fun handleBack(): Boolean {
|
||||
if(showingOptions) {
|
||||
if (showingOptions) {
|
||||
showingOptions = false
|
||||
updateOptionsState()
|
||||
return true
|
||||
@@ -142,7 +142,7 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
||||
}
|
||||
|
||||
private fun updatePrioritizeChapterCount(migrationMode: Boolean) {
|
||||
migration_mode.text = if(migrationMode) {
|
||||
migration_mode.text = if (migrationMode) {
|
||||
"Currently using the source with the most chapters and the above list to break ties (slow with many sources or smart search)"
|
||||
} else {
|
||||
"Currently using the first source in the list that has the manga"
|
||||
@@ -182,4 +182,4 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -29,4 +31,4 @@ class MigrationSourceAdapter(val items: List<MigrationSourceItem>,
|
||||
companion object {
|
||||
private const val SELECTED_SOURCES_KEY = "selected_sources"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
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>):
|
||||
class MigrationSourceHolder(view: View, val adapter: FlexibleAdapter<MigrationSourceItem>) :
|
||||
BaseFlexibleViewHolder(view, adapter) {
|
||||
init {
|
||||
setDragHandleView(reorder)
|
||||
@@ -20,10 +20,10 @@ class MigrationSourceHolder(view: View, val adapter: FlexibleAdapter<MigrationSo
|
||||
|
||||
// Update circle letter image.
|
||||
itemView.post {
|
||||
image.setImageDrawable(image.getRound(source.name.take(1).toUpperCase(),false))
|
||||
image.setImageDrawable(image.getRound(source.name.take(1).toUpperCase(), false))
|
||||
}
|
||||
|
||||
if(sourceEnabled) {
|
||||
if (sourceEnabled) {
|
||||
title.alpha = 1.0f
|
||||
image.alpha = 1.0f
|
||||
title.paintFlags = title.paintFlags and STRIKE_THRU_TEXT_FLAG.inv()
|
||||
@@ -37,4 +37,4 @@ class MigrationSourceHolder(view: View, val adapter: FlexibleAdapter<MigrationSo
|
||||
companion object {
|
||||
private const val DISABLED_ALPHA = 0.3f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
class MigrationSourceItem(val source: HttpSource, var sourceEnabled: Boolean): AbstractFlexibleItem<MigrationSourceHolder>() {
|
||||
class MigrationSourceItem(val source: HttpSource, var sourceEnabled: Boolean) : AbstractFlexibleItem<MigrationSourceHolder>() {
|
||||
override fun getLayoutRes() = R.layout.eh_source_item
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>): MigrationSourceHolder {
|
||||
@@ -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>>,
|
||||
holder: MigrationSourceHolder,
|
||||
position: Int,
|
||||
payloads: List<Any?>?) {
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>,
|
||||
holder: MigrationSourceHolder,
|
||||
position: Int,
|
||||
payloads: List<Any?>?
|
||||
) {
|
||||
holder.bind(source, sourceEnabled)
|
||||
}
|
||||
|
||||
@@ -52,7 +54,7 @@ class MigrationSourceItem(val source: HttpSource, var sourceEnabled: Boolean): A
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class ParcelableSI(val sourceId: Long, val sourceEnabled: Boolean): Parcelable
|
||||
data class ParcelableSI(val sourceId: Long, val sourceEnabled: Boolean) : Parcelable
|
||||
|
||||
fun asParcelable(): ParcelableSI {
|
||||
return ParcelableSI(source.id, sourceEnabled)
|
||||
@@ -68,4 +70,4 @@ class MigrationSourceItem(val source: HttpSource, var sourceEnabled: Boolean): A
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
|
||||
class DeactivatableViewPager : androidx.viewpager.widget.ViewPager {
|
||||
constructor(context: Context): super(context)
|
||||
constructor(context: Context, attrs: AttributeSet): super(context, attrs)
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
return !isEnabled || super.onTouchEvent(event)
|
||||
@@ -15,4 +15,4 @@ class DeactivatableViewPager : androidx.viewpager.widget.ViewPager {
|
||||
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
|
||||
return isEnabled && super.onInterceptTouchEvent(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
private val sourceManager: SourceManager,
|
||||
val mangaId: Long,
|
||||
parentContext: CoroutineContext) {
|
||||
class MigratingManga(
|
||||
private val db: DatabaseHelper,
|
||||
private val sourceManager: SourceManager,
|
||||
val mangaId: Long,
|
||||
parentContext: CoroutineContext
|
||||
) {
|
||||
val searchResult = DeferredField<Long?>()
|
||||
|
||||
// <MAX, PROGRESS>
|
||||
@@ -24,11 +27,11 @@ class MigratingManga(private val db: DatabaseHelper,
|
||||
@Volatile
|
||||
private var manga: Manga? = null
|
||||
suspend fun manga(): Manga? {
|
||||
if(manga == null) manga = db.getManga(mangaId).await()
|
||||
if (manga == null) manga = db.getManga(mangaId).await()
|
||||
return manga
|
||||
}
|
||||
|
||||
suspend fun mangaSource(): Source {
|
||||
return sourceManager.getOrStub(manga()?.source ?: -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
val migratingManga: List<MigratingManga>,
|
||||
override val coroutineContext: CoroutineContext) : androidx.viewpager.widget.PagerAdapter(), CoroutineScope {
|
||||
class MigrationProcedureAdapter(
|
||||
val controller: MigrationProcedureController,
|
||||
val migratingManga: List<MigratingManga>,
|
||||
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()
|
||||
@@ -69,7 +76,7 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
||||
performMigration(item)
|
||||
}
|
||||
controller.nextMigration()
|
||||
} catch(e: Exception) {
|
||||
} catch (e: Exception) {
|
||||
logger.e("Migration failure!", e)
|
||||
controller.migrationFailure()
|
||||
}
|
||||
@@ -81,7 +88,7 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
||||
}
|
||||
|
||||
suspend fun performMigration(manga: MigratingManga) {
|
||||
if(!manga.searchResult.initialized) {
|
||||
if (!manga.searchResult.initialized) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -96,9 +103,11 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrateMangaInternal(prevManga: Manga,
|
||||
manga: Manga,
|
||||
replace: Boolean) {
|
||||
private fun migrateMangaInternal(
|
||||
prevManga: Manga,
|
||||
manga: Manga,
|
||||
replace: Boolean
|
||||
) {
|
||||
db.inTransaction {
|
||||
// Update chapters read
|
||||
if (MigrationFlags.hasChapters(controller.config.migrationFlags)) {
|
||||
@@ -147,7 +156,7 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
||||
tag.launch {
|
||||
val manga = migratingManga.manga()
|
||||
val source = migratingManga.mangaSource()
|
||||
if(manga != null) {
|
||||
if (manga != null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
eh_manga_card_from.loading_group.gone()
|
||||
eh_manga_card_from.attachManga(tag, manga, source)
|
||||
@@ -174,7 +183,7 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
||||
sourceManager.get(it)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if(searchResult != null && resultSource != null) {
|
||||
if (searchResult != null && resultSource != null) {
|
||||
eh_manga_card_to.loading_group.gone()
|
||||
eh_manga_card_to.attachManga(tag, searchResult, resultSource)
|
||||
eh_manga_card_to.setOnClickListener {
|
||||
@@ -263,7 +272,7 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
||||
(objectAsView.tag as? ViewTag)?.destroy()
|
||||
}
|
||||
|
||||
class ViewTag(parent: CoroutineContext): CoroutineScope {
|
||||
class ViewTag(parent: CoroutineContext) : CoroutineScope {
|
||||
/**
|
||||
* The context of this scope.
|
||||
* Context is encapsulated by the scope and used for implementation of coroutine builders that are extensions on the scope.
|
||||
@@ -277,4 +286,4 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class MigrationProcedureConfig(
|
||||
val mangaIds: List<Long>,
|
||||
val targetSourceIds: List<Long>,
|
||||
val useSourceWithMostChapters: Boolean,
|
||||
val enableLenientSearch: Boolean,
|
||||
val migrationFlags: Int,
|
||||
val copy: Boolean,
|
||||
val extraSearchParams: String?
|
||||
): Parcelable
|
||||
val mangaIds: List<Long>,
|
||||
val targetSourceIds: List<Long>,
|
||||
val useSourceWithMostChapters: Boolean,
|
||||
val enableLenientSearch: Boolean,
|
||||
val migrationFlags: Int,
|
||||
val copy: Boolean,
|
||||
val extraSearchParams: String?
|
||||
) : Parcelable
|
||||
|
||||
@@ -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 {
|
||||
@@ -70,7 +78,7 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
pager.adapter = adapter
|
||||
pager.isEnabled = false
|
||||
|
||||
if(migrationsJob == null) {
|
||||
if (migrationsJob == null) {
|
||||
migrationsJob = launch {
|
||||
runMigrations(newMigratingManga)
|
||||
}
|
||||
@@ -89,7 +97,7 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
|
||||
fun nextMigration() {
|
||||
adapter?.let { adapter ->
|
||||
if(pager.currentItem >= adapter.count - 1) {
|
||||
if (pager.currentItem >= adapter.count - 1) {
|
||||
applicationContext?.toast("All migrations complete!")
|
||||
router.popCurrentController()
|
||||
} else {
|
||||
@@ -115,11 +123,11 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
suspend fun runMigrations(mangas: List<MigratingManga>) {
|
||||
val sources = config.targetSourceIds.mapNotNull { sourceManager.get(it) as? CatalogueSource }
|
||||
|
||||
for(manga in mangas) {
|
||||
if(!manga.searchResult.initialized && manga.migrationJob.isActive) {
|
||||
for (manga in mangas) {
|
||||
if (!manga.searchResult.initialized && manga.migrationJob.isActive) {
|
||||
val mangaObj = manga.manga()
|
||||
|
||||
if(mangaObj == null) {
|
||||
if (mangaObj == null) {
|
||||
manga.searchResult.initialize(null)
|
||||
continue
|
||||
}
|
||||
@@ -131,39 +139,39 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
val validSources = sources.filter {
|
||||
it.id != mangaSource.id
|
||||
}
|
||||
if(config.useSourceWithMostChapters) {
|
||||
if (config.useSourceWithMostChapters) {
|
||||
val sourceSemaphore = Semaphore(3)
|
||||
val processedSources = AtomicInteger()
|
||||
|
||||
validSources.map { source ->
|
||||
async {
|
||||
sourceSemaphore.withPermit {
|
||||
try {
|
||||
val searchResult = if (config.enableLenientSearch) {
|
||||
smartSearchEngine.smartSearch(source, mangaObj.title)
|
||||
} else {
|
||||
smartSearchEngine.normalSearch(source, mangaObj.title)
|
||||
}
|
||||
async {
|
||||
sourceSemaphore.withPermit {
|
||||
try {
|
||||
val searchResult = if (config.enableLenientSearch) {
|
||||
smartSearchEngine.smartSearch(source, mangaObj.title)
|
||||
} else {
|
||||
smartSearchEngine.normalSearch(source, mangaObj.title)
|
||||
}
|
||||
|
||||
if(searchResult != null) {
|
||||
val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id)
|
||||
val chapters = source.fetchChapterList(localManga).toSingle().await(Schedulers.io())
|
||||
withContext(Dispatchers.IO) {
|
||||
syncChaptersWithSource(db, chapters, localManga, source)
|
||||
}
|
||||
manga.progress.send(validSources.size to processedSources.incrementAndGet())
|
||||
localManga to chapters.size
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} catch(e: CancellationException) {
|
||||
// Ignore cancellations
|
||||
throw e
|
||||
} catch(e: Exception) {
|
||||
logger.e("Failed to search in source: ${source.id}!", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
if (searchResult != null) {
|
||||
val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id)
|
||||
val chapters = source.fetchChapterList(localManga).toSingle().await(Schedulers.io())
|
||||
withContext(Dispatchers.IO) {
|
||||
syncChaptersWithSource(db, chapters, localManga, source)
|
||||
}
|
||||
manga.progress.send(validSources.size to processedSources.incrementAndGet())
|
||||
localManga to chapters.size
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} catch (e: CancellationException) {
|
||||
// Ignore cancellations
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
logger.e("Failed to search in source: ${source.id}!", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}.mapNotNull { it.await() }.maxBy { it.second }?.first
|
||||
} else {
|
||||
@@ -183,28 +191,28 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
}
|
||||
localManga
|
||||
} else null
|
||||
} catch(e: CancellationException) {
|
||||
} catch (e: CancellationException) {
|
||||
// Ignore cancellations
|
||||
throw e
|
||||
} catch(e: Exception) {
|
||||
} catch (e: Exception) {
|
||||
logger.e("Failed to search in source: ${source.id}!", e)
|
||||
null
|
||||
}
|
||||
|
||||
manga.progress.send(validSources.size to (index + 1))
|
||||
|
||||
if(searchResult != null) return@async searchResult
|
||||
if (searchResult != null) return@async searchResult
|
||||
}
|
||||
|
||||
null
|
||||
}
|
||||
}.await()
|
||||
} catch(e: CancellationException) {
|
||||
} catch (e: CancellationException) {
|
||||
// Ignore canceled migrations
|
||||
continue
|
||||
}
|
||||
|
||||
if(result != null && result.thumbnail_url == null) {
|
||||
if (result != null && result.thumbnail_url == null) {
|
||||
try {
|
||||
val newManga = sourceManager.getOrStub(result.source)
|
||||
.fetchMangaDetails(result)
|
||||
@@ -213,10 +221,10 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
result.copyFrom(newManga)
|
||||
|
||||
db.insertManga(result).await()
|
||||
} catch(e: CancellationException) {
|
||||
} catch (e: CancellationException) {
|
||||
// Ignore cancellations
|
||||
throw e
|
||||
} catch(e: Exception) {
|
||||
} catch (e: Exception) {
|
||||
logger.e("Could not load search manga details", e)
|
||||
}
|
||||
}
|
||||
@@ -241,4 +249,4 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -37,7 +43,7 @@ class SmartSearchController(bundle: Bundle? = null) : NucleusController<SmartSea
|
||||
|
||||
appbar.bringToFront()
|
||||
|
||||
if(source == null || smartSearchConfig == null) {
|
||||
if (source == null || smartSearchConfig == null) {
|
||||
router.popCurrentController()
|
||||
applicationContext?.toast("Missing data!")
|
||||
return
|
||||
@@ -47,7 +53,7 @@ class SmartSearchController(bundle: Bundle? = null) : NucleusController<SmartSea
|
||||
presenter
|
||||
|
||||
launch(Dispatchers.Default) {
|
||||
for(event in presenter.smartSearchChannel) {
|
||||
for (event in presenter.smartSearchChannel) {
|
||||
withContext(NonCancellable) {
|
||||
if (event is SmartSearchPresenter.SearchResults.Found) {
|
||||
val transaction = MangaController(event.manga, true, smartSearchConfig).withFadeTransaction()
|
||||
@@ -81,4 +87,4 @@ class SmartSearchController(bundle: Bundle? = null) : NucleusController<SmartSea
|
||||
const val ARG_SOURCE_ID = "SOURCE_ID"
|
||||
const val ARG_SMART_SEARCH_CONFIG = "SMART_SEARCH_CONFIG"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,15 @@ 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?):
|
||||
class SmartSearchPresenter(private val source: CatalogueSource?, private val config: CatalogueController.SmartSearchConfig?) :
|
||||
BasePresenter<SmartSearchController>(), CoroutineScope {
|
||||
private val logger = XLog.tag("SmartSearchPresenter")
|
||||
|
||||
@@ -24,7 +29,7 @@ class SmartSearchPresenter(private val source: CatalogueSource?, private val con
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
if(source != null && config != null) {
|
||||
if (source != null && config != null) {
|
||||
launch(Dispatchers.Default) {
|
||||
val result = try {
|
||||
val resultManga = smartSearchEngine.smartSearch(source, config.origTitle)
|
||||
@@ -48,7 +53,6 @@ class SmartSearchPresenter(private val source: CatalogueSource?, private val con
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
@@ -58,8 +62,8 @@ class SmartSearchPresenter(private val source: CatalogueSource?, private val con
|
||||
data class SearchEntry(val manga: SManga, val dist: Double)
|
||||
|
||||
sealed class SearchResults {
|
||||
data class Found(val manga: Manga): SearchResults()
|
||||
object NotFound: SearchResults()
|
||||
object Error: SearchResults()
|
||||
data class Found(val manga: Manga) : SearchResults()
|
||||
object NotFound : SearchResults()
|
||||
object Error : SearchResults()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user