From 7c6478fe6b5f7a4a19042548029d099cd4b5ade6 Mon Sep 17 00:00:00 2001 From: FlaminSarge Date: Mon, 29 Apr 2019 09:38:59 -0700 Subject: [PATCH 01/21] Force Migration to display titles from source rather than from local DB, and update local titles when migrated (#1670) --- .../data/database/queries/MangaQueries.kt | 5 +++ .../resolvers/MangaTitlePutResolver.kt | 32 +++++++++++++++++++ .../resolvers/MangaViewerPutResolver.kt | 8 ++--- .../global_search/CatalogueSearchPresenter.kt | 2 +- .../ui/migration/MigrationPresenter.kt | 3 ++ .../tachiyomi/ui/migration/SearchPresenter.kt | 8 +++++ 6 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaTitlePutResolver.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt index a27a11c34..a64a09792 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt @@ -82,6 +82,11 @@ interface MangaQueries : DbProvider { .withPutResolver(MangaViewerPutResolver()) .prepare() + fun updateMangaTitle(manga: Manga) = db.put() + .`object`(manga) + .withPutResolver(MangaTitlePutResolver()) + .prepare() + fun deleteManga(manga: Manga) = db.delete().`object`(manga).prepare() fun deleteMangas(mangas: List) = db.delete().objects(mangas).prepare() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaTitlePutResolver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaTitlePutResolver.kt new file mode 100644 index 000000000..702173afb --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaTitlePutResolver.kt @@ -0,0 +1,32 @@ +package eu.kanade.tachiyomi.data.database.resolvers + +import android.content.ContentValues +import com.pushtorefresh.storio.sqlite.StorIOSQLite +import com.pushtorefresh.storio.sqlite.operations.put.PutResolver +import com.pushtorefresh.storio.sqlite.operations.put.PutResult +import com.pushtorefresh.storio.sqlite.queries.UpdateQuery +import eu.kanade.tachiyomi.data.database.inTransactionReturn +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.database.tables.MangaTable + +class MangaTitlePutResolver : PutResolver() { + + override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn { + val updateQuery = mapToUpdateQuery(manga) + val contentValues = mapToContentValues(manga) + + val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues) + PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table()) + } + + fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder() + .table(MangaTable.TABLE) + .where("${MangaTable.COL_ID} = ?") + .whereArgs(manga.id) + .build() + + fun mapToContentValues(manga: Manga) = ContentValues(1).apply { + put(MangaTable.COL_TITLE, manga.title) + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaViewerPutResolver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaViewerPutResolver.kt index 86c67f346..e40f397a8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaViewerPutResolver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaViewerPutResolver.kt @@ -20,10 +20,10 @@ class MangaViewerPutResolver : PutResolver() { } fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder() - .table(MangaTable.TABLE) - .where("${MangaTable.COL_ID} = ?") - .whereArgs(manga.id) - .build() + .table(MangaTable.TABLE) + .where("${MangaTable.COL_ID} = ?") + .whereArgs(manga.id) + .build() fun mapToContentValues(manga: Manga) = ContentValues(1).apply { put(MangaTable.COL_VIEWER, manga.viewer) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchPresenter.kt index b0cc97a62..39bcfc1c6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchPresenter.kt @@ -239,7 +239,7 @@ open class CatalogueSearchPresenter( * @param sManga the manga from the source. * @return a manga from the database. */ - private fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga { + protected open fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga { var localManga = db.getManga(sManga.url, sourceId).executeAsBlocking() if (localManga == null) { val newManga = Manga.create(sManga.url, sManga.title, sourceId) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationPresenter.kt index 712006367..35b6bc06b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationPresenter.kt @@ -146,6 +146,9 @@ class MigrationPresenter( } manga.favorite = true db.updateMangaFavorite(manga).executeAsBlocking() + + // SearchPresenter#networkToLocalManga may have updated the manga title, so ensure db gets updated title + db.updateMangaTitle(manga).executeAsBlocking() } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SearchPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SearchPresenter.kt index 2a7f218b8..b4a31c0da 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SearchPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SearchPresenter.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.migration import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.source.CatalogueSource +import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchCardItem import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchItem import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchPresenter @@ -21,4 +22,11 @@ class SearchPresenter( //Set the catalogue search item as highlighted if the source matches that of the selected manga return CatalogueSearchItem(source, results, source.id == manga.source) } + + override fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga { + val localManga = super.networkToLocalManga(sManga, sourceId) + // For migration, displayed title should always match source rather than local DB + localManga.title = sManga.title + return localManga + } } From 5840a3e1e2388c3f2f77ef22f6620282e13186b9 Mon Sep 17 00:00:00 2001 From: Pavka Date: Mon, 29 Apr 2019 19:40:26 +0300 Subject: [PATCH 02/21] Shikomori -> Shikimori. Fix update chapters (#1996) * Shikomori -> Shikimori. Fix update chapters * Removed logs and format code --- app/src/main/AndroidManifest.xml | 4 +- .../tachiyomi/data/track/TrackManager.kt | 8 +-- .../track/{shikomori => shikimori}/OAuth.kt | 2 +- .../Shikomori.kt => shikimori/Shikimori.kt} | 13 ++-- .../ShikimoriApi.kt} | 56 +++++++++++------- .../ShikimoriInterceptor.kt} | 12 ++-- .../data/track/shikimori/ShikimoriModels.kt | 24 ++++++++ .../data/track/shikomori/ShikomoriModels.kt | 24 -------- .../ui/setting/SettingsTrackingController.kt | 8 +-- .../ui/setting/ShikomoriLoginActivity.kt | 6 +- .../{shikomori.png => shikimori.png} | Bin 11 files changed, 87 insertions(+), 70 deletions(-) rename app/src/main/java/eu/kanade/tachiyomi/data/track/{shikomori => shikimori}/OAuth.kt (83%) rename app/src/main/java/eu/kanade/tachiyomi/data/track/{shikomori/Shikomori.kt => shikimori/Shikimori.kt} (89%) rename app/src/main/java/eu/kanade/tachiyomi/data/track/{shikomori/ShikomoriApi.kt => shikimori/ShikimoriApi.kt} (77%) rename app/src/main/java/eu/kanade/tachiyomi/data/track/{shikomori/ShikomoriInterceptor.kt => shikimori/ShikimoriInterceptor.kt} (79%) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriModels.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/ShikomoriModels.kt rename app/src/main/res/drawable-xxxhdpi/{shikomori.png => shikimori.png} (100%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index feddf2096..eff4c7057 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -62,8 +62,8 @@ + android:name=".ui.setting.ShikimoriLoginActivity" + android:label="Shikimori"> diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackManager.kt index 14558d1f1..70d669af5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackManager.kt @@ -4,7 +4,7 @@ import android.content.Context import eu.kanade.tachiyomi.data.track.anilist.Anilist import eu.kanade.tachiyomi.data.track.kitsu.Kitsu import eu.kanade.tachiyomi.data.track.myanimelist.Myanimelist -import eu.kanade.tachiyomi.data.track.shikomori.Shikomori +import eu.kanade.tachiyomi.data.track.shikimori.Shikimori class TrackManager(private val context: Context) { @@ -12,7 +12,7 @@ class TrackManager(private val context: Context) { const val MYANIMELIST = 1 const val ANILIST = 2 const val KITSU = 3 - const val SHIKOMORI = 4 + const val SHIKIMORI = 4 } val myAnimeList = Myanimelist(context, MYANIMELIST) @@ -21,9 +21,9 @@ class TrackManager(private val context: Context) { val kitsu = Kitsu(context, KITSU) - val shikomori = Shikomori(context, SHIKOMORI) + val shikimori = Shikimori(context, SHIKIMORI) - val services = listOf(myAnimeList, aniList, kitsu, shikomori) + val services = listOf(myAnimeList, aniList, kitsu, shikimori) fun getService(id: Int) = services.find { it.id == id } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/OAuth.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/OAuth.kt similarity index 83% rename from app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/OAuth.kt rename to app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/OAuth.kt index ad6adc18a..118e584e7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/OAuth.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/OAuth.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.data.track.shikomori +package eu.kanade.tachiyomi.data.track.shikimori data class OAuth( val access_token: String, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/Shikomori.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt similarity index 89% rename from app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/Shikomori.kt rename to app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt index 83fee74cf..8068e6d55 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/Shikomori.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt @@ -1,7 +1,8 @@ -package eu.kanade.tachiyomi.data.track.shikomori +package eu.kanade.tachiyomi.data.track.shikimori import android.content.Context import android.graphics.Color +import android.util.Log import com.google.gson.Gson import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Track @@ -11,7 +12,7 @@ import rx.Completable import rx.Observable import uy.kohesive.injekt.injectLazy -class Shikomori(private val context: Context, id: Int) : TrackService(id) { +class Shikimori(private val context: Context, id: Int) : TrackService(id) { override fun getScoreList(): List { return IntRange(0, 10).map(Int::toString) @@ -75,15 +76,15 @@ class Shikomori(private val context: Context, id: Int) : TrackService(id) { const val DEFAULT_SCORE = 0 } - override val name = "Shikomori" + override val name = "Shikimori" private val gson: Gson by injectLazy() - private val interceptor by lazy { ShikomoriInterceptor(this, gson) } + private val interceptor by lazy { ShikimoriInterceptor(this, gson) } - private val api by lazy { ShikomoriApi(client, interceptor) } + private val api by lazy { ShikimoriApi(client, interceptor) } - override fun getLogo() = R.drawable.shikomori + override fun getLogo() = R.drawable.shikimori override fun getLogoColor() = Color.rgb(40, 40, 40) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/ShikomoriApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt similarity index 77% rename from app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/ShikomoriApi.kt rename to app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt index 2df1eae63..35dbb532b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/ShikomoriApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.data.track.shikomori +package eu.kanade.tachiyomi.data.track.shikimori import android.net.Uri import com.github.salomonbrys.kotson.array @@ -18,7 +18,7 @@ import okhttp3.* import rx.Observable import uy.kohesive.injekt.injectLazy -class ShikomoriApi(private val client: OkHttpClient, interceptor: ShikomoriInterceptor) { +class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInterceptor) { private val gson: Gson by injectLazy() private val parser = JsonParser() @@ -33,7 +33,7 @@ class ShikomoriApi(private val client: OkHttpClient, interceptor: ShikomoriInter "target_type" to "Manga", "chapters" to track.last_chapter_read, "score" to track.score.toInt(), - "status" to track.toShikomoriStatus() + "status" to track.toShikimoriStatus() ) ) val body = RequestBody.create(jsonime, payload.toString()) @@ -74,7 +74,7 @@ class ShikomoriApi(private val client: OkHttpClient, interceptor: ShikomoriInter } private fun jsonToSearch(obj: JsonObject): TrackSearch { - return TrackSearch.create(TrackManager.SHIKOMORI).apply { + return TrackSearch.create(TrackManager.SHIKIMORI).apply { media_id = obj["id"].asInt title = obj["name"].asString total_chapters = obj["chapters"].asInt @@ -87,14 +87,15 @@ class ShikomoriApi(private val client: OkHttpClient, interceptor: ShikomoriInter } } - private fun jsonToTrack(obj: JsonObject): Track { - return Track.create(TrackManager.SHIKOMORI).apply { + private fun jsonToTrack(obj: JsonObject, mangas: JsonObject): Track { + return Track.create(TrackManager.SHIKIMORI).apply { + title = mangas["name"].asString media_id = obj["id"].asInt - title = "" + total_chapters = mangas["chapters"].asInt last_chapter_read = obj["chapters"].asInt - total_chapters = obj["chapters"].asInt score = (obj["score"].asInt).toFloat() status = toTrackStatus(obj["status"].asString) + tracking_url = baseUrl + mangas["url"].asString } } @@ -108,21 +109,36 @@ class ShikomoriApi(private val client: OkHttpClient, interceptor: ShikomoriInter .url(url.toString()) .get() .build() - return authClient.newCall(request) + + val urlMangas = Uri.parse("$apiUrl/mangas").buildUpon() + .appendPath(track.media_id.toString()) + .build() + val requestMangas = Request.Builder() + .url(urlMangas.toString()) + .get() + .build() + return authClient.newCall(requestMangas) .asObservableSuccess() .map { netResponse -> val responseBody = netResponse.body()?.string().orEmpty() - if (responseBody.isEmpty()) { - throw Exception("Null Response") - } - val response = parser.parse(responseBody).array - if (response.size() > 1) { - throw Exception("Too much mangas in response") - } - val entry = response.map { - jsonToTrack(it.obj) - } - entry.firstOrNull() + parser.parse(responseBody).obj + }.flatMap { mangas -> + authClient.newCall(request) + .asObservableSuccess() + .map { netResponse -> + val responseBody = netResponse.body()?.string().orEmpty() + if (responseBody.isEmpty()) { + throw Exception("Null Response") + } + val response = parser.parse(responseBody).array + if (response.size() > 1) { + throw Exception("Too much mangas in response") + } + val entry = response.map { + jsonToTrack(it.obj, mangas) + } + entry.firstOrNull() + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/ShikomoriInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriInterceptor.kt similarity index 79% rename from app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/ShikomoriInterceptor.kt rename to app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriInterceptor.kt index e46e7cfb4..154020727 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/ShikomoriInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriInterceptor.kt @@ -1,26 +1,26 @@ -package eu.kanade.tachiyomi.data.track.shikomori +package eu.kanade.tachiyomi.data.track.shikimori import com.google.gson.Gson import okhttp3.Interceptor import okhttp3.Response -class ShikomoriInterceptor(val shikomori: Shikomori, val gson: Gson) : Interceptor { +class ShikimoriInterceptor(val shikimori: Shikimori, val gson: Gson) : Interceptor { /** * OAuth object used for authenticated requests. */ - private var oauth: OAuth? = shikomori.restoreToken() + private var oauth: OAuth? = shikimori.restoreToken() override fun intercept(chain: Interceptor.Chain): Response { val originalRequest = chain.request() - val currAuth = oauth ?: throw Exception("Not authenticated with Shikomori") + val currAuth = oauth ?: throw Exception("Not authenticated with Shikimori") val refreshToken = currAuth.refresh_token!! // Refresh access token if expired. if (currAuth.isExpired()) { - val response = chain.proceed(ShikomoriApi.refreshTokenRequest(refreshToken)) + val response = chain.proceed(ShikimoriApi.refreshTokenRequest(refreshToken)) if (response.isSuccessful) { newAuth(gson.fromJson(response.body()!!.string(), OAuth::class.java)) } else { @@ -38,6 +38,6 @@ class ShikomoriInterceptor(val shikomori: Shikomori, val gson: Gson) : Intercept fun newAuth(oauth: OAuth?) { this.oauth = oauth - shikomori.saveToken(oauth) + shikimori.saveToken(oauth) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriModels.kt new file mode 100644 index 000000000..91e556bdd --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriModels.kt @@ -0,0 +1,24 @@ +package eu.kanade.tachiyomi.data.track.shikimori + +import eu.kanade.tachiyomi.data.database.models.Track + +fun Track.toShikimoriStatus() = when (status) { + Shikimori.READING -> "watching" + Shikimori.COMPLETED -> "completed" + Shikimori.ON_HOLD -> "on_hold" + Shikimori.DROPPED -> "dropped" + Shikimori.PLANNING -> "planned" + Shikimori.REPEATING -> "rewatching" + else -> throw NotImplementedError("Unknown status") +} + +fun toTrackStatus(status: String) = when (status) { + "watching" -> Shikimori.READING + "completed" -> Shikimori.COMPLETED + "on_hold" -> Shikimori.ON_HOLD + "dropped" -> Shikimori.DROPPED + "planned" -> Shikimori.PLANNING + "rewatching" -> Shikimori.REPEATING + + else -> throw Exception("Unknown status") +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/ShikomoriModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/ShikomoriModels.kt deleted file mode 100644 index d66f20649..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikomori/ShikomoriModels.kt +++ /dev/null @@ -1,24 +0,0 @@ -package eu.kanade.tachiyomi.data.track.shikomori - -import eu.kanade.tachiyomi.data.database.models.Track - -fun Track.toShikomoriStatus() = when (status) { - Shikomori.READING -> "watching" - Shikomori.COMPLETED -> "completed" - Shikomori.ON_HOLD -> "on_hold" - Shikomori.DROPPED -> "dropped" - Shikomori.PLANNING -> "planned" - Shikomori.REPEATING -> "rewatching" - else -> throw NotImplementedError("Unknown status") -} - -fun toTrackStatus(status: String) = when (status) { - "watching" -> Shikomori.READING - "completed" -> Shikomori.COMPLETED - "on_hold" -> Shikomori.ON_HOLD - "dropped" -> Shikomori.DROPPED - "planned" -> Shikomori.PLANNING - "rewatching" -> Shikomori.REPEATING - - else -> throw Exception("Unknown status") -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt index 250289cc1..2a8f1f73a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt @@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.anilist.AnilistApi -import eu.kanade.tachiyomi.data.track.shikomori.ShikomoriApi +import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi import eu.kanade.tachiyomi.util.getResourceColor import eu.kanade.tachiyomi.widget.preference.LoginPreference import eu.kanade.tachiyomi.widget.preference.TrackLoginDialog @@ -54,13 +54,13 @@ class SettingsTrackingController : SettingsController(), dialog.showDialog(router) } } - trackPreference(trackManager.shikomori) { + trackPreference(trackManager.shikimori) { onClick { val tabsIntent = CustomTabsIntent.Builder() .setToolbarColor(context.getResourceColor(R.attr.colorPrimary)) .build() tabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) - tabsIntent.launchUrl(activity, ShikomoriApi.authUrl()) + tabsIntent.launchUrl(activity, ShikimoriApi.authUrl()) } } } @@ -80,7 +80,7 @@ class SettingsTrackingController : SettingsController(), super.onActivityResumed(activity) // Manually refresh anilist holder updatePreference(trackManager.aniList.id) - updatePreference(trackManager.shikomori.id) + updatePreference(trackManager.shikimori.id) } private fun updatePreference(id: Int) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/ShikomoriLoginActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/ShikomoriLoginActivity.kt index 6c3ba6f83..d369896ed 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/ShikomoriLoginActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/ShikomoriLoginActivity.kt @@ -13,7 +13,7 @@ import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers import uy.kohesive.injekt.injectLazy -class ShikomoriLoginActivity : AppCompatActivity() { +class ShikimoriLoginActivity : AppCompatActivity() { private val trackManager: TrackManager by injectLazy() @@ -25,7 +25,7 @@ class ShikomoriLoginActivity : AppCompatActivity() { val code = intent.data?.getQueryParameter("code") if (code != null) { - trackManager.shikomori.login(code) + trackManager.shikimori.login(code) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ @@ -34,7 +34,7 @@ class ShikomoriLoginActivity : AppCompatActivity() { returnToSettings() }) } else { - trackManager.shikomori.logout() + trackManager.shikimori.logout() returnToSettings() } } diff --git a/app/src/main/res/drawable-xxxhdpi/shikomori.png b/app/src/main/res/drawable-xxxhdpi/shikimori.png similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/shikomori.png rename to app/src/main/res/drawable-xxxhdpi/shikimori.png From 021dde66ebb854f54de7b461c8e6783b43c5198f Mon Sep 17 00:00:00 2001 From: Deumiankio <39210184+Deumiankio@users.noreply.github.com> Date: Mon, 29 Apr 2019 19:32:49 +0200 Subject: [PATCH 03/21] Add color filter blend modes (#2013) * Add color filter blend modes * Only show modes supported by currently used API level. * Fix arrays.xml for API level <=27. --- .../data/preference/PreferenceKeys.kt | 2 ++ .../data/preference/PreferencesHelper.kt | 2 ++ .../tachiyomi/ui/reader/ReaderActivity.kt | 5 ++- .../ui/reader/ReaderColorFilterSheet.kt | 11 +++++- .../ui/reader/ReaderColorFilterView.kt | 32 +++++++++++++++++ .../layout-land/reader_color_filter_sheet.xml | 2 +- app/src/main/res/layout/reader_activity.xml | 2 +- .../main/res/layout/reader_color_filter.xml | 36 ++++++++++++++++++- .../res/layout/reader_color_filter_sheet.xml | 2 +- app/src/main/res/values-v28/arrays.xml | 15 ++++++++ app/src/main/res/values/arrays.xml | 6 ++++ app/src/main/res/values/strings.xml | 7 ++++ 12 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderColorFilterView.kt create mode 100644 app/src/main/res/values-v28/arrays.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index 1b2e6bb32..eb5f9d4a1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -29,6 +29,8 @@ object PreferenceKeys { const val colorFilterValue = "color_filter_value" + const val colorFilterMode = "color_filter_mode" + const val defaultViewer = "pref_default_viewer_key" const val imageScaleType = "pref_image_scale_type_key" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 58ad2b0af..6545b4a39 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -57,6 +57,8 @@ class PreferencesHelper(val context: Context) { fun colorFilterValue() = rxPrefs.getInteger(Keys.colorFilterValue, 0) + fun colorFilterMode() = rxPrefs.getInteger(Keys.colorFilterMode, 0) + fun defaultViewer() = prefs.getInt(Keys.defaultViewer, 1) fun imageScaleType() = rxPrefs.getInteger(Keys.imageScaleType, 1) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index c3b8aefd8..ee324e896 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -574,6 +574,9 @@ class ReaderActivity : BaseRxActivity() { subscriptions += preferences.colorFilter().asObservable() .subscribe { setColorFilter(it) } + + subscriptions += preferences.colorFilterMode().asObservable() + .subscribe { setColorFilter(preferences.colorFilter().getOrDefault()) } } /** @@ -722,7 +725,7 @@ class ReaderActivity : BaseRxActivity() { */ private fun setColorFilterValue(value: Int) { color_overlay.visibility = View.VISIBLE - color_overlay.setBackgroundColor(value) + color_overlay.setFilterColor(value, preferences.colorFilterMode().getOrDefault()) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderColorFilterSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderColorFilterSheet.kt index 263bd5de2..a0579cdcc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderColorFilterSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderColorFilterSheet.kt @@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.util.plusAssign +import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener import eu.kanade.tachiyomi.widget.SimpleSeekBarListener import kotlinx.android.synthetic.main.reader_color_filter.* import kotlinx.android.synthetic.main.reader_color_filter_sheet.* @@ -54,6 +55,9 @@ class ReaderColorFilterSheet(activity: ReaderActivity) : BottomSheetDialog(activ subscriptions += preferences.colorFilter().asObservable() .subscribe { setColorFilter(it, view) } + subscriptions += preferences.colorFilterMode().asObservable() + .subscribe { setColorFilter(preferences.colorFilter().getOrDefault(), view) } + subscriptions += preferences.customBrightness().asObservable() .subscribe { setCustomBrightness(it, view) } @@ -84,6 +88,11 @@ class ReaderColorFilterSheet(activity: ReaderActivity) : BottomSheetDialog(activ preferences.customBrightness().set(isChecked) } + color_filter_mode.onItemSelectedListener = IgnoreFirstSpinnerListener { position -> + preferences.colorFilterMode().set(position) + } + color_filter_mode.setSelection(preferences.colorFilterMode().getOrDefault(), false) + seekbar_color_filter_alpha.setOnSeekBarChangeListener(object : SimpleSeekBarListener() { override fun onProgressChanged(seekBar: SeekBar, value: Int, fromUser: Boolean) { if (fromUser) { @@ -248,7 +257,7 @@ class ReaderColorFilterSheet(activity: ReaderActivity) : BottomSheetDialog(activ */ private fun setColorFilterValue(@ColorInt color: Int, view: View) = with(view) { color_overlay.visibility = View.VISIBLE - color_overlay.setBackgroundColor(color) + color_overlay.setFilterColor(color, preferences.colorFilterMode().getOrDefault()) setValues(color, view) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderColorFilterView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderColorFilterView.kt new file mode 100644 index 000000000..01d91a3b4 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderColorFilterView.kt @@ -0,0 +1,32 @@ +package eu.kanade.tachiyomi.ui.reader + +import android.content.Context +import android.graphics.* +import android.util.AttributeSet +import android.view.View + +class ReaderColorFilterView( + context: Context, + attrs: AttributeSet? = null +) : View(context, attrs) { + + private val colorFilterPaint: Paint = Paint() + + fun setFilterColor(color: Int, filterMode: Int) { + colorFilterPaint.setColor(color) + colorFilterPaint.xfermode = PorterDuffXfermode(when (filterMode) { + 1 -> PorterDuff.Mode.MULTIPLY + 2 -> PorterDuff.Mode.SCREEN + 3 -> PorterDuff.Mode.OVERLAY + 4 -> PorterDuff.Mode.LIGHTEN + 5 -> PorterDuff.Mode.DARKEN + else -> PorterDuff.Mode.SRC_OVER + }) + invalidate() + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + canvas.drawPaint(colorFilterPaint) + } +} diff --git a/app/src/main/res/layout-land/reader_color_filter_sheet.xml b/app/src/main/res/layout-land/reader_color_filter_sheet.xml index ba4d45e40..761c27992 100644 --- a/app/src/main/res/layout-land/reader_color_filter_sheet.xml +++ b/app/src/main/res/layout-land/reader_color_filter_sheet.xml @@ -29,7 +29,7 @@ android:layout_height="wrap_content" android:visibility="gone" /> - - + + + + + + + + + app:layout_constraintTop_toBottomOf="@id/color_filter_mode_text"/> @@ -202,4 +229,11 @@ app:layout_constraintBottom_toBottomOf="@id/brightness_seekbar" app:layout_constraintRight_toRightOf="parent"/> + + diff --git a/app/src/main/res/layout/reader_color_filter_sheet.xml b/app/src/main/res/layout/reader_color_filter_sheet.xml index 618a8a77f..3155bc15c 100644 --- a/app/src/main/res/layout/reader_color_filter_sheet.xml +++ b/app/src/main/res/layout/reader_color_filter_sheet.xml @@ -21,7 +21,7 @@ android:layout_height="match_parent" android:visibility="gone" /> - + + + + @string/filter_mode_default + @string/filter_mode_multiply + @string/filter_mode_screen + + + @string/filter_mode_overlay + @string/filter_mode_lighten + @string/filter_mode_darken + + + diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 9018a31f7..76bd9430c 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -102,4 +102,10 @@ 2 + + @string/filter_mode_default + @string/filter_mode_multiply + @string/filter_mode_screen + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2388bdc1f..14ab046b1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -178,6 +178,13 @@ Crop borders Use custom brightness Use custom color filter + Color filter blend mode + Default + Overlay + Multiply + Screen + Dodge / Lighten + Burn / Darken Keep screen on Skip chapters marked read Navigation From 5c1770247c71901db2455cc2b079263e2718a0c3 Mon Sep 17 00:00:00 2001 From: DarKCroX Date: Fri, 3 May 2019 19:32:31 +0800 Subject: [PATCH 04/21] Update README.md mangafox has been broken for months --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b21282d6..294bb39ce 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Tachiyomi is a free and open source manga reader for Android. ## Features Features include: -* Online reading from sources such as KissManga, MangaFox, [and more](https://github.com/inorichi/tachiyomi-extensions) +* Online reading from sources such as KissManga, MangaDex, [and more](https://github.com/inorichi/tachiyomi-extensions) * Local reading of downloaded manga * Configurable reader with multiple viewers, reading directions and other settings * [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), and [Kitsu](https://kitsu.io/explore/anime) support From 003dca9d45ff122cb24f176b314389e57b4ad7f1 Mon Sep 17 00:00:00 2001 From: Pavka Date: Tue, 7 May 2019 12:06:38 +0300 Subject: [PATCH 05/21] Bugfix. Sharing images with very long name (#1999) * Fix sharing with very long images name * Fix dropLast to take --- .../main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index fbb2f4bbf..e24feb8e3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -434,7 +434,8 @@ class ReaderPresenter( // Build destination file. val filename = DiskUtil.buildValidFilename( - "${manga.title} - ${chapter.name}") + " - ${page.number}.${type.extension}" + "${manga.title} - ${chapter.name}".take(225) + ) + " - ${page.number}.${type.extension}" val destFile = File(directory, filename) stream().use { input -> From a0939e1c482e11bccb4a3e72e5aa510ea3ebfb31 Mon Sep 17 00:00:00 2001 From: BlueCat300 <42893969+BlueCat300@users.noreply.github.com> Date: Thu, 16 May 2019 19:21:54 +0300 Subject: [PATCH 06/21] Update Shikimori (#2038) Domain name change due to blocking by local authorities. --- .../kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt index 35dbb532b..0180e015e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt @@ -172,10 +172,10 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter private const val clientId = "1aaf4cf232372708e98b5abc813d795b539c5a916dbbfe9ac61bf02a360832cc" private const val clientSecret = "229942c742dd4cde803125d17d64501d91c0b12e14cb1e5120184d77d67024c0" - private const val baseUrl = "https://shikimori.org" - private const val apiUrl = "https://shikimori.org/api" - private const val oauthUrl = "https://shikimori.org/oauth/token" - private const val loginUrl = "https://shikimori.org/oauth/authorize" + private const val baseUrl = "https://shikimori.one" + private const val apiUrl = "https://shikimori.one/api" + private const val oauthUrl = "https://shikimori.one/oauth/token" + private const val loginUrl = "https://shikimori.one/oauth/authorize" private const val redirectUrl = "tachiyomi://shikimori-auth" private const val baseMangaUrl = "$apiUrl/mangas" From be3ed9b6af3f3207935191da4b72bcab323e501e Mon Sep 17 00:00:00 2001 From: inorichi Date: Fri, 24 May 2019 09:56:31 +0200 Subject: [PATCH 07/21] Create FUNDING.yml --- .github/FUNDING.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..410a50bd9 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: inorichi +ko_fi: inorichi From a32572fc96b3700e4c9b6781eb3a2e6c6de113b9 Mon Sep 17 00:00:00 2001 From: Harsh Parekh Date: Fri, 24 May 2019 03:57:05 -0400 Subject: [PATCH 08/21] Ignore case while sorting Library (#2048) * Ignore case while sorting Library * Simplify code As suggested by @arkon --- .../java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt index eb3999ffc..17ac0cba5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -185,7 +185,7 @@ class LibraryPresenter( val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 -> when (sortingMode) { - LibrarySort.ALPHA -> i1.manga.title.compareTo(i2.manga.title) + LibrarySort.ALPHA -> i1.manga.title.compareTo(i2.manga.title, true) LibrarySort.LAST_READ -> { // Get index of manga, set equal to list if size unknown. val manga1LastRead = lastReadManga[i1.manga.id!!] ?: lastReadManga.size From 15f225537ec1ea67939002b12f6ad61920f2709e Mon Sep 17 00:00:00 2001 From: Jannis Becker <31903525+moka491@users.noreply.github.com> Date: Sat, 25 May 2019 13:46:20 +0200 Subject: [PATCH 09/21] Update tracking sites after finishing chapter (#2044) * Added second updateTrackLastChapterRead() called whenever a chapter has been read in the reader * Removed old updateTrackLastChapterRead() so that it's not called twice. --- .../java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index e24feb8e3..b2aafa0a2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -147,10 +147,9 @@ class ReaderPresenter( /** * Called when the user pressed the back button and is going to leave the reader. Used to - * update tracking services and trigger deletion of the downloaded chapters. + * trigger deletion of the downloaded chapters. */ fun onBackPressed() { - updateTrackLastChapterRead() deletePendingChapters() } @@ -308,7 +307,7 @@ class ReaderPresenter( /** * Called every time a page changes on the reader. Used to mark the flag of chapters being - * read, enqueue downloaded chapter deletion, and updating the active chapter if this + * read, update tracking services, enqueue downloaded chapter deletion, and updating the active chapter if this * [page]'s chapter is different from the currently active. */ fun onPageSelected(page: ReaderPage) { @@ -320,6 +319,7 @@ class ReaderPresenter( selectedChapter.chapter.last_page_read = page.index if (selectedChapter.pages?.lastIndex == page.index) { selectedChapter.chapter.read = true + updateTrackLastChapterRead() enqueueDeleteReadChapters(selectedChapter) } From 974a24d03b27a6a09b99e650be6ca0211dd572f6 Mon Sep 17 00:00:00 2001 From: Eugene Date: Sat, 25 May 2019 07:46:42 -0400 Subject: [PATCH 10/21] Add help link to nav drawer (#2049) --- .../main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt | 6 ++++++ app/src/main/res/menu/menu_navigation.xml | 5 +++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 12 insertions(+) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 00acfa494..9fc4fa6cc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -24,6 +24,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController import eu.kanade.tachiyomi.ui.setting.SettingsMainController +import eu.kanade.tachiyomi.util.openInBrowser import kotlinx.android.synthetic.main.main_activity.* import uy.kohesive.injekt.injectLazy @@ -91,6 +92,9 @@ class MainActivity : BaseActivity() { R.id.nav_drawer_settings -> { router.pushController(SettingsMainController().withFadeTransaction()) } + R.id.nav_drawer_help -> { + openInBrowser(URL_HELP) + } } } drawer.closeDrawer(GravityCompat.START) @@ -271,6 +275,8 @@ class MainActivity : BaseActivity() { const val INTENT_SEARCH = "eu.kanade.tachiyomi.SEARCH" const val INTENT_SEARCH_QUERY = "query" const val INTENT_SEARCH_FILTER = "filter" + + private const val URL_HELP = "https://github.com/inorichi/tachiyomi/wiki" } } diff --git a/app/src/main/res/menu/menu_navigation.xml b/app/src/main/res/menu/menu_navigation.xml index 7703c6ef8..1ea7a6541 100644 --- a/app/src/main/res/menu/menu_navigation.xml +++ b/app/src/main/res/menu/menu_navigation.xml @@ -36,5 +36,10 @@ android:icon="@drawable/ic_settings_black_24dp" android:title="@string/label_settings" android:checkable="false" /> + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 14ab046b1..78b2350f8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -24,6 +24,7 @@ Source migration Extensions Extension info + Help From 47f14e8555f518eae6a81fcffe6876f40e536547 Mon Sep 17 00:00:00 2001 From: Harsh Parekh Date: Sat, 25 May 2019 07:47:53 -0400 Subject: [PATCH 11/21] Long click to manage categories (#2045) --- .../ui/manga/info/MangaInfoController.kt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt index b3bb48289..0a3a40973 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt @@ -88,6 +88,9 @@ class MangaInfoController : NucleusController(), // Set onclickListener to toggle favorite when FAB clicked. fab_favorite.clicks().subscribeUntilDestroy { onFabClick() } + // Set onLongClickListener to manage categories when FAB is clicked. + fab_favorite.longClicks().subscribeUntilDestroy{ onFabLongClick() } + // Set SwipeRefresh to refresh manga data. swipe_refresh.refreshes().subscribeUntilDestroy { fetchMangaFromSource() } @@ -400,6 +403,33 @@ class MangaInfoController : NucleusController(), } } + /** + * Called when the fab is long clicked. + */ + private fun onFabLongClick() { + val manga = presenter.manga + if (!manga.favorite) { + toggleFavorite() + activity?.toast(activity?.getString(R.string.manga_added_library)) + } + val categories = presenter.getCategories() + val defaultCategory = categories.find { it.id == preferences.defaultCategory() } + when { + defaultCategory != null -> presenter.moveMangaToCategory(manga, defaultCategory) + categories.size <= 1 -> // default or the one from the user + presenter.moveMangaToCategory(manga, categories.firstOrNull()) + else -> { + val ids = presenter.getMangaCategoryIds(manga) + val preselected = ids.mapNotNull { id -> + categories.indexOfFirst { it.id == id }.takeIf { it != -1 } + }.toTypedArray() + + ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected) + .showDialog(router) + } + } + } + override fun updateCategoriesForMangas(mangas: List, categories: List) { val manga = mangas.firstOrNull() ?: return presenter.moveMangaToCategories(manga, categories) From 8ebda219c4b45a24c83e7c40c8ab218ec2fd07f2 Mon Sep 17 00:00:00 2001 From: Harsh Parekh Date: Sun, 26 May 2019 05:37:47 -0400 Subject: [PATCH 12/21] Fix the category selection bug (#2052) Fixes #2051 --- .../ui/manga/info/MangaInfoController.kt | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt index 0a3a40973..534062707 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt @@ -413,20 +413,17 @@ class MangaInfoController : NucleusController(), activity?.toast(activity?.getString(R.string.manga_added_library)) } val categories = presenter.getCategories() - val defaultCategory = categories.find { it.id == preferences.defaultCategory() } - when { - defaultCategory != null -> presenter.moveMangaToCategory(manga, defaultCategory) - categories.size <= 1 -> // default or the one from the user - presenter.moveMangaToCategory(manga, categories.firstOrNull()) - else -> { - val ids = presenter.getMangaCategoryIds(manga) - val preselected = ids.mapNotNull { id -> - categories.indexOfFirst { it.id == id }.takeIf { it != -1 } - }.toTypedArray() + if (categories.size <= 1) { + // default or the one from the user then just add to favorite. + presenter.moveMangaToCategory(manga, categories.firstOrNull()) + } else { + val ids = presenter.getMangaCategoryIds(manga) + val preselected = ids.mapNotNull { id -> + categories.indexOfFirst { it.id == id }.takeIf { it != -1 } + }.toTypedArray() - ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected) - .showDialog(router) - } + ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected) + .showDialog(router) } } From 9ba7312cafe5f54f0a5a6747aa44634482ad9aae Mon Sep 17 00:00:00 2001 From: MCAxiaz Date: Sun, 9 Jun 2019 05:31:19 -0700 Subject: [PATCH 13/21] Make MAL Tracking Slightly Less Shitty (#2042) * * fix cookieManager not clearing cookies properly * manually clear tracking prefs when !isLogged (e.g. cookies were cleared) * use full url for removing cookies * add interceptor for all non-login network calls * attempt auto login if cookies are missing * move handling of csrf token to interceptor * * move methods around to improve readability * fix TrackSearchAdapter not updating other fields if cover_url is missing * revert accidental removal of feature in https://github.com/inorichi/tachiyomi/issues/65 * avoid login if credentials are missing * fix eol * *separate login flow from rxjava for reuse in sync * *use less expensive method of finding manga * *move variable declaration * formatting * set total chapters in remote track --- .../data/track/myanimelist/MyAnimeList.kt | 45 ++- .../myanimelist/MyAnimeListInterceptor.kt | 49 +++ .../data/track/myanimelist/MyanimelistApi.kt | 361 ++++++++++-------- .../tachiyomi/network/AndroidCookieJar.kt | 7 +- .../ui/manga/track/TrackSearchAdapter.kt | 36 +- 5 files changed, 296 insertions(+), 202 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt index a83e8b9ff..fbfc1e019 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt @@ -10,11 +10,11 @@ import eu.kanade.tachiyomi.data.track.model.TrackSearch import okhttp3.HttpUrl import rx.Completable import rx.Observable +import java.lang.Exception class Myanimelist(private val context: Context, id: Int) : TrackService(id) { companion object { - const val READING = 1 const val COMPLETED = 2 const val ON_HOLD = 3 @@ -29,7 +29,8 @@ class Myanimelist(private val context: Context, id: Int) : TrackService(id) { const val LOGGED_IN_COOKIE = "is_logged_in" } - private val api by lazy { MyanimelistApi(client) } + private val interceptor by lazy { MyAnimeListInterceptor(this) } + private val api by lazy { MyanimelistApi(client, interceptor) } override val name: String get() = "MyAnimeList" @@ -62,7 +63,7 @@ class Myanimelist(private val context: Context, id: Int) : TrackService(id) { } override fun add(track: Track): Observable { - return api.addLibManga(track, getCSRF()) + return api.addLibManga(track) } override fun update(track: Track): Observable { @@ -70,11 +71,11 @@ class Myanimelist(private val context: Context, id: Int) : TrackService(id) { track.status = COMPLETED } - return api.updateLibManga(track, getCSRF()) + return api.updateLibManga(track) } override fun bind(track: Track): Observable { - return api.findLibManga(track, getCSRF()) + return api.findLibManga(track) .flatMap { remoteTrack -> if (remoteTrack != null) { track.copyPersonalFrom(remoteTrack) @@ -93,7 +94,7 @@ class Myanimelist(private val context: Context, id: Int) : TrackService(id) { } override fun refresh(track: Track): Observable { - return api.getLibManga(track, getCSRF()) + return api.getLibManga(track) .map { remoteTrack -> track.copyPersonalFrom(remoteTrack) track.total_chapters = remoteTrack.total_chapters @@ -104,26 +105,44 @@ class Myanimelist(private val context: Context, id: Int) : TrackService(id) { override fun login(username: String, password: String): Completable { logout() - return api.login(username, password) + return Observable.fromCallable { api.login(username, password) } .doOnNext { csrf -> saveCSRF(csrf) } .doOnNext { saveCredentials(username, password) } .doOnError { logout() } .toCompletable() } + // Attempt to login again if cookies have been cleared but credentials are still filled + fun ensureLoggedIn() { + if (isAuthorized) return + if (!isLogged) throw Exception("MAL Login Credentials not found") + + val username = getUsername() + val password = getPassword() + logout() + + try { + val csrf = api.login(username, password) + saveCSRF(csrf) + saveCredentials(username, password) + } catch (e: Exception) { + logout() + throw e + } + } + override fun logout() { super.logout() preferences.trackToken(this).delete() networkService.cookieManager.remove(HttpUrl.parse(BASE_URL)!!) } - override val isLogged: Boolean - get() = !getUsername().isEmpty() && - !getPassword().isEmpty() && - checkCookies() && - !getCSRF().isEmpty() + val isAuthorized: Boolean + get() = super.isLogged && + getCSRF().isNotEmpty() && + checkCookies() - private fun getCSRF(): String = preferences.trackToken(this).getOrDefault() + fun getCSRF(): String = preferences.trackToken(this).getOrDefault() private fun saveCSRF(csrf: String) = preferences.trackToken(this).set(csrf) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt new file mode 100644 index 000000000..0a032c6a5 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt @@ -0,0 +1,49 @@ +package eu.kanade.tachiyomi.data.track.myanimelist + +import okhttp3.Interceptor +import okhttp3.RequestBody +import okhttp3.Response +import okio.Buffer +import org.json.JSONObject + +class MyAnimeListInterceptor(private val myanimelist: Myanimelist): Interceptor { + + override fun intercept(chain: Interceptor.Chain): Response { + myanimelist.ensureLoggedIn() + + var request = chain.request() + request.body()?.let { + val contentType = it.contentType().toString() + val updatedBody = when { + contentType.contains("x-www-form-urlencoded") -> updateFormBody(it) + contentType.contains("json") -> updateJsonBody(it) + else -> it + } + request = request.newBuilder().post(updatedBody).build() + } + + return chain.proceed(request) + } + + private fun bodyToString(requestBody: RequestBody): String { + Buffer().use { + requestBody.writeTo(it) + return it.readUtf8() + } + } + + private fun updateFormBody(requestBody: RequestBody): RequestBody { + val formString = bodyToString(requestBody) + + return RequestBody.create(requestBody.contentType(), + "$formString${if (formString.isNotEmpty()) "&" else ""}${MyanimelistApi.CSRF}=${myanimelist.getCSRF()}") + } + + private fun updateJsonBody(requestBody: RequestBody): RequestBody { + val jsonString = bodyToString(requestBody) + val newBody = JSONObject(jsonString) + .put(MyanimelistApi.CSRF, myanimelist.getCSRF()) + + return RequestBody.create(requestBody.contentType(), newBody.toString()) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyanimelistApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyanimelistApi.kt index 392a7d441..efc3abefc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyanimelistApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyanimelistApi.kt @@ -22,61 +22,122 @@ import java.io.InputStreamReader import java.util.zip.GZIPInputStream -class MyanimelistApi(private val client: OkHttpClient) { +class MyanimelistApi(private val client: OkHttpClient, interceptor: MyAnimeListInterceptor) { - fun addLibManga(track: Track, csrf: String): Observable { - return Observable.defer { - client.newCall(POST(url = getAddUrl(), body = getMangaPostPayload(track, csrf))) - .asObservableSuccess() - .map { track } - } - } - - fun updateLibManga(track: Track, csrf: String): Observable { - return Observable.defer { - client.newCall(POST(url = getUpdateUrl(), body = getMangaPostPayload(track, csrf))) - .asObservableSuccess() - .map { track } - } - } + private val authClient = client.newBuilder().addInterceptor(interceptor).build() fun search(query: String): Observable> { - return client.newCall(GET(getSearchUrl(query))) - .asObservable() - .flatMap { response -> - Observable.from(Jsoup.parse(response.consumeBody()) - .select("div.js-categories-seasonal.js-block-list.list") - .select("table").select("tbody") - .select("tr").drop(1)) - } - .filter { row -> - row.select(TD)[2].text() != "Novel" - } - .map { row -> - TrackSearch.create(TrackManager.MYANIMELIST).apply { - title = row.searchTitle() - media_id = row.searchMediaId() - total_chapters = row.searchTotalChapters() - summary = row.searchSummary() - cover_url = row.searchCoverUrl() - tracking_url = mangaUrl(media_id) - publishing_status = row.searchPublishingStatus() - publishing_type = row.searchPublishingType() - start_date = row.searchStartDate() + return if (query.startsWith(PREFIX_MY)) { + val realQuery = query.removePrefix(PREFIX_MY) + getList() + .flatMap { Observable.from(it) } + .filter { it.title.contains(realQuery, true) } + .toList() + } + else { + client.newCall(GET(searchUrl(query))) + .asObservable() + .flatMap { response -> + Observable.from(Jsoup.parse(response.consumeBody()) + .select("div.js-categories-seasonal.js-block-list.list") + .select("table").select("tbody") + .select("tr").drop(1)) } - } - .toList() + .filter { row -> + row.select(TD)[2].text() != "Novel" + } + .map { row -> + TrackSearch.create(TrackManager.MYANIMELIST).apply { + title = row.searchTitle() + media_id = row.searchMediaId() + total_chapters = row.searchTotalChapters() + summary = row.searchSummary() + cover_url = row.searchCoverUrl() + tracking_url = mangaUrl(media_id) + publishing_status = row.searchPublishingStatus() + publishing_type = row.searchPublishingType() + start_date = row.searchStartDate() + } + } + .toList() + } } - private fun getList(csrf: String): Observable> { - return getListUrl(csrf) + fun addLibManga(track: Track): Observable { + return Observable.defer { + authClient.newCall(POST(url = addUrl(), body = mangaPostPayload(track))) + .asObservableSuccess() + .map { track } + } + } + + fun updateLibManga(track: Track): Observable { + return Observable.defer { + authClient.newCall(POST(url = updateUrl(), body = mangaPostPayload(track))) + .asObservableSuccess() + .map { track } + } + } + + fun findLibManga(track: Track): Observable { + return authClient.newCall(GET(url = listEntryUrl(track.media_id))) + .asObservable() + .map {response -> + var libTrack: Track? = null + response.use { + if (it.priorResponse()?.isRedirect != true) { + val trackForm = Jsoup.parse(it.consumeBody()) + + libTrack = Track.create(TrackManager.MYANIMELIST).apply { + last_chapter_read = trackForm.select("#add_manga_num_read_chapters").`val`().toInt() + total_chapters = trackForm.select("#totalChap").text().toInt() + status = trackForm.select("#add_manga_status > option[selected]").`val`().toInt() + score = trackForm.select("#add_manga_score > option[selected]").`val`().toFloatOrNull() ?: 0f + } + } + } + libTrack + } + } + + fun getLibManga(track: Track): Observable { + return findLibManga(track) + .map { it ?: throw Exception("Could not find manga") } + } + + fun login(username: String, password: String): String { + val csrf = getSessionInfo() + + login(username, password, csrf) + + return csrf + } + + private fun getSessionInfo(): String { + val response = client.newCall(GET(loginUrl())).execute() + + return Jsoup.parse(response.consumeBody()) + .select("meta[name=csrf_token]") + .attr("content") + } + + private fun login(username: String, password: String, csrf: String) { + val response = client.newCall(POST(url = loginUrl(), body = loginPostBody(username, password, csrf))).execute() + + response.use { + if (response.priorResponse()?.code() != 302) throw Exception("Authentication error") + } + } + + private fun getList(): Observable> { + return getListUrl() .flatMap { url -> getListXml(url) } .flatMap { doc -> Observable.from(doc.select("manga")) } - .map { it -> + .map { TrackSearch.create(TrackManager.MYANIMELIST).apply { title = it.selectText("manga_title")!! media_id = it.selectInt("manga_mangadb_id") @@ -90,107 +151,8 @@ class MyanimelistApi(private val client: OkHttpClient) { .toList() } - private fun getListXml(url: String): Observable { - return client.newCall(GET(url)) - .asObservable() - .map { response -> - Jsoup.parse(response.consumeXmlBody(), "", Parser.xmlParser()) - } - } - - fun findLibManga(track: Track, csrf: String): Observable { - return getList(csrf) - .map { list -> list.find { it.media_id == track.media_id } } - } - - fun getLibManga(track: Track, csrf: String): Observable { - return findLibManga(track, csrf) - .map { it ?: throw Exception("Could not find manga") } - } - - fun login(username: String, password: String): Observable { - return getSessionInfo() - .flatMap { csrf -> - login(username, password, csrf) - } - } - - private fun getSessionInfo(): Observable { - return client.newCall(GET(getLoginUrl())) - .asObservable() - .map { response -> - Jsoup.parse(response.consumeBody()) - .select("meta[name=csrf_token]") - .attr("content") - } - } - - private fun login(username: String, password: String, csrf: String): Observable { - return client.newCall(POST(url = getLoginUrl(), body = getLoginPostBody(username, password, csrf))) - .asObservable() - .map { response -> - response.use { - if (response.priorResponse()?.code() != 302) throw Exception("Authentication error") - } - csrf - } - } - - private fun getLoginPostBody(username: String, password: String, csrf: String): RequestBody { - return FormBody.Builder() - .add("user_name", username) - .add("password", password) - .add("cookie", "1") - .add("sublogin", "Login") - .add("submit", "1") - .add(CSRF, csrf) - .build() - } - - private fun getExportPostBody(csrf: String): RequestBody { - return FormBody.Builder() - .add("type", "2") - .add("subexport", "Export My List") - .add(CSRF, csrf) - .build() - } - - private fun getMangaPostPayload(track: Track, csrf: String): RequestBody { - val body = JSONObject() - .put("manga_id", track.media_id) - .put("status", track.status) - .put("score", track.score) - .put("num_read_chapters", track.last_chapter_read) - .put(CSRF, csrf) - - return RequestBody.create(MediaType.parse("application/json; charset=utf-8"), body.toString()) - } - - private fun getLoginUrl() = Uri.parse(baseUrl).buildUpon() - .appendPath("login.php") - .toString() - - private fun getSearchUrl(query: String): String { - val col = "c[]" - return Uri.parse(baseUrl).buildUpon() - .appendPath("manga.php") - .appendQueryParameter("q", query) - .appendQueryParameter(col, "a") - .appendQueryParameter(col, "b") - .appendQueryParameter(col, "c") - .appendQueryParameter(col, "d") - .appendQueryParameter(col, "e") - .appendQueryParameter(col, "g") - .toString() - } - - private fun getExportListUrl() = Uri.parse(baseUrl).buildUpon() - .appendPath("panel.php") - .appendQueryParameter("go", "export") - .toString() - - private fun getListUrl(csrf: String): Observable { - return client.newCall(POST(url = getExportListUrl(), body = getExportPostBody(csrf))) + private fun getListUrl(): Observable { + return authClient.newCall(POST(url = exportListUrl(), body = exportPostBody())) .asObservable() .map {response -> baseUrl + Jsoup.parse(response.consumeBody()) @@ -200,17 +162,17 @@ class MyanimelistApi(private val client: OkHttpClient) { } } - private fun getUpdateUrl() = Uri.parse(baseModifyListUrl).buildUpon() - .appendPath("edit.json") - .toString() + private fun getListXml(url: String): Observable { + return authClient.newCall(GET(url)) + .asObservable() + .map { response -> + Jsoup.parse(response.consumeXmlBody(), "", Parser.xmlParser()) + } + } - private fun getAddUrl() = Uri.parse(baseModifyListUrl).buildUpon() - .appendPath( "add.json") - .toString() - private fun Response.consumeBody(): String? { use { - if (it.code() != 200) throw Exception("Login error") + if (it.code() != 200) throw Exception("HTTP error ${it.code()}") return it.body()?.string() } } @@ -229,37 +191,105 @@ class MyanimelistApi(private val client: OkHttpClient) { } companion object { - const val baseUrl = "https://myanimelist.net" + const val CSRF = "csrf_token" + + private const val baseUrl = "https://myanimelist.net" private const val baseMangaUrl = "$baseUrl/manga/" private const val baseModifyListUrl = "$baseUrl/ownlist/manga" + private const val PREFIX_MY = "my:" + private const val TD = "td" - fun mangaUrl(remoteId: Int) = baseMangaUrl + remoteId + private fun mangaUrl(remoteId: Int) = baseMangaUrl + remoteId - fun Element.searchTitle() = select("strong").text()!! + private fun loginUrl() = Uri.parse(baseUrl).buildUpon() + .appendPath("login.php") + .toString() - fun Element.searchTotalChapters() = if (select(TD)[4].text() == "-") 0 else select(TD)[4].text().toInt() + private fun searchUrl(query: String): String { + val col = "c[]" + return Uri.parse(baseUrl).buildUpon() + .appendPath("manga.php") + .appendQueryParameter("q", query) + .appendQueryParameter(col, "a") + .appendQueryParameter(col, "b") + .appendQueryParameter(col, "c") + .appendQueryParameter(col, "d") + .appendQueryParameter(col, "e") + .appendQueryParameter(col, "g") + .toString() + } - fun Element.searchCoverUrl() = select("img") + private fun exportListUrl() = Uri.parse(baseUrl).buildUpon() + .appendPath("panel.php") + .appendQueryParameter("go", "export") + .toString() + + private fun updateUrl() = Uri.parse(baseModifyListUrl).buildUpon() + .appendPath("edit.json") + .toString() + + private fun addUrl() = Uri.parse(baseModifyListUrl).buildUpon() + .appendPath( "add.json") + .toString() + + private fun listEntryUrl(mediaId: Int) = Uri.parse(baseModifyListUrl).buildUpon() + .appendPath(mediaId.toString()) + .appendPath("edit") + .toString() + + private fun loginPostBody(username: String, password: String, csrf: String): RequestBody { + return FormBody.Builder() + .add("user_name", username) + .add("password", password) + .add("cookie", "1") + .add("sublogin", "Login") + .add("submit", "1") + .add(CSRF, csrf) + .build() + } + + private fun exportPostBody(): RequestBody { + return FormBody.Builder() + .add("type", "2") + .add("subexport", "Export My List") + .build() + } + + private fun mangaPostPayload(track: Track): RequestBody { + val body = JSONObject() + .put("manga_id", track.media_id) + .put("status", track.status) + .put("score", track.score) + .put("num_read_chapters", track.last_chapter_read) + + return RequestBody.create(MediaType.parse("application/json; charset=utf-8"), body.toString()) + } + + private fun Element.searchTitle() = select("strong").text()!! + + private fun Element.searchTotalChapters() = if (select(TD)[4].text() == "-") 0 else select(TD)[4].text().toInt() + + private fun Element.searchCoverUrl() = select("img") .attr("data-src") .split("\\?")[0] .replace("/r/50x70/", "/") - fun Element.searchMediaId() = select("div.picSurround") + private fun Element.searchMediaId() = select("div.picSurround") .select("a").attr("id") .replace("sarea", "") .toInt() - fun Element.searchSummary() = select("div.pt4") + private fun Element.searchSummary() = select("div.pt4") .first() .ownText()!! - fun Element.searchPublishingStatus() = if (select(TD).last().text() == "-") PUBLISHING else FINISHED + private fun Element.searchPublishingStatus() = if (select(TD).last().text() == "-") "Publishing" else "Finished" - fun Element.searchPublishingType() = select(TD)[2].text()!! + private fun Element.searchPublishingType() = select(TD)[2].text()!! - fun Element.searchStartDate() = select(TD)[6].text()!! + private fun Element.searchStartDate() = select(TD)[6].text()!! - fun getStatus(status: String) = when (status) { + private fun getStatus(status: String) = when (status) { "Reading" -> 1 "Completed" -> 2 "On-Hold" -> 3 @@ -267,10 +297,5 @@ class MyanimelistApi(private val client: OkHttpClient) { "Plan to Read" -> 6 else -> 1 } - - const val CSRF = "csrf_token" - const val TD = "td" - private const val FINISHED = "Finished" - private const val PUBLISHING = "Publishing" } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/AndroidCookieJar.kt b/app/src/main/java/eu/kanade/tachiyomi/network/AndroidCookieJar.kt index 0795b5e5d..6d425bfb9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/network/AndroidCookieJar.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/AndroidCookieJar.kt @@ -47,11 +47,12 @@ class AndroidCookieJar(context: Context) : CookieJar { } fun remove(url: HttpUrl) { - val cookies = manager.getCookie(url.toString()) ?: return - val domain = ".${url.host()}" + val urlString = url.toString() + val cookies = manager.getCookie(urlString) ?: return + cookies.split(";") .map { it.substringBefore("=") } - .onEach { manager.setCookie(domain, "$it=;Max-Age=-1") } + .onEach { manager.setCookie(urlString, "$it=;Max-Age=-1") } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { syncManager.sync() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchAdapter.kt index c11a9bdd0..c9b3f3265 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchAdapter.kt @@ -52,27 +52,27 @@ class TrackSearchAdapter(context: Context) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .centerCrop() .into(view.track_search_cover) + } - if (track.publishing_status.isNullOrBlank()) { - view.track_search_status.gone() - view.track_search_status_result.gone() - } else { - view.track_search_status_result.text = track.publishing_status.capitalize() - } + if (track.publishing_status.isNullOrBlank()) { + view.track_search_status.gone() + view.track_search_status_result.gone() + } else { + view.track_search_status_result.text = track.publishing_status.capitalize() + } - if (track.publishing_type.isNullOrBlank()) { - view.track_search_type.gone() - view.track_search_type_result.gone() - } else { - view.track_search_type_result.text = track.publishing_type.capitalize() - } + if (track.publishing_type.isNullOrBlank()) { + view.track_search_type.gone() + view.track_search_type_result.gone() + } else { + view.track_search_type_result.text = track.publishing_type.capitalize() + } - if (track.start_date.isNullOrBlank()) { - view.track_search_start.gone() - view.track_search_start_result.gone() - } else { - view.track_search_start_result.text = track.start_date - } + if (track.start_date.isNullOrBlank()) { + view.track_search_start.gone() + view.track_search_start_result.gone() + } else { + view.track_search_start_result.text = track.start_date } } } From 5d8dc241d8dd9b5f7e37cb07c77ff719a995af37 Mon Sep 17 00:00:00 2001 From: Harsh Parekh Date: Sun, 9 Jun 2019 08:32:12 -0400 Subject: [PATCH 14/21] Update ranking (#1772) * Add LibraryUpdateRanker This class provides various functions to generate Comparators that can be used to order the manga to update. One such ordering is by relevance: It prioritises manga that were updated more recently. Another Ordering is by lexicographic order: This is the default behaviour. * Use relevanceRanking scheme Instead of default(noRanking/lex ranking) now mangaList is sorted with relevanceRanking. * Add UI and associated variables & strings for Update Ranking. * Use user preferences to determine update ranking scheme. * Refactor relevanceRanking to latestFirstranking. This name seems to better reflect the ranking scheme and frees up the name relevanceRanking for future use. * Set latestFirst scheme as default. (Changing over from lexicographic scheme) * Fix 1 [Convert LibraryUpdateRanker to a object.](./files/82f263749f0ae775385b23dd919f1865360db969#r287513539) [Nitpick: Add lines](./files/82f263749f0ae775385b23dd919f1865360db969#r287540256) [Replace Java comparator](./files/82f263749f0ae775385b23dd919f1865360db969#r287539976) [Nitpick: Add local variable](./files/82f263749f0ae775385b23dd919f1865360db969#r287514805) * Fix 2 [Weird import](./files/82f263749f0ae775385b23dd919f1865360db969#r287513709) [Default value](./files/82f263749f0ae775385b23dd919f1865360db969#r287540064) [Use existing Strings](./files/82f263749f0ae775385b23dd919f1865360db969#r287514476) [Use Library update order](./files/82f263749f0ae775385b23dd919f1865360db969#r287540204) --- .../data/library/LibraryUpdateRanker.kt | 43 +++++++++++++++++++ .../data/library/LibraryUpdateService.kt | 3 ++ .../data/preference/PreferenceKeys.kt | 2 + .../data/preference/PreferencesHelper.kt | 2 + .../ui/setting/SettingsGeneralController.kt | 16 +++++++ app/src/main/res/values/strings.xml | 2 + 6 files changed, 68 insertions(+) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateRanker.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateRanker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateRanker.kt new file mode 100644 index 000000000..735afa833 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateRanker.kt @@ -0,0 +1,43 @@ +package eu.kanade.tachiyomi.data.library + +import eu.kanade.tachiyomi.data.database.models.Manga + +/** + * This class will provide various functions to Rank mangas to efficiently schedule mangas to update. + */ +object LibraryUpdateRanker { + + val rankingScheme = listOf( + (this::lexicographicRanking)(), + (this::latestFirstRanking)()) + + /** + * Provides a total ordering over all the Mangas. + * + * Assumption: An active [Manga] mActive is expected to have been last updated after an + * inactive [Manga] mInactive. + * + * Using this insight, function returns a Comparator for which mActive appears before mInactive. + * @return a Comparator that ranks manga based on relevance. + */ + fun latestFirstRanking(): Comparator { + return Comparator { mangaFirst: Manga, + mangaSecond: Manga -> + compareValues(mangaSecond.last_update, mangaFirst.last_update) + } + } + + /** + * Provides a total ordering over all the Mangas. + * + * Order the manga lexicographically. + * @return a Comparator that ranks manga lexicographically based on the title. + */ + fun lexicographicRanking(): Comparator { + return Comparator { mangaFirst: Manga, + mangaSecond: Manga -> + compareValues(mangaFirst.title, mangaSecond.title) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt index faf44eee4..415a45868 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -18,6 +18,7 @@ import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadService +import eu.kanade.tachiyomi.data.library.LibraryUpdateRanker.rankingScheme import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications @@ -204,7 +205,9 @@ class LibraryUpdateService( // Update favorite manga. Destroy service when completed or in case of an error. subscription = Observable .defer { + val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault() val mangaList = getMangaToUpdate(intent, target) + .sortedWith(rankingScheme[selectedScheme]) // Update either chapter list or manga details. when (target) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index eb5f9d4a1..58388547c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -87,6 +87,8 @@ object PreferenceKeys { const val libraryUpdateCategories = "library_update_categories" + const val libraryUpdatePrioritization = "library_update_prioritization" + const val filterDownloaded = "pref_filter_downloaded_key" const val filterUnread = "pref_filter_unread_key" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 6545b4a39..b8ab34909 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -143,6 +143,8 @@ class PreferencesHelper(val context: Context) { fun libraryUpdateCategories() = rxPrefs.getStringSet(Keys.libraryUpdateCategories, emptySet()) + fun libraryUpdatePrioritization() = rxPrefs.getInteger(Keys.libraryUpdatePrioritization, 1) + fun libraryAsList() = rxPrefs.getBoolean(Keys.libraryAsList, false) fun downloadBadge() = rxPrefs.getBoolean(Keys.downloadBadge, false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt index bc1eb6468..b4619de08 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt @@ -159,6 +159,22 @@ class SettingsGeneralController : SettingsController() { selectedCategories.joinToString { it.name } } } + intListPreference{ + key = Keys.libraryUpdatePrioritization + titleRes = R.string.pref_library_update_prioritization + // The following arrays are to be lined up with the list rankingScheme in: + // ../../data/library/LibraryUpdateRanker.kt + entriesRes = arrayOf( + R.string.action_sort_alpha, + R.string.action_sort_last_updated + ) + entryValues = arrayOf( + "0", + "1" + ) + defaultValue = "1" + summaryRes = R.string.pref_library_update_prioritization_summary + } intListPreference { key = Keys.defaultCategory titleRes = R.string.default_category diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 78b2350f8..4b0f0e6f3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -132,6 +132,8 @@ Monthly Categories to include in global update All + Library update order + Select the order in which Tachiyomi checks for update Library update restrictions Update only when the conditions are met Wi-Fi From cccb56bda195eaa81d30e54b217187f64d6cea00 Mon Sep 17 00:00:00 2001 From: inorichi Date: Sun, 9 Jun 2019 14:35:24 +0200 Subject: [PATCH 15/21] Change default update priorization --- .../eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt | 2 +- .../eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index b8ab34909..68e6371ee 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -143,7 +143,7 @@ class PreferencesHelper(val context: Context) { fun libraryUpdateCategories() = rxPrefs.getStringSet(Keys.libraryUpdateCategories, emptySet()) - fun libraryUpdatePrioritization() = rxPrefs.getInteger(Keys.libraryUpdatePrioritization, 1) + fun libraryUpdatePrioritization() = rxPrefs.getInteger(Keys.libraryUpdatePrioritization, 0) fun libraryAsList() = rxPrefs.getBoolean(Keys.libraryAsList, false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt index b4619de08..1f3e49844 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt @@ -172,7 +172,7 @@ class SettingsGeneralController : SettingsController() { "0", "1" ) - defaultValue = "1" + defaultValue = "0" summaryRes = R.string.pref_library_update_prioritization_summary } intListPreference { From 1d079dd9a40e0eedf2408e51f207b4e28cff8435 Mon Sep 17 00:00:00 2001 From: MCAxiaz Date: Tue, 18 Jun 2019 04:53:47 -0700 Subject: [PATCH 16/21] use dist: trusty (#2085) --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 13727a6ee..9667a53d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +dist: trusty language: android android: components: From 62f9071adcb9104329a25bcf19e004297d1a5d5e Mon Sep 17 00:00:00 2001 From: Eugene Date: Sat, 29 Jun 2019 16:27:58 -0400 Subject: [PATCH 17/21] Avoid infinite loading in global search if a single catalogue fails (#2097) --- .../ui/catalogue/global_search/CatalogueSearchPresenter.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchPresenter.kt index 39bcfc1c6..14312da13 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchPresenter.kt @@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceManager 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.LoginSource import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter @@ -157,9 +158,9 @@ open class CatalogueSearchPresenter( fetchSourcesSubscription?.unsubscribe() fetchSourcesSubscription = Observable.from(sources) .flatMap({ source -> - source.fetchSearchManga(1, query, FilterList()) + Observable.defer { source.fetchSearchManga(1, query, FilterList()) } .subscribeOn(Schedulers.io()) - .onExceptionResumeNext(Observable.empty()) // Ignore timeouts. + .onErrorReturn { MangasPage(emptyList(), false) } // Ignore timeouts or other exceptions .map { it.mangas.take(10) } // Get at most 10 manga from search result. .map { it.map { networkToLocalManga(it, source.id) } } // Convert to local manga. .doOnNext { fetchImage(it, source) } // Load manga covers. From e8638cb0b32989262b1bc4fdf5d96537c8eedd81 Mon Sep 17 00:00:00 2001 From: MCAxiaz Date: Mon, 1 Jul 2019 04:06:19 -0700 Subject: [PATCH 18/21] Hide Empty Search Results in Catalogues (#2066) * test2 * remove nothing_found view and associated resources --- .../global_search/CatalogueSearchHolder.kt | 20 +++++++---- .../res/drawable/ic_search_black_112dp.xml | 9 ----- ...atalogue_global_search_controller_card.xml | 35 ------------------- app/src/main/res/values-ar/strings.xml | 1 - app/src/main/res/values-bg/strings.xml | 1 - app/src/main/res/values-bn/strings.xml | 1 - app/src/main/res/values-cs/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-el/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-hi/strings.xml | 1 - app/src/main/res/values-in/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 1 - app/src/main/res/values-ko/strings.xml | 1 - app/src/main/res/values-ms/strings.xml | 1 - app/src/main/res/values-nl/strings.xml | 1 - app/src/main/res/values-pl/strings.xml | 1 - app/src/main/res/values-pt-rBR/strings.xml | 1 - app/src/main/res/values-ro/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-sc/strings.xml | 1 - app/src/main/res/values-sr/strings.xml | 1 - app/src/main/res/values-sv/strings.xml | 1 - app/src/main/res/values-tr/strings.xml | 1 - app/src/main/res/values-uk/strings.xml | 1 - app/src/main/res/values-vi/strings.xml | 1 - app/src/main/res/values-zh-rCN/strings.xml | 1 - app/src/main/res/values/strings.xml | 1 - 29 files changed, 14 insertions(+), 76 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_search_black_112dp.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchHolder.kt index d189ada45..eefb37960 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchHolder.kt @@ -31,9 +31,6 @@ class CatalogueSearchHolder(view: View, val adapter: CatalogueSearchAdapter) : // Set layout horizontal. recycler.layoutManager = LinearLayoutManager(view.context, LinearLayoutManager.HORIZONTAL, false) recycler.adapter = mangaAdapter - - nothing_found_icon.setVectorCompat(R.drawable.ic_search_black_112dp, - view.context.getResourceColor(android.R.attr.textColorHint)) } /** @@ -54,15 +51,15 @@ class CatalogueSearchHolder(view: View, val adapter: CatalogueSearchAdapter) : when { results == null -> { progress.visible() - nothing_found.gone() + showHolder() } results.isEmpty() -> { progress.gone() - nothing_found.visible() + hideHolder() } else -> { progress.gone() - nothing_found.gone() + showHolder() } } if (results !== lastBoundResults) { @@ -96,4 +93,15 @@ class CatalogueSearchHolder(view: View, val adapter: CatalogueSearchAdapter) : return null } + + private fun showHolder() { + title.visible() + source_card.visible() + } + + private fun hideHolder() { + title.gone() + source_card.gone() + } + } diff --git a/app/src/main/res/drawable/ic_search_black_112dp.xml b/app/src/main/res/drawable/ic_search_black_112dp.xml deleted file mode 100644 index 05705a607..000000000 --- a/app/src/main/res/drawable/ic_search_black_112dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/layout/catalogue_global_search_controller_card.xml b/app/src/main/res/layout/catalogue_global_search_controller_card.xml index 6d220c41b..c5f81880d 100644 --- a/app/src/main/res/layout/catalogue_global_search_controller_card.xml +++ b/app/src/main/res/layout/catalogue_global_search_controller_card.xml @@ -36,41 +36,6 @@ android:layout_height="wrap_content" android:layout_gravity="center" /> - - - - - - - - لا يمكن تحديد الإعداد الافتراضي مع الفئات الأخرى تم إضافة المانجا إلى مكتبتك البحث الشامل… - لا توجد نتائج! اﻷخيرة تصفح diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 17c298057..d605db986 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -397,7 +397,6 @@ Вход Други Глобално търсене… - Не бяха открити резултати! Последни Търсене Прекият път беше добавен към началния екран. diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index cbc91aef5..20fdf52d2 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -266,7 +266,6 @@ নির্ধারিতগুলো অন্যান্য ধরণের সাথে নির্বাচন করা যাবে না মাংগাটি আপনার মাংগাশালায় যোগ হয়েছে সার্বজনীন খোঁজ… - কোন ফলাফল পাওয়া যায়নি! সর্বশেষ ব্রাউজ diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index ae39ddeed..e34cf03e5 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -134,7 +134,6 @@ Žádné další výsledky Manga byla přidána do vaší knihovny Globální vyhledávání… - Žádné výsledky! Manga byla odstraněna z databáze! Info Popis diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 09b203536..4ca8aac08 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -398,7 +398,6 @@ Andere Globale Suche… - Keine Treffer gefunden! Letzte Umsehen diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 00d75d39d..67e80fbfb 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -301,7 +301,6 @@ Το manga έχει προστεθεί στη βιβλιοθήκη σας \n Καθολική αναζήτηση… - Δεν βρέθηκαν αποτελέσματα! Τελευταίο Ξεφύλλισμα diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 6ef35a667..55b97f65e 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -395,7 +395,6 @@ También asegúrese de haber iniciado sesión en las fuentes que lo requieren an Local Otros Búsqueda global… - Ningún resultado encontrado! Recientes Explorar Acceso directo fue agregado a la pantalla de inicio. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 4ef1f1c34..e8eba2fa2 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -396,7 +396,6 @@ Assurez-vous que vous êtes connecté à des sources qui le demande avant de com Connexion Autre Recherche globale… - Aucun résultat ! Récents Explorer Un raccourci a été ajouté à la page d\'accueil. diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 34da05d9e..509eb7991 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -250,7 +250,6 @@ डिफ़ॉल्ट को अन्य श्रेणियों के साथ नहीं चुना जा सकता है मंगा को आपकी लाइब्रेरी में जोड़ा गया है वैश्विक खोज … - कोई परिणाम नहीं मिला! नवीनतम ब्राउज यह मंगा डेटाबेस से हटा दिया गया था! diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index cdfa74d5c..59ae19053 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -400,7 +400,6 @@ Lokal Lainnya Pencarian global… - Tidak ada hasil! Terbaru Jelajahi diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index c802af8f1..bd63cae33 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -476,7 +476,6 @@ Altro Predefinito non può essere selezionato con altre categorie Ricerca globale… - Nessun risultato! Ultimi Sfoglia diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index e59864f8f..f7e4d10bf 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -237,7 +237,6 @@ 최소한 1개의 유효한 소스를 선택해주세요 더이상 결과 없음 전역 검색… - 결과가 없습니다! 최신 정보 설명 diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index 401c91d9a..793c54f60 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -268,7 +268,6 @@ Lalai tidak boleh dipilih bersama kategori lain Manga ini telah ditambahkan ke koleksi anda Carian keseluruhan… - Tiada sebarang hasil! Terkini Semak imbas diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 7515ee495..ee21122e5 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -365,7 +365,6 @@ Zorg ook dat je ingelogd bent voor bronnen die dit vereisen alvorens je het teru Lokaal Alternatief Globaal zoeken… - Geen resultaten gevonden! Recent Snelkoppeling toegevoegd aan startscherm. Bibliotheek diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 1b01f0e64..c58a0d7bd 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -398,7 +398,6 @@ Nie znaleziono źródła %1$s Inne Wyszukiwanie globalne… - Brak wyników! Przeglądaj Skrót został dodany do ekranu głównego. diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index d58e37555..051b0001c 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -362,7 +362,6 @@ Além disso, verifique se as fontes que requerem uma conta foram configuradas co Entrar Outras Pesquisa global… - Nenhum resultado encontrado! Mais recente Navegar O atalho foi adicionado à sua tela inicial. diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 011f37610..57f030db4 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -299,7 +299,6 @@ Modul implicit nu poate fi selectat cu alte categorii Manga-ul a fost adăugat bibliotecii tale Căutare globală… - Nici un rezultat găsit! Cel mai recent Caută diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 74a381fce..e4143ff50 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -360,7 +360,6 @@ Локальная Другие Глобальный поиск… - Результат не найден! Последняя Смотреть Ярлык был добавлен на главный экран. diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 7b04850ec..f3a696e65 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -283,7 +283,6 @@ Predefinidu non podet èssere ischertadu cun àteras categorias Su manga est istadu annantu a sa biblioteca tua Chirca globale… - Perunu risultadu agatadu! Ùrtimos Esplora Custu manga est istadu bogadu dae sa base de datos! diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index f6e79afd8..fbd6e5270 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -283,7 +283,6 @@ Opšte je nemoguće označiti sa ostalim kategorijama Ova manga je dodata u biblioteku Globalno pretraživanje… - Nema pronađenih rezultata! Poslednje Pretraži Ova manga je uklonjena iz baze podataka! diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index b7debab87..4ba03fd02 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -300,7 +300,6 @@ Standard inte kan väljas med andra kategorier Mangan har lagts till i din bibliotek Global sökning… - Inga resultat hittades! Senaste Bläddra diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index cd808c059..87a7a2242 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -281,7 +281,6 @@ Öntanımlı diğer kategorilerle birlikte seçilemez Manga kitaplığınıza eklendi Genel arama… - Sonuç bulunmadı! En son Göz at Bu manga veritabanından kaldırıldı! diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 2ac2d5fd9..aaa4a877f 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -282,7 +282,6 @@ Категорія за замовчуванням не може бути вибраною разом з іншими категоріями Цю мангу уже додано до бібліотеки Глобальний пошук… - Результатів не знайдено! Остання Переглянути Ця манга було видалена з бази даних! diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index f5619a7f1..e54508010 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -461,7 +461,6 @@ Mặc định không thể chọn với các danh mục khác Truyện này đã được thêm vào thư viện Tìm kiếm toàn cầu… - Không tìm thấy kết quả! Mới nhất Duyệt Tiêu đề diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 3b23b8cd1..420b1cddf 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -281,7 +281,6 @@ 默认标签不能与其它标签一起选择 已将此漫画添加至书架 全局搜索… - 找不到! 最近更新 浏览 漫画已被移出数据库! diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4b0f0e6f3..f72686b4f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -328,7 +328,6 @@ Default can\'t be selected with other categories The manga has been added to your library Global search… - No results found! Latest Browse From d5f5ba95bb13e8d408d8c49afa0ca7851e860766 Mon Sep 17 00:00:00 2001 From: Eugene Date: Sat, 13 Jul 2019 13:36:30 -0400 Subject: [PATCH 19/21] Add automatic updates for dev builds (#2128) --- .../data/updater/GithubUpdateResult.kt | 7 ---- .../kanade/tachiyomi/data/updater/Release.kt | 13 ++++++ .../tachiyomi/data/updater/UpdateChecker.kt | 25 ++++++++++++ .../tachiyomi/data/updater/UpdateResult.kt | 8 ++++ .../tachiyomi/data/updater/UpdaterJob.kt | 10 ++--- .../data/updater/devrepo/DevRepoRelease.kt | 14 +++++++ .../updater/devrepo/DevRepoUpdateChecker.kt | 40 +++++++++++++++++++ .../updater/devrepo/DevRepoUpdateResult.kt | 10 +++++ .../updater/{ => github}/GithubRelease.kt | 11 ++--- .../updater/{ => github}/GithubService.kt | 4 +- .../{ => github}/GithubUpdateChecker.kt | 14 +++---- .../data/updater/github/GithubUpdateResult.kt | 10 +++++ .../ui/setting/SettingsAboutController.kt | 15 ++++--- 13 files changed, 147 insertions(+), 34 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubUpdateResult.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/updater/Release.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdateChecker.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdateResult.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/updater/devrepo/DevRepoRelease.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/updater/devrepo/DevRepoUpdateChecker.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/updater/devrepo/DevRepoUpdateResult.kt rename app/src/main/java/eu/kanade/tachiyomi/data/updater/{ => github}/GithubRelease.kt (72%) rename app/src/main/java/eu/kanade/tachiyomi/data/updater/{ => github}/GithubService.kt (95%) rename app/src/main/java/eu/kanade/tachiyomi/data/updater/{ => github}/GithubUpdateChecker.kt (67%) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubUpdateResult.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubUpdateResult.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubUpdateResult.kt deleted file mode 100644 index 3f07d2da6..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubUpdateResult.kt +++ /dev/null @@ -1,7 +0,0 @@ -package eu.kanade.tachiyomi.data.updater - -sealed class GithubUpdateResult { - - class NewUpdate(val release: GithubRelease): GithubUpdateResult() - class NoNewUpdate : GithubUpdateResult() -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/Release.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/Release.kt new file mode 100644 index 000000000..9ac138e98 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/Release.kt @@ -0,0 +1,13 @@ +package eu.kanade.tachiyomi.data.updater + +interface Release { + + val info: String + + /** + * Get download link of latest release. + * @return download link of latest release. + */ + val downloadLink: String + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdateChecker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdateChecker.kt new file mode 100644 index 000000000..4d2a1de66 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdateChecker.kt @@ -0,0 +1,25 @@ +package eu.kanade.tachiyomi.data.updater + +import eu.kanade.tachiyomi.BuildConfig +import eu.kanade.tachiyomi.data.updater.devrepo.DevRepoUpdateChecker +import eu.kanade.tachiyomi.data.updater.github.GithubUpdateChecker +import rx.Observable + +abstract class UpdateChecker { + + companion object { + fun getUpdateChecker(): UpdateChecker { + return if (BuildConfig.DEBUG) { + DevRepoUpdateChecker() + } else { + GithubUpdateChecker() + } + } + } + + /** + * Returns observable containing release information + */ + abstract fun checkForUpdate(): Observable + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdateResult.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdateResult.kt new file mode 100644 index 000000000..a59864f55 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdateResult.kt @@ -0,0 +1,8 @@ +package eu.kanade.tachiyomi.data.updater + +abstract class UpdateResult { + + open class NewUpdate(val release: T): UpdateResult() + open class NoNewUpdate: UpdateResult() + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterJob.kt index 59832bd2d..594ecd31b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterJob.kt @@ -13,10 +13,10 @@ import eu.kanade.tachiyomi.util.notificationManager class UpdaterJob : Job() { override fun onRunJob(params: Params): Result { - return GithubUpdateChecker() + return UpdateChecker.getUpdateChecker() .checkForUpdate() .map { result -> - if (result is GithubUpdateResult.NewUpdate) { + if (result is UpdateResult.NewUpdate<*>) { val url = result.release.downloadLink val intent = Intent(context, UpdaterService::class.java).apply { @@ -33,9 +33,9 @@ class UpdaterJob : Job() { PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)) } } - Job.Result.SUCCESS + Result.SUCCESS } - .onErrorReturn { Job.Result.FAILURE } + .onErrorReturn { Result.FAILURE } // Sadly, the task needs to be synchronous. .toBlocking() .single() @@ -64,4 +64,4 @@ class UpdaterJob : Job() { } } -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/devrepo/DevRepoRelease.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/devrepo/DevRepoRelease.kt new file mode 100644 index 000000000..ea8a79a18 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/devrepo/DevRepoRelease.kt @@ -0,0 +1,14 @@ +package eu.kanade.tachiyomi.data.updater.devrepo + +import eu.kanade.tachiyomi.data.updater.Release + +class DevRepoRelease(override val info: String) : Release { + + override val downloadLink: String + get() = LATEST_URL + + companion object { + const val LATEST_URL = "https://tachiyomi.kanade.eu/latest" + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/devrepo/DevRepoUpdateChecker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/devrepo/DevRepoUpdateChecker.kt new file mode 100644 index 000000000..a24036830 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/devrepo/DevRepoUpdateChecker.kt @@ -0,0 +1,40 @@ +package eu.kanade.tachiyomi.data.updater.devrepo + +import eu.kanade.tachiyomi.BuildConfig +import eu.kanade.tachiyomi.data.updater.UpdateChecker +import eu.kanade.tachiyomi.data.updater.UpdateResult +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.NetworkHelper +import eu.kanade.tachiyomi.network.asObservable +import okhttp3.OkHttpClient +import rx.Observable +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class DevRepoUpdateChecker : UpdateChecker() { + + private val client: OkHttpClient by lazy { + Injekt.get().client.newBuilder() + .followRedirects(false) + .build() + } + + private val versionRegex: Regex by lazy { + Regex("tachiyomi-r(\\d+).apk") + } + + override fun checkForUpdate(): Observable { + return client.newCall(GET(DevRepoRelease.LATEST_URL)).asObservable() + .map { response -> + // Get latest repo version number from header in format "Location: tachiyomi-r1512.apk" + val latestVersionNumber: String = versionRegex.find(response.header("Location")!!)!!.groupValues[1] + + if (latestVersionNumber.toInt() > BuildConfig.COMMIT_COUNT.toInt()) { + DevRepoUpdateResult.NewUpdate(DevRepoRelease("v$latestVersionNumber")) + } else { + DevRepoUpdateResult.NoNewUpdate() + } + } + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/devrepo/DevRepoUpdateResult.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/devrepo/DevRepoUpdateResult.kt new file mode 100644 index 000000000..1bda48b9c --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/devrepo/DevRepoUpdateResult.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.data.updater.devrepo + +import eu.kanade.tachiyomi.data.updater.UpdateResult + +sealed class DevRepoUpdateResult : UpdateResult() { + + class NewUpdate(release: DevRepoRelease): UpdateResult.NewUpdate(release) + class NoNewUpdate: UpdateResult.NoNewUpdate() + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubRelease.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubRelease.kt similarity index 72% rename from app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubRelease.kt rename to app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubRelease.kt index 8c20690e2..f65bf39ba 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubRelease.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubRelease.kt @@ -1,24 +1,25 @@ -package eu.kanade.tachiyomi.data.updater +package eu.kanade.tachiyomi.data.updater.github import com.google.gson.annotations.SerializedName +import eu.kanade.tachiyomi.data.updater.Release /** * Release object. * Contains information about the latest release from Github. * * @param version version of latest release. - * @param changeLog log of latest release. + * @param info log of latest release. * @param assets assets of latest release. */ class GithubRelease(@SerializedName("tag_name") val version: String, - @SerializedName("body") val changeLog: String, - @SerializedName("assets") private val assets: List) { + @SerializedName("body") override val info: String, + @SerializedName("assets") private val assets: List): Release { /** * Get download link of latest release from the assets. * @return download link of latest release. */ - val downloadLink: String + override val downloadLink: String get() = assets[0].downloadLink /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubService.kt similarity index 95% rename from app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubService.kt rename to app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubService.kt index 82befdc72..e19e3528d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubService.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.data.updater +package eu.kanade.tachiyomi.data.updater.github import eu.kanade.tachiyomi.network.NetworkHelper import retrofit2.Retrofit @@ -30,4 +30,4 @@ interface GithubService { @GET("/repos/inorichi/tachiyomi/releases/latest") fun getLatestVersion(): Observable -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubUpdateChecker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubUpdateChecker.kt similarity index 67% rename from app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubUpdateChecker.kt rename to app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubUpdateChecker.kt index 7d321dc71..6fc629740 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/GithubUpdateChecker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubUpdateChecker.kt @@ -1,16 +1,15 @@ -package eu.kanade.tachiyomi.data.updater +package eu.kanade.tachiyomi.data.updater.github import eu.kanade.tachiyomi.BuildConfig +import eu.kanade.tachiyomi.data.updater.UpdateChecker +import eu.kanade.tachiyomi.data.updater.UpdateResult import rx.Observable -class GithubUpdateChecker { +class GithubUpdateChecker : UpdateChecker() { private val service: GithubService = GithubService.create() - /** - * Returns observable containing release information - */ - fun checkForUpdate(): Observable { + override fun checkForUpdate(): Observable { return service.getLatestVersion().map { release -> val newVersion = release.version.replace("[^\\d.]".toRegex(), "") @@ -22,4 +21,5 @@ class GithubUpdateChecker { } } } -} \ No newline at end of file + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubUpdateResult.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubUpdateResult.kt new file mode 100644 index 000000000..fcb304604 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubUpdateResult.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.data.updater.github + +import eu.kanade.tachiyomi.data.updater.UpdateResult + +sealed class GithubUpdateResult : UpdateResult() { + + class NewUpdate(release: GithubRelease): UpdateResult.NewUpdate(release) + class NoNewUpdate : UpdateResult.NoNewUpdate() + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt index a736da2a5..bbd23da2c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt @@ -9,8 +9,8 @@ import android.view.View import com.afollestad.materialdialogs.MaterialDialog import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.updater.GithubUpdateChecker -import eu.kanade.tachiyomi.data.updater.GithubUpdateResult +import eu.kanade.tachiyomi.data.updater.UpdateChecker +import eu.kanade.tachiyomi.data.updater.UpdateResult import eu.kanade.tachiyomi.data.updater.UpdaterJob import eu.kanade.tachiyomi.data.updater.UpdaterService import eu.kanade.tachiyomi.ui.base.controller.DialogController @@ -26,20 +26,19 @@ import java.util.Locale import java.util.TimeZone import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys - class SettingsAboutController : SettingsController() { /** * Checks for new releases */ - private val updateChecker by lazy { GithubUpdateChecker() } + private val updateChecker by lazy { UpdateChecker.getUpdateChecker() } /** * The subscribtion service of the obtained release object */ private var releaseSubscription: Subscription? = null - private val isUpdaterEnabled = !BuildConfig.DEBUG && BuildConfig.INCLUDE_UPDATER + private val isUpdaterEnabled = BuildConfig.INCLUDE_UPDATER override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { titleRes = R.string.pref_category_about @@ -124,14 +123,14 @@ class SettingsAboutController : SettingsController() { .observeOn(AndroidSchedulers.mainThread()) .subscribe({ result -> when (result) { - is GithubUpdateResult.NewUpdate -> { - val body = result.release.changeLog + is UpdateResult.NewUpdate<*> -> { + val body = result.release.info val url = result.release.downloadLink // Create confirmation window NewUpdateDialogController(body, url).showDialog(router) } - is GithubUpdateResult.NoNewUpdate -> { + is UpdateResult.NoNewUpdate -> { activity?.toast(R.string.update_check_no_new_updates) } } From b68ef8c983bd24d6567190534991c30e722cb41e Mon Sep 17 00:00:00 2001 From: Eugene Date: Tue, 23 Jul 2019 06:29:01 -0400 Subject: [PATCH 20/21] Update info about auto updates in README (#2132) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 294bb39ce..114e87bde 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ | Build | Stable | Dev | Contribute | Contact | |-------|----------|---------|------------|---------| -| [![Travis](https://img.shields.io/travis/inorichi/tachiyomi.svg)](https://travis-ci.org/inorichi/tachiyomi) | [![stable release](https://img.shields.io/github/release/inorichi/tachiyomi.svg?maxAge=3600&label=download%20(autoupdate%20included))](https://github.com/inorichi/tachiyomi/releases) | [![latest dev build](https://img.shields.io/badge/download-latest%20build-blue.svg)](http://tachiyomi.kanade.eu/latest) | [![Translation status](https://hosted.weblate.org/widgets/tachiyomi/-/svg-badge.svg)](https://hosted.weblate.org/engage/tachiyomi/?utm_source=widget) | [![Discord](https://img.shields.io/discord/349436576037732353.svg)](https://discord.gg/tachiyomi) | +| [![Travis](https://img.shields.io/travis/inorichi/tachiyomi.svg)](https://travis-ci.org/inorichi/tachiyomi) | [![stable release](https://img.shields.io/github/release/inorichi/tachiyomi.svg?maxAge=3600&label=download)](https://github.com/inorichi/tachiyomi/releases) | [![latest dev build](https://img.shields.io/badge/download-latest%20build-blue.svg)](http://tachiyomi.kanade.eu/latest) | [![Translation status](https://hosted.weblate.org/widgets/tachiyomi/-/svg-badge.svg)](https://hosted.weblate.org/engage/tachiyomi/?utm_source=widget) | [![Discord](https://img.shields.io/discord/349436576037732353.svg)](https://discord.gg/tachiyomi) | # ![app icon](./.github/readme-images/app-icon.png)Tachiyomi @@ -23,7 +23,7 @@ Features include: ## Download Get the app from our [releases page](https://github.com/inorichi/tachiyomi/releases). -If you want to try new features before they get to the stable release, you can download the dev version [here](http://tachiyomi.kanade.eu/latest). (auto-updates not included) +If you want to try new features before they get to the stable release, you can download the dev version [here](http://tachiyomi.kanade.eu/latest). ## Issues, Feature Requests and Contributing From 3abae1cc75837e9b64f5f1577da827daa17f00ce Mon Sep 17 00:00:00 2001 From: fei long Date: Tue, 23 Jul 2019 18:35:38 +0800 Subject: [PATCH 21/21] Add chinese track website "bangumi" (#2032) * copy from shikimori and change parmater * add login activity * fix * login sucess * search * add... * auth fix * save status * revert shikimori * fix oauth error * add bangumi info * update read chapter index * refersh token * remove outdate file * drop comment * change icon * drop search result which type not comic * fix bind logic * set status * add ep status * format code * disable cache for `collection` api --- app/src/main/AndroidManifest.xml | 14 ++ .../tachiyomi/data/track/TrackManager.kt | 6 +- .../tachiyomi/data/track/bangumi/Avatar.kt | 7 + .../tachiyomi/data/track/bangumi/Bangumi.kt | 144 ++++++++++++ .../data/track/bangumi/BangumiApi.kt | 208 ++++++++++++++++++ .../data/track/bangumi/BangumiInterceptor.kt | 61 +++++ .../data/track/bangumi/BangumiModels.kt | 22 ++ .../data/track/bangumi/Collection.kt | 13 ++ .../tachiyomi/data/track/bangumi/OAuth.kt | 16 ++ .../tachiyomi/data/track/bangumi/Status.kt | 7 + .../tachiyomi/data/track/bangumi/User.kt | 11 + .../ui/setting/BangumiLoginActivity.kt | 50 +++++ .../ui/setting/SettingsTrackingController.kt | 10 + app/src/main/res/drawable-xxxhdpi/bangumi.png | Bin 0 -> 6388 bytes 14 files changed, 568 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Avatar.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiModels.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Collection.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/OAuth.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Status.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/User.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/setting/BangumiLoginActivity.kt create mode 100644 app/src/main/res/drawable-xxxhdpi/bangumi.png diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index eff4c7057..e13d89ba7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -75,6 +75,20 @@ android:scheme="tachiyomi" /> + + + + + + + + + + { + return IntRange(0, 10).map(Int::toString) + } + + override fun displayScore(track: Track): String { + return track.score.toInt().toString() + } + + override fun add(track: Track): Observable { + return api.addLibManga(track) + } + + override fun update(track: Track): Observable { + if (track.total_chapters != 0 && track.last_chapter_read == track.total_chapters) { + track.status = COMPLETED + } + return api.updateLibManga(track) + } + + override fun bind(track: Track): Observable { + return api.statusLibManga(track) + .flatMap { + api.findLibManga(track).flatMap { remoteTrack -> + if (remoteTrack != null && it != null) { + track.copyPersonalFrom(remoteTrack) + track.library_id = remoteTrack.library_id + track.status = remoteTrack.status + track.last_chapter_read = remoteTrack.last_chapter_read + update(track) + } else { + // Set default fields if it's not found in the list + track.score = DEFAULT_SCORE.toFloat() + track.status = DEFAULT_STATUS + add(track) + update(track) + } + } + } + } + + override fun search(query: String): Observable> { + return api.search(query) + } + + override fun refresh(track: Track): Observable { + return api.statusLibManga(track) + .flatMap { + track.copyPersonalFrom(it!!) + api.findLibManga(track) + .map { remoteTrack -> + if (remoteTrack != null) { + track.total_chapters = remoteTrack.total_chapters + track.status = remoteTrack.status + } + track + } + } + } + + companion object { + const val READING = 3 + const val COMPLETED = 2 + const val ON_HOLD = 4 + const val DROPPED = 5 + const val PLANNING = 1 + + const val DEFAULT_STATUS = READING + const val DEFAULT_SCORE = 0 + } + + override val name = "Bangumi" + + private val gson: Gson by injectLazy() + + private val interceptor by lazy { BangumiInterceptor(this, gson) } + + private val api by lazy { BangumiApi(client, interceptor) } + + override fun getLogo() = R.drawable.bangumi + + override fun getLogoColor() = Color.rgb(0xF0, 0x91, 0x99) + + override fun getStatusList(): List { + return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLANNING) + } + + override fun getStatus(status: Int): String = with(context) { + when (status) { + READING -> getString(R.string.reading) + COMPLETED -> getString(R.string.completed) + ON_HOLD -> getString(R.string.on_hold) + DROPPED -> getString(R.string.dropped) + PLANNING -> getString(R.string.plan_to_read) + else -> "" + } + } + + override fun login(username: String, password: String) = login(password) + + fun login(code: String): Completable { + return api.accessToken(code).map { oauth: OAuth? -> + interceptor.newAuth(oauth) + if (oauth != null) { + saveCredentials(oauth.user_id.toString(), oauth.access_token) + } + }.doOnError { + logout() + }.toCompletable() + } + + fun saveToken(oauth: OAuth?) { + val json = gson.toJson(oauth) + preferences.trackToken(this).set(json) + } + + fun restoreToken(): OAuth? { + return try { + gson.fromJson(preferences.trackToken(this).get(), OAuth::class.java) + } catch (e: Exception) { + null + } + } + + override fun logout() { + super.logout() + preferences.trackToken(this).set(null) + interceptor.newAuth(null) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt new file mode 100644 index 000000000..661c26523 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt @@ -0,0 +1,208 @@ +package eu.kanade.tachiyomi.data.track.bangumi + +import android.net.Uri +import com.github.salomonbrys.kotson.array +import com.github.salomonbrys.kotson.obj +import com.google.gson.Gson +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import eu.kanade.tachiyomi.data.database.models.Track +import eu.kanade.tachiyomi.data.track.TrackManager +import eu.kanade.tachiyomi.data.track.model.TrackSearch +import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.network.asObservableSuccess +import okhttp3.CacheControl +import okhttp3.FormBody +import okhttp3.OkHttpClient +import okhttp3.Request +import rx.Observable +import uy.kohesive.injekt.injectLazy +import java.net.URLEncoder + +class BangumiApi(private val client: OkHttpClient, interceptor: BangumiInterceptor) { + + private val gson: Gson by injectLazy() + private val parser = JsonParser() + private val authClient = client.newBuilder().addInterceptor(interceptor).build() + + fun addLibManga(track: Track): Observable { + val body = FormBody.Builder() + .add("rating", track.score.toInt().toString()) + .add("status", track.toBangumiStatus()) + .build() + val request = Request.Builder() + .url("$apiUrl/collection/${track.media_id}/update") + .post(body) + .build() + return authClient.newCall(request) + .asObservableSuccess() + .map { + track + } + } + + fun updateLibManga(track: Track): Observable { + // chapter update + val body = FormBody.Builder() + .add("watched_eps", track.last_chapter_read.toString()) + .build() + val request = Request.Builder() + .url("$apiUrl/subject/${track.media_id}/update/watched_eps") + .post(body) + .build() + + // read status update + val sbody = FormBody.Builder() + .add("status", track.toBangumiStatus()) + .build() + val srequest = Request.Builder() + .url("$apiUrl/collection/${track.media_id}/update") + .post(sbody) + .build() + return authClient.newCall(request) + .asObservableSuccess() + .map { + track + }.flatMap { + authClient.newCall(srequest) + .asObservableSuccess() + .map { + track + } + } + } + + fun search(search: String): Observable> { + val url = Uri.parse( + "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}").buildUpon() + .appendQueryParameter("max_results", "20") + .build() + val request = Request.Builder() + .url(url.toString()) + .get() + .build() + return authClient.newCall(request) + .asObservableSuccess() + .map { netResponse -> + val responseBody = netResponse.body()?.string().orEmpty() + if (responseBody.isEmpty()) { + throw Exception("Null Response") + } + val response = parser.parse(responseBody).obj["list"]?.array + response?.filter { it.obj["type"].asInt == 1 }?.map { jsonToSearch(it.obj) } + } + + } + + private fun jsonToSearch(obj: JsonObject): TrackSearch { + return TrackSearch.create(TrackManager.BANGUMI).apply { + media_id = obj["id"].asInt + title = obj["name_cn"].asString + cover_url = obj["images"].obj["common"].asString + summary = obj["name"].asString + tracking_url = obj["url"].asString + } + } + + private fun jsonToTrack(mangas: JsonObject): Track { + return Track.create(TrackManager.BANGUMI).apply { + title = mangas["name"].asString + media_id = mangas["id"].asInt + score = if (mangas["rating"] != null) + (if (mangas["rating"].isJsonObject) mangas["rating"].obj["score"].asFloat else 0f) + else 0f + status = Bangumi.DEFAULT_STATUS + tracking_url = mangas["url"].asString + } + } + + fun findLibManga(track: Track): Observable { + val urlMangas = "$apiUrl/subject/${track.media_id}" + val requestMangas = Request.Builder() + .url(urlMangas) + .get() + .build() + + return authClient.newCall(requestMangas) + .asObservableSuccess() + .map { netResponse -> + // get comic info + val responseBody = netResponse.body()?.string().orEmpty() + jsonToTrack(parser.parse(responseBody).obj) + } + } + + fun statusLibManga(track: Track): Observable { + val urlUserRead = "$apiUrl/collection/${track.media_id}" + val requestUserRead = Request.Builder() + .url(urlUserRead) + .cacheControl(CacheControl.FORCE_NETWORK) + .get() + .build() + + // todo get user readed chapter here + return authClient.newCall(requestUserRead) + .asObservableSuccess() + .map { netResponse -> + val resp = netResponse.body()?.string() + val coll = gson.fromJson(resp, Collection::class.java) + track.status = coll.status?.id!! + track.last_chapter_read = coll.ep_status!! + track + } + } + + fun accessToken(code: String): Observable { + return client.newCall(accessTokenRequest(code)).asObservableSuccess().map { netResponse -> + val responseBody = netResponse.body()?.string().orEmpty() + if (responseBody.isEmpty()) { + throw Exception("Null Response") + } + gson.fromJson(responseBody, OAuth::class.java) + } + } + + private fun accessTokenRequest(code: String) = POST(oauthUrl, + body = FormBody.Builder() + .add("grant_type", "authorization_code") + .add("client_id", clientId) + .add("client_secret", clientSecret) + .add("code", code) + .add("redirect_uri", redirectUrl) + .build() + ) + + companion object { + private const val clientId = "bgm10555cda0762e80ca" + private const val clientSecret = "8fff394a8627b4c388cbf349ec865775" + + private const val baseUrl = "https://bangumi.org" + private const val apiUrl = "https://api.bgm.tv" + private const val oauthUrl = "https://bgm.tv/oauth/access_token" + private const val loginUrl = "https://bgm.tv/oauth/authorize" + + private const val redirectUrl = "tachiyomi://bangumi-auth" + private const val baseMangaUrl = "$apiUrl/mangas" + + fun mangaUrl(remoteId: Int): String { + return "$baseMangaUrl/$remoteId" + } + + fun authUrl() = + Uri.parse(loginUrl).buildUpon() + .appendQueryParameter("client_id", clientId) + .appendQueryParameter("response_type", "code") + .appendQueryParameter("redirect_uri", redirectUrl) + .build() + + fun refreshTokenRequest(token: String) = POST(oauthUrl, + body = FormBody.Builder() + .add("grant_type", "refresh_token") + .add("client_id", clientId) + .add("client_secret", clientSecret) + .add("refresh_token", token) + .add("redirect_uri", redirectUrl) + .build()) + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt new file mode 100644 index 000000000..69565f447 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt @@ -0,0 +1,61 @@ +package eu.kanade.tachiyomi.data.track.bangumi + +import com.google.gson.Gson +import okhttp3.FormBody +import okhttp3.Interceptor +import okhttp3.Response + +class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor { + + /** + * OAuth object used for authenticated requests. + */ + private var oauth: OAuth? = bangumi.restoreToken() + + fun addTocken(tocken: String, oidFormBody: FormBody): FormBody { + val newFormBody = FormBody.Builder() + for (i in 0 until oidFormBody.size()) { + newFormBody.add(oidFormBody.name(i), oidFormBody.value(i)) + } + newFormBody.add("access_token", tocken) + return newFormBody.build() + } + + override fun intercept(chain: Interceptor.Chain): Response { + val originalRequest = chain.request() + + val currAuth = oauth ?: throw Exception("Not authenticated with Bangumi") + + if (currAuth.isExpired()) { + val response = chain.proceed(BangumiApi.refreshTokenRequest(currAuth.refresh_token!!)) + if (response.isSuccessful) { + newAuth(gson.fromJson(response.body()!!.string(), OAuth::class.java)) + } else { + response.close() + } + } + + var authRequest = if (originalRequest.method() == "GET") originalRequest.newBuilder() + .header("User-Agent", "Tachiyomi") + .url(originalRequest.url().newBuilder() + .addQueryParameter("access_token", currAuth.access_token).build()) + .build() else originalRequest.newBuilder() + .post(addTocken(currAuth.access_token, originalRequest.body() as FormBody)) + .header("User-Agent", "Tachiyomi") + .build() + + return chain.proceed(authRequest) + } + + fun newAuth(oauth: OAuth?) { + this.oauth = if (oauth == null) null else OAuth( + oauth.access_token, + oauth.token_type, + System.currentTimeMillis() / 1000, + oauth.expires_in, + oauth.refresh_token, + this.oauth?.user_id) + + bangumi.saveToken(oauth) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiModels.kt new file mode 100644 index 000000000..83b9ce305 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiModels.kt @@ -0,0 +1,22 @@ +package eu.kanade.tachiyomi.data.track.bangumi + +import eu.kanade.tachiyomi.data.database.models.Track + +fun Track.toBangumiStatus() = when (status) { + Bangumi.READING -> "do" + Bangumi.COMPLETED -> "collect" + Bangumi.ON_HOLD -> "on_hold" + Bangumi.DROPPED -> "dropped" + Bangumi.PLANNING -> "wish" + else -> throw NotImplementedError("Unknown status") +} + +fun toTrackStatus(status: String) = when (status) { + "do" -> Bangumi.READING + "collect" -> Bangumi.COMPLETED + "on_hold" -> Bangumi.ON_HOLD + "dropped" -> Bangumi.DROPPED + "wish" -> Bangumi.PLANNING + + else -> throw Exception("Unknown status") +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Collection.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Collection.kt new file mode 100644 index 000000000..732676bf3 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Collection.kt @@ -0,0 +1,13 @@ +package eu.kanade.tachiyomi.data.track.bangumi + +data class Collection( + val `private`: Int? = 0, + val comment: String? = "", + val ep_status: Int? = 0, + val lasttouch: Int? = 0, + val rating: Int? = 0, + val status: Status? = Status(), + val tag: List? = listOf(), + val user: User? = User(), + val vol_status: Int? = 0 +) \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/OAuth.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/OAuth.kt new file mode 100644 index 000000000..68dc7e5c4 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/OAuth.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.data.track.bangumi + +data class OAuth( + val access_token: String, + val token_type: String, + val created_at: Long, + val expires_in: Long, + val refresh_token: String?, + val user_id: Long? +) { + + // Access token refersh before expired + fun isExpired() = (System.currentTimeMillis() / 1000) > (created_at + expires_in - 3600) + +} + diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Status.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Status.kt new file mode 100644 index 000000000..78e22e882 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Status.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.data.track.bangumi + +data class Status( + val id: Int? = 0, + val name: String? = "", + val type: String? = "" +) \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/User.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/User.kt new file mode 100644 index 000000000..808e4860a --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/User.kt @@ -0,0 +1,11 @@ +package eu.kanade.tachiyomi.data.track.bangumi + +data class User( + val avatar: Avatar? = Avatar(), + val id: Int? = 0, + val nickname: String? = "", + val sign: String? = "", + val url: String? = "", + val usergroup: Int? = 0, + val username: String? = "" +) \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/BangumiLoginActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/BangumiLoginActivity.kt new file mode 100644 index 000000000..5654b4efa --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/BangumiLoginActivity.kt @@ -0,0 +1,50 @@ +package eu.kanade.tachiyomi.ui.setting + +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.view.Gravity.CENTER +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import android.widget.FrameLayout +import android.widget.ProgressBar +import eu.kanade.tachiyomi.data.track.TrackManager +import eu.kanade.tachiyomi.ui.main.MainActivity +import rx.android.schedulers.AndroidSchedulers +import rx.schedulers.Schedulers +import uy.kohesive.injekt.injectLazy + +class BangumiLoginActivity : AppCompatActivity() { + + private val trackManager: TrackManager by injectLazy() + + override fun onCreate(savedState: Bundle?) { + super.onCreate(savedState) + + val view = ProgressBar(this) + setContentView(view, FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, CENTER)) + + val code = intent.data?.getQueryParameter("code") + if (code != null) { + trackManager.bangumi.login(code) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + returnToSettings() + }, { + returnToSettings() + }) + } else { + trackManager.bangumi.logout() + returnToSettings() + } + } + + private fun returnToSettings() { + finish() + + val intent = Intent(this, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + startActivity(intent) + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt index 2a8f1f73a..55bc47059 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt @@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.anilist.AnilistApi import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi +import eu.kanade.tachiyomi.data.track.bangumi.BangumiApi import eu.kanade.tachiyomi.util.getResourceColor import eu.kanade.tachiyomi.widget.preference.LoginPreference import eu.kanade.tachiyomi.widget.preference.TrackLoginDialog @@ -63,6 +64,15 @@ class SettingsTrackingController : SettingsController(), tabsIntent.launchUrl(activity, ShikimoriApi.authUrl()) } } + trackPreference(trackManager.bangumi) { + onClick { + val tabsIntent = CustomTabsIntent.Builder() + .setToolbarColor(context.getResourceColor(R.attr.colorPrimary)) + .build() + tabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) + tabsIntent.launchUrl(activity, BangumiApi.authUrl()) + } + } } } diff --git a/app/src/main/res/drawable-xxxhdpi/bangumi.png b/app/src/main/res/drawable-xxxhdpi/bangumi.png new file mode 100644 index 0000000000000000000000000000000000000000..412a28ba1c2a3ab9cb48d057d9bc9dfcedafc0ee GIT binary patch literal 6388 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 UxXem0tX~FbsOqX