Convert java threads to kotlin coroutines

This commit is contained in:
Jobobby04 2020-10-29 15:29:43 -04:00
parent 46998d81f4
commit 3b5249c8bc
10 changed files with 117 additions and 77 deletions

View File

@ -43,6 +43,7 @@ import exh.md.utils.MdUtil
import exh.metadata.metadata.MangaDexSearchMetadata
import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.MangaDexDescriptionAdapter
import exh.util.asObservable
import exh.util.urlImportFetchSearchManga
import exh.widget.preference.MangadexLoginDialog
import kotlinx.coroutines.Dispatchers
@ -266,20 +267,22 @@ class MangaDex(delegate: HttpSource, val context: Context) :
private fun importIdToMdId(query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
when {
query.toIntOrNull() != null -> {
Observable.fromCallable {
// MdUtil.
val res = GalleryAdder().addGallery(context, MdUtil.baseUrl + MdUtil.mapMdIdToMangaUrl(query.toInt()), false, this)
MangasPage(
(
if (res is GalleryAddEvent.Success) {
listOf(res.manga)
} else {
emptyList()
}
),
false
)
flow {
emit(GalleryAdder().addGallery(context, MdUtil.baseUrl + MdUtil.mapMdIdToMangaUrl(query.toInt()), false, this@MangaDex))
}
.asObservable()
.map { res ->
MangasPage(
(
if (res is GalleryAddEvent.Success) {
listOf(res.manga)
} else {
emptyList()
}
),
false
)
}
}
else -> fail()
}

View File

@ -91,9 +91,9 @@ class ChapterLoader(
return when {
// SY -->
source is MergedSource -> {
val mangaReference = mergedReferences.firstOrNull { it.mangaId == chapter.chapter.manga_id } ?: throw Exception("Merge reference null")
val source = sourceManager.get(mangaReference.mangaSourceId) ?: throw Exception("Source ${mangaReference.mangaSourceId} was null")
val manga = mergedManga.firstOrNull { it.id == chapter.chapter.manga_id } ?: throw Exception("Manga for merged chapter was null")
val mangaReference = mergedReferences.firstOrNull { it.mangaId == chapter.chapter.manga_id } ?: error("Merge reference null")
val source = sourceManager.get(mangaReference.mangaSourceId) ?: error("Source ${mangaReference.mangaSourceId} was null")
val manga = mergedManga.firstOrNull { it.id == chapter.chapter.manga_id } ?: error("Manga for merged chapter was null")
val isMergedMangaDownloaded = downloadManager.isChapterDownloaded(chapter.chapter, manga, true)
when {
isMergedMangaDownloaded -> DownloadPageLoader(chapter, manga, source, downloadManager)

View File

@ -11,6 +11,8 @@ import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import exh.source.getMainSource
import exh.util.await
import exh.util.awaitSingle
import uy.kohesive.injekt.injectLazy
import java.util.Date
@ -34,7 +36,7 @@ class GalleryAdder {
}
}
fun addGallery(
suspend fun addGallery(
context: Context,
url: String,
fav: Boolean = false,
@ -84,7 +86,7 @@ class GalleryAdder {
} ?: return GalleryAddEvent.Fail.UnknownType(url, context)
// Use manga in DB if possible, otherwise, make a new manga
val manga = db.getManga(cleanedUrl, source.id).executeAsBlocking()
val manga = db.getManga(cleanedUrl, source.id).await()
?: Manga.create(source.id).apply {
this.url = cleanedUrl
title = realUrl
@ -93,13 +95,13 @@ class GalleryAdder {
// Insert created manga if not in DB before fetching details
// This allows us to keep the metadata when fetching details
if (manga.id == null) {
db.insertManga(manga).executeAsBlocking().insertedId()?.let {
db.insertManga(manga).await().insertedId()?.let {
manga.id = it
}
}
// Fetch and copy details
val newManga = source.fetchMangaDetails(manga).toBlocking().first()
val newManga = source.fetchMangaDetails(manga).awaitSingle()
manga.copyFrom(newManga)
manga.initialized = true
@ -108,7 +110,7 @@ class GalleryAdder {
manga.date_added = Date().time
}
db.insertManga(manga).executeAsBlocking()
db.insertManga(manga).await()
// Fetch and copy chapters
try {
@ -119,7 +121,7 @@ class GalleryAdder {
}
chapterListObs.map {
syncChaptersWithSource(db, it, manga, source)
}.toBlocking().first()
}.awaitSingle()
} catch (e: Exception) {
XLog.w(context.getString(R.string.gallery_adder_chapter_fetch_error, manga.title), e)
return GalleryAddEvent.Fail.Error(url, context.getString(R.string.gallery_adder_chapter_fetch_error, url))

View File

@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.util.lang.launchUI
@ -21,22 +22,28 @@ import exh.GalleryAddEvent
import exh.GalleryAdder
import exh.eh.EHentaiThrottleManager
import exh.eh.EHentaiUpdateWorker
import exh.util.await
import exh.util.ignore
import exh.util.trans
import exh.util.wifiManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
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()
private val prefs: PreferencesHelper by injectLazy()
private val scope = CoroutineScope(Job() + Dispatchers.Main)
private val exh by lazy {
Injekt.get<SourceManager>().get(EXH_SOURCE_ID) as? EHentai
?: EHentai(0, true, context)
@ -53,7 +60,7 @@ class FavoritesSyncHelper(val context: Context) {
private val logger = XLog.tag("EHFavSync").build()
val status: BehaviorSubject<FavoritesSyncStatus> = BehaviorSubject.create<FavoritesSyncStatus>(FavoritesSyncStatus.Idle(context))
val status: BehaviorSubject<FavoritesSyncStatus> = BehaviorSubject.create(FavoritesSyncStatus.Idle(context))
@Synchronized
fun runSync() {
@ -63,10 +70,10 @@ class FavoritesSyncHelper(val context: Context) {
status.onNext(FavoritesSyncStatus.Initializing(context))
thread { beginSync() }
scope.launch(Dispatchers.IO) { beginSync() }
}
private fun beginSync() {
private suspend fun beginSync() {
// Check if logged in
if (!prefs.enableExhentai().get()) {
status.onNext(FavoritesSyncStatus.Error(context.getString(R.string.please_login)))
@ -75,13 +82,13 @@ class FavoritesSyncHelper(val context: Context) {
// Validate library state
status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context))
val libraryManga = db.getLibraryMangas().executeAsBlocking()
val libraryManga = db.getLibraryMangas().await()
val seenManga = HashSet<Long>(libraryManga.size)
libraryManga.forEach {
if (it.source != EXH_SOURCE_ID && it.source != EH_SOURCE_ID) return@forEach
if (it.id in seenManga) {
val inCategories = db.getCategoriesForManga(it).executeAsBlocking()
val inCategories = db.getCategoriesForManga(it).await()
status.onNext(
FavoritesSyncStatus.BadLibraryState
.MangaInMultipleCategories(it, inCategories, context)
@ -153,9 +160,8 @@ class FavoritesSyncHelper(val context: Context) {
}
}
val theContext = context
launchUI {
theContext.toast(context.getString(R.string.favorites_sync_complete))
context.toast(context.getString(R.string.favorites_sync_complete))
}
} catch (e: IgnoredException) {
// Do not display error as this error has already been reported
@ -187,8 +193,8 @@ class FavoritesSyncHelper(val context: Context) {
}
}
private fun applyRemoteCategories(categories: List<String>) {
val localCategories = db.getCategories().executeAsBlocking()
private suspend fun applyRemoteCategories(categories: List<String>) {
val localCategories = db.getCategories().await()
val newLocalCategories = localCategories.toMutableList()
@ -226,11 +232,11 @@ class FavoritesSyncHelper(val context: Context) {
// Only insert categories if changed
if (changed) {
db.insertCategories(newLocalCategories).executeAsBlocking()
db.insertCategories(newLocalCategories).await()
}
}
private fun addGalleryRemote(errorList: MutableList<String>, gallery: FavoriteEntry) {
private suspend fun addGalleryRemote(errorList: MutableList<String>, gallery: FavoriteEntry) {
val url = "${exh.baseUrl}/gallerypopups.php?gid=${gallery.gid}&t=${gallery.token}&act=addfav"
val request = Request.Builder()
@ -257,12 +263,12 @@ class FavoritesSyncHelper(val context: Context) {
}
}
private fun explicitlyRetryExhRequest(retryCount: Int, request: Request): Boolean {
private suspend fun explicitlyRetryExhRequest(retryCount: Int, request: Request): Boolean {
var success = false
for (i in 1..retryCount) {
try {
val resp = exh.client.newCall(request).execute()
val resp = exh.client.newCall(request).await()
if (resp.isSuccessful) {
success = true
@ -276,7 +282,7 @@ class FavoritesSyncHelper(val context: Context) {
return success
}
private fun applyChangeSetToRemote(errorList: MutableList<String>, changeSet: ChangeSet) {
private suspend fun applyChangeSetToRemote(errorList: MutableList<String>, changeSet: ChangeSet) {
// Apply removals
if (changeSet.removed.isNotEmpty()) {
status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_removing_galleries, changeSet.removed.size), context = context))
@ -324,7 +330,7 @@ class FavoritesSyncHelper(val context: Context) {
}
}
private fun applyChangeSetToLocal(errorList: MutableList<String>, changeSet: ChangeSet) {
private suspend fun applyChangeSetToLocal(errorList: MutableList<String>, changeSet: ChangeSet) {
val removedManga = mutableListOf<Manga>()
// Apply removals
@ -337,12 +343,12 @@ class FavoritesSyncHelper(val context: Context) {
db.getManga(url, EXH_SOURCE_ID),
db.getManga(url, EH_SOURCE_ID)
).forEach {
val manga = it.executeAsBlocking()
val manga = it.await()
if (manga?.favorite == true) {
manga.favorite = false
manga.date_added = 0
db.updateMangaFavorite(manga).executeAsBlocking()
db.updateMangaFavorite(manga).await()
removedManga += manga
}
}
@ -350,11 +356,11 @@ class FavoritesSyncHelper(val context: Context) {
// Can't do too many DB OPs in one go
removedManga.chunked(10).forEach {
db.deleteOldMangasCategories(it).executeAsBlocking()
db.deleteOldMangasCategories(it).await()
}
val insertedMangaCategories = mutableListOf<Pair<MangaCategory, Manga>>()
val categories = db.getCategories().executeAsBlocking()
val categories = db.getCategories().await()
// Apply additions
throttleManager.resetThrottle()

View File

@ -9,22 +9,27 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.toast
import kotlin.concurrent.thread
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class ConfiguringDialogController : DialogController() {
private var materialDialog: MaterialDialog? = null
val scope = CoroutineScope(Job() + Dispatchers.Main)
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
if (savedViewState == null) {
thread {
scope.launch(Dispatchers.IO) {
try {
EHConfigurator(activity!!).configureAll()
launchUI {
activity?.toast(activity?.getString(R.string.eh_settings_successfully_uploaded))
}
} catch (e: Exception) {
activity?.let {
it.runOnUiThread {
withContext(Dispatchers.Main) {
activity?.let {
MaterialDialog(it)
.title(R.string.eh_settings_configuration_failed)
.message(text = it.getString(R.string.eh_settings_configuration_failed_message, e.message))

View File

@ -4,6 +4,7 @@ import android.content.Context
import com.elvishew.xlog.XLog
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.util.asJsoup
@ -26,7 +27,7 @@ class EHConfigurator(val context: Context) {
private fun EHentai.requestWithCreds(sp: Int = 1) = Request.Builder()
.addHeader("Cookie", cookiesHeader(sp))
private fun EHentai.execProfileActions(
private suspend fun EHentai.execProfileActions(
action: String,
name: String,
set: String,
@ -44,11 +45,11 @@ class EHConfigurator(val context: Context) {
)
.build()
)
.execute()
.await()
private val EHentai.uconfigUrl get() = baseUrl + UCONFIG_URL
fun configureAll() {
suspend fun configureAll() {
val ehSource = sources.get(EH_SOURCE_ID) as EHentai
val exhSource = sources.get(EXH_SOURCE_ID) as EHentai
@ -58,7 +59,7 @@ class EHConfigurator(val context: Context) {
.url(HATH_PERKS_URL)
.build()
)
.execute().asJsoup()
.await().asJsoup()
val hathPerks = EHHathPerksResponse()
@ -85,10 +86,10 @@ class EHConfigurator(val context: Context) {
configure(exhSource, hathPerks)
}
private fun configure(source: EHentai, hathPerks: EHHathPerksResponse) {
private suspend fun configure(source: EHentai, hathPerks: EHHathPerksResponse) {
// Delete old app profiles
val scanReq = source.requestWithCreds().url(source.uconfigUrl).build()
val resp = configuratorClient.newCall(scanReq).execute().asJsoup()
val resp = configuratorClient.newCall(scanReq).await().asJsoup()
var lastDoc = resp
resp.select(PROFILE_SELECTOR).forEach {
if (it.text() == PROFILE_NAME) {
@ -127,7 +128,7 @@ class EHConfigurator(val context: Context) {
.url(source.uconfigUrl)
.post(form)
.build()
).execute()
).await()
// Persist slot + sk
source.spPref().set(slot)

View File

@ -1,6 +1,7 @@
package exh.ui.batchadd
import android.content.Context
import com.elvishew.xlog.XLog
import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.ReplayRelay
import eu.kanade.tachiyomi.R
@ -9,13 +10,20 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import exh.GalleryAddEvent
import exh.GalleryAdder
import exh.util.trimOrNull
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.concurrent.thread
class BatchAddPresenter : BasePresenter<BatchAddController>() {
private val galleryAdder by lazy { GalleryAdder() }
private val scope = CoroutineScope(Job() + Dispatchers.Main)
val progressTotalRelay = BehaviorRelay.create(0)!!
val progressRelay = BehaviorRelay.create(0)!!
@ -50,12 +58,17 @@ class BatchAddPresenter : BasePresenter<BatchAddController>() {
currentlyAddingRelay.call(STATE_INPUT_TO_PROGRESS)
thread {
val handler = CoroutineExceptionHandler { _, throwable ->
XLog.e(throwable)
}
scope.launch(Dispatchers.IO + handler) {
val succeeded = mutableListOf<String>()
val failed = mutableListOf<String>()
splitGalleries.forEachIndexed { i, s ->
val result = galleryAdder.addGallery(context, s, true)
ensureActive()
val result = withContext(Dispatchers.IO) { galleryAdder.addGallery(context, s, true) }
if (result is GalleryAddEvent.Success) {
succeeded.add(s)
} else {

View File

@ -25,6 +25,9 @@ import exh.source.DelegatedHttpSource
import exh.util.melt
import kotlinx.android.synthetic.main.eh_activity_captcha.toolbar
import kotlinx.android.synthetic.main.eh_activity_captcha.webview
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
@ -179,13 +182,13 @@ class BrowserActionActivity : AppCompatActivity() {
return true
}
fun captchaSolveFail() {
suspend fun captchaSolveFail() {
currentLoopId = null
validateCurrentLoopId = null
XLog.e(IllegalStateException("Captcha solve failure!"))
runOnUiThread {
withContext(Dispatchers.Main) {
webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE, null)
MaterialDialog(this)
MaterialDialog(this@BrowserActionActivity)
.title(R.string.captcha_solve_failure)
.message(R.string.captcha_solve_failure_message)
.cancelable(true)
@ -196,7 +199,7 @@ class BrowserActionActivity : AppCompatActivity() {
}
@JavascriptInterface
fun callback(result: String?, loopId: String, stage: Int) {
suspend fun callback(result: String?, loopId: String, stage: Int) {
if (loopId != currentLoopId) return
when (stage) {
@ -259,7 +262,7 @@ class BrowserActionActivity : AppCompatActivity() {
}
},
{
captchaSolveFail()
runBlocking { captchaSolveFail() }
}
)
} else {
@ -456,7 +459,7 @@ class BrowserActionActivity : AppCompatActivity() {
}
@JavascriptInterface
fun validateCaptchaCallback(result: Boolean, loopId: String) {
suspend fun validateCaptchaCallback(result: Boolean, loopId: String) {
if (loopId != validateCurrentLoopId) return
if (result) {

View File

@ -16,10 +16,11 @@ import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.manga.MangaController
import exh.GalleryAddEvent
import exh.GalleryAdder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import rx.subjects.BehaviorSubject
import kotlin.concurrent.thread
class InterceptActivity : BaseActivity<EhActivityInterceptBinding>() {
private var statusSubscription: Subscription? = null
@ -119,14 +120,14 @@ class InterceptActivity : BaseActivity<EhActivityInterceptBinding>() {
@Synchronized
private fun loadGalleryEnd(gallery: String, source: UrlImportableSource? = null) {
// Load gallery async
thread {
val result = galleryAdder.addGallery(this, gallery, forceSource = source)
scope.launch(Dispatchers.IO) {
val result = galleryAdder.addGallery(this@InterceptActivity, gallery, forceSource = source)
status.onNext(
when (result) {
is GalleryAddEvent.Success -> result.manga.id?.let {
InterceptResult.Success(it)
} ?: InterceptResult.Failure(this.getString(R.string.manga_id_is_null))
} ?: InterceptResult.Failure(this@InterceptActivity.getString(R.string.manga_id_is_null))
is GalleryAddEvent.Fail -> InterceptResult.Failure(result.logMessage)
}
)

View File

@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import exh.GalleryAddEvent
import exh.GalleryAdder
import kotlinx.coroutines.flow.flow
import rx.Observable
private val galleryAdder by lazy {
@ -17,19 +18,24 @@ private val galleryAdder by lazy {
fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
when {
query.startsWith("http://") || query.startsWith("https://") -> {
Observable.fromCallable {
val res = galleryAdder.addGallery(context, query, false, this)
MangasPage(
(
if (res is GalleryAddEvent.Success) {
listOf(res.manga)
} else {
emptyList()
}
),
false
flow {
emit(
galleryAdder.addGallery(context, query, false, this@urlImportFetchSearchManga)
)
}
.asObservable()
.map { res ->
MangasPage(
(
if (res is GalleryAddEvent.Success) {
listOf(res.manga)
} else {
emptyList()
}
),
false
)
}
}
else -> fail()
}