diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenHelper.kt index f8fd268cf8..5330e1ab52 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenHelper.kt @@ -17,7 +17,7 @@ class DbOpenHelper(context: Context) /** * Version of the database. */ - const val DATABASE_VERSION = 5 + const val DATABASE_VERSION = 6 } override fun onCreate(db: SQLiteDatabase) = with(db) { @@ -54,6 +54,9 @@ class DbOpenHelper(context: Context) if (oldVersion < 5) { db.execSQL(ChapterTable.addScanlator) } + if (oldVersion < 6) { + db.execSQL(TrackTable.addTrackingUrl) + } } override fun onConfigure(db: SQLiteDatabase) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/mappers/TrackTypeMapping.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/mappers/TrackTypeMapping.kt index 3d66b104c4..aaf64f23a1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/mappers/TrackTypeMapping.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/mappers/TrackTypeMapping.kt @@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_STATUS import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_SYNC_ID import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_TITLE import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_TOTAL_CHAPTERS +import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_TRACKING_URL import eu.kanade.tachiyomi.data.database.tables.TrackTable.TABLE class TrackTypeMapping : SQLiteTypeMapping( @@ -40,7 +41,7 @@ class TrackPutResolver : DefaultPutResolver() { .whereArgs(obj.id) .build() - override fun mapToContentValues(obj: Track) = ContentValues(9).apply { + override fun mapToContentValues(obj: Track) = ContentValues(10).apply { put(COL_ID, obj.id) put(COL_MANGA_ID, obj.manga_id) put(COL_SYNC_ID, obj.sync_id) @@ -49,7 +50,9 @@ class TrackPutResolver : DefaultPutResolver() { put(COL_LAST_CHAPTER_READ, obj.last_chapter_read) put(COL_TOTAL_CHAPTERS, obj.total_chapters) put(COL_STATUS, obj.status) + put(COL_TRACKING_URL, obj.tracking_url) put(COL_SCORE, obj.score) + } } @@ -65,6 +68,7 @@ class TrackGetResolver : DefaultGetResolver() { total_chapters = cursor.getInt(cursor.getColumnIndex(COL_TOTAL_CHAPTERS)) status = cursor.getInt(cursor.getColumnIndex(COL_STATUS)) score = cursor.getFloat(cursor.getColumnIndex(COL_SCORE)) + tracking_url = cursor.getString(cursor.getColumnIndex(COL_TRACKING_URL)) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Track.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Track.kt index 5068f899e4..3b883a8747 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Track.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Track.kt @@ -22,6 +22,8 @@ interface Track : Serializable { var status: Int + var tracking_url: String + fun copyPersonalFrom(other: Track) { last_chapter_read = other.last_chapter_read score = other.score @@ -29,7 +31,6 @@ interface Track : Serializable { } companion object { - fun create(serviceId: Int): Track = TrackImpl().apply { sync_id = serviceId } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/TrackImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/TrackImpl.kt index 4ae4723aa8..448aaf554d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/TrackImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/TrackImpl.kt @@ -20,6 +20,8 @@ class TrackImpl : Track { override var status: Int = 0 + override lateinit var tracking_url: String + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || javaClass != other.javaClass) return false diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/TrackTable.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/TrackTable.kt index 94622cc333..79aba55232 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/TrackTable.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/TrackTable.kt @@ -22,6 +22,8 @@ object TrackTable { const val COL_TOTAL_CHAPTERS = "total_chapters" + const val COL_TRACKING_URL = "remote_url" + val createTableQuery: String get() = """CREATE TABLE $TABLE( $COL_ID INTEGER NOT NULL PRIMARY KEY, @@ -33,9 +35,12 @@ object TrackTable { $COL_TOTAL_CHAPTERS INTEGER NOT NULL, $COL_STATUS INTEGER NOT NULL, $COL_SCORE FLOAT NOT NULL, + $COL_TRACKING_URL TEXT NOT NULL, UNIQUE ($COL_MANGA_ID, $COL_SYNC_ID) ON CONFLICT REPLACE, FOREIGN KEY($COL_MANGA_ID) REFERENCES ${MangaTable.TABLE} (${MangaTable.COL_ID}) ON DELETE CASCADE )""" + val addTrackingUrl: String + get() = "ALTER TABLE $TABLE ADD COLUMN $COL_TRACKING_URL TEXT DEFAULT ''" } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt index 1b91e812a3..d3d02517de 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.track import android.support.annotation.CallSuper import android.support.annotation.DrawableRes import eu.kanade.tachiyomi.data.database.models.Track +import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.network.NetworkHelper import okhttp3.OkHttpClient @@ -44,7 +45,7 @@ abstract class TrackService(val id: Int) { abstract fun bind(track: Track): Observable - abstract fun search(query: String): Observable> + abstract fun search(query: String): Observable> abstract fun refresh(track: Track): Observable diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt index 25fafc6a5d..0b71e3e853 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt @@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.track.TrackService +import eu.kanade.tachiyomi.data.track.model.TrackSearch import rx.Completable import rx.Observable @@ -120,7 +121,7 @@ class Anilist(private val context: Context, id: Int) : TrackService(id) { } } - override fun search(query: String): Observable> { + override fun search(query: String): Observable> { return api.search(query) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt index 8f67ddbe88..ee28643526 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt @@ -5,6 +5,7 @@ import com.github.salomonbrys.kotson.int import com.github.salomonbrys.kotson.string import com.google.gson.JsonObject import eu.kanade.tachiyomi.data.database.models.Track +import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.network.POST import okhttp3.FormBody import okhttp3.OkHttpClient @@ -46,7 +47,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { } } - fun search(query: String): Observable> { + fun search(query: String): Observable> { return rest.search(query, 1) .map { list -> list.filter { it.type != "Novel" }.map { it.toTrack() } @@ -140,6 +141,11 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { private const val clientSecret = "nlGB5OmgE9YWq5dr3gIDbTQV0C" private const val clientUrl = "tachiyomi://anilist-auth" private const val baseUrl = "https://anilist.co/api/" + private const val baseMangaUrl = "https://anilist.co/manga/" + + fun mangaUrl(remoteId: Int): String { + return baseMangaUrl + remoteId + } fun authUrl() = Uri.parse("${baseUrl}auth/authorize").buildUpon() .appendQueryParameter("grant_type", "authorization_code") diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt index c4623308fe..38d805ace2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt @@ -1,21 +1,45 @@ package eu.kanade.tachiyomi.data.track.anilist +import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.track.TrackManager +import eu.kanade.tachiyomi.data.track.model.TrackSearch import uy.kohesive.injekt.injectLazy +import java.text.SimpleDateFormat +import java.util.* data class ALManga( val id: Int, val title_romaji: String, + val image_url_lge: String, + val description: String, val type: String, + val publishing_status: String, + val start_date_fuzzy: String, val total_chapters: Int) { - fun toTrack() = Track.create(TrackManager.ANILIST).apply { + fun toTrack() = TrackSearch.create(TrackManager.ANILIST).apply { remote_id = this@ALManga.id title = title_romaji total_chapters = this@ALManga.total_chapters + cover_url = image_url_lge + summary = description + tracking_url = AnilistApi.mangaUrl(remote_id) + publishing_status = this@ALManga.publishing_status + publishing_type = type + if (!start_date_fuzzy.isNullOrBlank()) { + start_date = try { + val inputDf = SimpleDateFormat("yyyyMMdd", Locale.US) + val outputDf = SimpleDateFormat("yyyy-MM-dd", Locale.US) + val date = inputDf.parse(BuildConfig.BUILD_TIME) + outputDf.format(date) + } catch (e: Exception) { + start_date_fuzzy.orEmpty() + } + } + } } @@ -60,11 +84,11 @@ fun Track.toAnilistStatus() = when (status) { private val preferences: PreferencesHelper by injectLazy() fun Track.toAnilistScore(): String = when (preferences.anilistScoreType().getOrDefault()) { - // 10 point +// 10 point 0 -> (score.toInt() / 10).toString() - // 100 point +// 100 point 1 -> score.toInt().toString() - // 5 stars +// 5 stars 2 -> when { score == 0f -> "0" score < 30 -> "1" @@ -73,14 +97,14 @@ fun Track.toAnilistScore(): String = when (preferences.anilistScoreType().getOrD score < 90 -> "4" else -> "5" } - // Smiley +// Smiley 3 -> when { score == 0f -> "0" score <= 30 -> ":(" score <= 60 -> ":|" else -> ":)" } - // 10 point decimal +// 10 point decimal 4 -> (score / 10).toString() else -> throw Exception("Unknown score type") } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt index 5195f5fc0b..c7f4f94dd9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt @@ -6,6 +6,7 @@ import com.google.gson.Gson import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.track.TrackService +import eu.kanade.tachiyomi.data.track.model.TrackSearch import rx.Completable import rx.Observable import uy.kohesive.injekt.injectLazy @@ -96,7 +97,7 @@ class Kitsu(private val context: Context, id: Int) : TrackService(id) { } } - override fun search(query: String): Observable> { + override fun search(query: String): Observable> { return api.search(query) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuApi.kt index 895eca98d7..03226896e5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuApi.kt @@ -4,6 +4,7 @@ import com.github.salomonbrys.kotson.* import com.google.gson.GsonBuilder import com.google.gson.JsonObject import eu.kanade.tachiyomi.data.database.models.Track +import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.network.POST import okhttp3.FormBody import okhttp3.OkHttpClient @@ -27,25 +28,25 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor) return Observable.defer { // @formatter:off val data = jsonObject( - "type" to "libraryEntries", - "attributes" to jsonObject( - "status" to track.toKitsuStatus(), - "progress" to track.last_chapter_read - ), - "relationships" to jsonObject( - "user" to jsonObject( - "data" to jsonObject( - "id" to userId, - "type" to "users" - ) + "type" to "libraryEntries", + "attributes" to jsonObject( + "status" to track.toKitsuStatus(), + "progress" to track.last_chapter_read ), - "media" to jsonObject( - "data" to jsonObject( - "id" to track.remote_id, - "type" to "manga" - ) + "relationships" to jsonObject( + "user" to jsonObject( + "data" to jsonObject( + "id" to userId, + "type" to "users" + ) + ), + "media" to jsonObject( + "data" to jsonObject( + "id" to track.remote_id, + "type" to "manga" + ) + ) ) - ) ) // @formatter:on @@ -61,13 +62,13 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor) return Observable.defer { // @formatter:off val data = jsonObject( - "type" to "libraryEntries", - "id" to track.remote_id, - "attributes" to jsonObject( - "status" to track.toKitsuStatus(), - "progress" to track.last_chapter_read, - "ratingTwenty" to track.toKitsuScore() - ) + "type" to "libraryEntries", + "id" to track.remote_id, + "attributes" to jsonObject( + "status" to track.toKitsuStatus(), + "progress" to track.last_chapter_read, + "ratingTwenty" to track.toKitsuScore() + ) ) // @formatter:on @@ -76,7 +77,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor) } } - fun search(query: String): Observable> { + fun search(query: String): Observable> { return rest.search(query) .map { json -> val data = json["data"].array @@ -186,6 +187,11 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor) private const val clientSecret = "54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151" private const val baseUrl = "https://kitsu.io/api/edge/" private const val loginUrl = "https://kitsu.io/api/" + private const val baseMangaUrl = "https://kitsu.io/manga/" + + fun mangaUrl(remoteId: Int): String { + return baseMangaUrl + remoteId + } fun refreshTokenRequest(token: String) = POST("${loginUrl}oauth/token", diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt index 6498c995fd..107a789241 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt @@ -5,24 +5,35 @@ import com.github.salomonbrys.kotson.* import com.google.gson.JsonObject import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.track.TrackManager +import eu.kanade.tachiyomi.data.track.model.TrackSearch open class KitsuManga(obj: JsonObject) { val id by obj.byInt val canonicalTitle by obj["attributes"].byString val chapterCount = obj["attributes"].obj.get("chapterCount").nullInt - val type = obj["attributes"].obj.get("mangaType").nullString + val type = obj["attributes"].obj.get("mangaType").nullString.orEmpty() + val original by obj["attributes"].obj["posterImage"].byString + val synopsis by obj["attributes"].byString + val startDate = obj["attributes"].obj.get("startDate").nullString.orEmpty() + val status = obj["attributes"].obj.get("status").nullString.orEmpty() @CallSuper - open fun toTrack() = Track.create(TrackManager.KITSU).apply { + open fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply { remote_id = this@KitsuManga.id title = canonicalTitle total_chapters = chapterCount ?: 0 + cover_url = original + summary = synopsis + tracking_url = KitsuApi.mangaUrl(remote_id) + publishing_status = this@KitsuManga.status + publishing_type = type + start_date = startDate.orEmpty() } } class KitsuLibManga(obj: JsonObject, manga: JsonObject) : KitsuManga(manga) { val remoteId by obj.byInt("id") - val status by obj["attributes"].byString + //override val status by obj["attributes"].byString val ratingTwenty = obj["attributes"].obj.get("ratingTwenty").nullString val progress by obj["attributes"].byInt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/model/TrackSearch.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/model/TrackSearch.kt new file mode 100644 index 0000000000..0e701730f4 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/model/TrackSearch.kt @@ -0,0 +1,62 @@ +package eu.kanade.tachiyomi.data.track.model + +import eu.kanade.tachiyomi.data.database.models.Track + +class TrackSearch : Track { + + override var id: Long? = null + + override var manga_id: Long = 0 + + override var sync_id: Int = 0 + + override var remote_id: Int = 0 + + override lateinit var title: String + + override var last_chapter_read: Int = 0 + + override var total_chapters: Int = 0 + + override var score: Float = 0f + + override var status: Int = 0 + + override lateinit var tracking_url: String + + var cover_url: String = "" + + var summary: String = "" + + var publishing_status: String = "" + + var publishing_type: String = "" + + var start_date: String = "" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || javaClass != other.javaClass) return false + + other as Track + + if (manga_id != other.manga_id) return false + if (sync_id != other.sync_id) return false + return remote_id == other.remote_id + } + + override fun hashCode(): Int { + var result = (manga_id xor manga_id.ushr(32)).toInt() + result = 31 * result + sync_id + result = 31 * result + remote_id + return result + } + companion object { + + fun create(serviceId: Int): TrackSearch = TrackSearch().apply { + sync_id = serviceId + } + + } + +} \ No newline at end of file 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 a3a131e5a9..e58bafcf77 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 @@ -4,6 +4,7 @@ import android.content.Context import android.graphics.Color import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Track +import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.TrackService import rx.Completable import rx.Observable @@ -81,7 +82,7 @@ class Myanimelist(private val context: Context, id: Int) : TrackService(id) { } } - override fun search(query: String): Observable> { + override fun search(query: String): Observable> { return api.search(query, getUsername()) } 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 85f85807e9..392ff220b7 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 @@ -4,6 +4,7 @@ import android.net.Uri import android.util.Xml 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.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.asObservable @@ -12,6 +13,7 @@ import eu.kanade.tachiyomi.util.selectInt import eu.kanade.tachiyomi.util.selectText import okhttp3.* import org.jsoup.Jsoup +import org.jsoup.parser.Parser import org.xmlpull.v1.XmlSerializer import rx.Observable import java.io.StringWriter @@ -36,7 +38,7 @@ class MyanimelistApi(private val client: OkHttpClient, username: String, passwor } } - fun search(query: String, username: String): Observable> { + fun search(query: String, username: String): Observable> { return if (query.startsWith(PREFIX_MY)) { val realQuery = query.substring(PREFIX_MY.length).toLowerCase().trim() getList(username) @@ -46,34 +48,42 @@ class MyanimelistApi(private val client: OkHttpClient, username: String, passwor } else { client.newCall(GET(getSearchUrl(query), headers)) .asObservable() - .map { Jsoup.parse(it.body()!!.string()) } + .map { Jsoup.parse(Parser.unescapeEntities(it.body()!!.string(), false), "", Parser.xmlParser()) } .flatMap { Observable.from(it.select("entry")) } .filter { it.select("type").text() != "Novel" } .map { - Track.create(TrackManager.MYANIMELIST).apply { + TrackSearch.create(TrackManager.MYANIMELIST).apply { title = it.selectText("title")!! remote_id = it.selectInt("id") total_chapters = it.selectInt("chapters") + summary = it.selectText("synopsis")!! + cover_url = it.selectText("image")!! + tracking_url = MyanimelistApi.mangaUrl(remote_id) + publishing_status = it.selectText("status")!! + publishing_type = it.selectText("type")!! + start_date = it.selectText("start_date")!! } } .toList() } } - fun getList(username: String): Observable> { + fun getList(username: String): Observable> { return client .newCall(GET(getListUrl(username), headers)) .asObservable() - .map { Jsoup.parse(it.body()!!.string()) } + .map { Jsoup.parse(Parser.unescapeEntities(it.body()!!.string(), false), "", Parser.xmlParser()) } .flatMap { Observable.from(it.select("manga")) } .map { - Track.create(TrackManager.MYANIMELIST).apply { + TrackSearch.create(TrackManager.MYANIMELIST).apply { title = it.selectText("series_title")!! remote_id = it.selectInt("series_mangadb_id") last_chapter_read = it.selectInt("my_read_chapters") status = it.selectInt("my_status") score = it.selectInt("my_score").toFloat() total_chapters = it.selectInt("series_chapters") + cover_url = it.selectText("series_image")!! + tracking_url = MyanimelistApi.mangaUrl(remote_id) } } .toList() @@ -176,6 +186,11 @@ class MyanimelistApi(private val client: OkHttpClient, username: String, passwor companion object { const val baseUrl = "https://myanimelist.net" + const val baseMangaUrl = baseUrl + "/manga/" + + fun mangaUrl(remoteId: Int): String { + return baseMangaUrl + remoteId + } private val ENTRY_TAG = "entry" private val CHAPTER_TAG = "chapter" diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt index bc0b5ec5b8..bb8871097b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt @@ -15,7 +15,7 @@ class TrackAdapter(controller: TrackController) : RecyclerView.Adapter(), - TrackAdapter.OnRowClickListener, + TrackAdapter.OnClickListener, SetTrackStatusDialog.Listener, SetTrackChaptersDialog.Listener, SetTrackScoreDialog.Listener { @@ -58,12 +61,13 @@ class TrackController : NucleusController(), (parentController as? MangaController)?.setTrackingIcon(atLeastOneLink) } - fun onSearchResults(results: List) { + fun onSearchResults(results: List) { getSearchDialog()?.onSearchResults(results) } @Suppress("UNUSED_PARAMETER") fun onSearchResultsError(error: Throwable) { + Timber.e(error) getSearchDialog()?.onSearchResultsError() } @@ -80,6 +84,16 @@ class TrackController : NucleusController(), activity?.toast(error.message) } + override fun onLogoClick(position: Int) { + val track = adapter?.getItem(position)?.track ?: return + + if (track.tracking_url.isNullOrBlank()) { + activity?.toast(R.string.url_not_set) + } else { + activity?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(track.tracking_url))) + } + } + override fun onTitleClick(position: Int) { val item = adapter?.getItem(position) ?: return TrackSearchDialog(this, item.service).showDialog(router, TAG_SEARCH_CONTROLLER) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackHolder.kt index 2996bc9296..2f018f19db 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackHolder.kt @@ -7,9 +7,10 @@ import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder import kotlinx.android.synthetic.main.track_item.* class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) { - + init { val listener = adapter.rowClickListener + logo_container.setOnClickListener { listener.onLogoClick(adapterPosition) } title_container.setOnClickListener { listener.onTitleClick(adapterPosition) } status_container.setOnClickListener { listener.onStatusClick(adapterPosition) } chapters_container.setOnClickListener { listener.onChaptersClick(adapterPosition) } @@ -21,7 +22,7 @@ class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) { fun bind(item: TrackItem) { val track = item.track track_logo.setImageResource(item.service.getLogo()) - logo.setBackgroundColor(item.service.getLogoColor()) + logo_container.setBackgroundColor(item.service.getLogoColor()) if (track != null) { track_title.setTextAppearance(itemView.context, R.style.TextAppearance_Regular_Body1_Secondary) track_title.setAllCaps(false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackPresenter.kt index bac625de35..ac8592ed93 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackPresenter.kt @@ -16,6 +16,7 @@ import rx.schedulers.Schedulers import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get + class TrackPresenter( val manga: Manga, preferences: PreferencesHelper = Injekt.get(), 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 31b8bf89de..fe9e0012c6 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 @@ -4,14 +4,17 @@ import android.content.Context import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter +import com.bumptech.glide.load.engine.DiskCacheStrategy import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.Track +import eu.kanade.tachiyomi.data.track.model.TrackSearch +import eu.kanade.tachiyomi.data.glide.GlideApp +import eu.kanade.tachiyomi.util.gone import eu.kanade.tachiyomi.util.inflate import kotlinx.android.synthetic.main.track_search_item.view.* import java.util.* class TrackSearchAdapter(context: Context) -: ArrayAdapter(context, R.layout.track_search_item, ArrayList()) { + : ArrayAdapter(context, R.layout.track_search_item, ArrayList()) { override fun getView(position: Int, view: View?, parent: ViewGroup): View { var v = view @@ -30,7 +33,7 @@ class TrackSearchAdapter(context: Context) return v } - fun setItems(syncs: List) { + fun setItems(syncs: List) { setNotifyOnChange(false) clear() addAll(syncs) @@ -39,8 +42,40 @@ class TrackSearchAdapter(context: Context) class TrackSearchHolder(private val view: View) { - fun onSetValues(track: Track) { + fun onSetValues(track: TrackSearch) { view.track_search_title.text = track.title + view.track_search_summary.text = track.summary + GlideApp.with(view.context).clear(view.track_search_cover) + if (!track.cover_url.isNullOrEmpty()) { + GlideApp.with(view.context) + .load(track.cover_url) + .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_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 + } + + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchDialog.kt index c9ce8cd663..691800058d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchDialog.kt @@ -8,6 +8,7 @@ import com.jakewharton.rxbinding.widget.itemClicks import com.jakewharton.rxbinding.widget.textChanges import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Track +import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.ui.base.controller.DialogController @@ -114,11 +115,10 @@ class TrackSearchDialog : DialogController { val view = dialogView ?: return view.progress.visibility = View.VISIBLE view.track_search_list.visibility = View.GONE - trackController.presenter.search(query, service) } - fun onSearchResults(results: List) { + fun onSearchResults(results: List) { selectedItem = null val view = dialogView ?: return view.progress.visibility = View.GONE diff --git a/app/src/main/res/layout/track_item.xml b/app/src/main/res/layout/track_item.xml index 49484a42ec..07c05ebcc2 100644 --- a/app/src/main/res/layout/track_item.xml +++ b/app/src/main/res/layout/track_item.xml @@ -11,12 +11,13 @@ android:layout_height="wrap_content"> @@ -68,7 +69,7 @@ android:layout_marginRight="16dp" android:layout_marginStart="16dp" android:background="?android:attr/divider" - app:layout_constraintLeft_toRightOf="@+id/logo" + app:layout_constraintLeft_toRightOf="@+id/logo_container" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/title_container" /> @@ -79,7 +80,7 @@ android:background="?attr/selectable_list_drawable" android:clickable="true" android:padding="16dp" - app:layout_constraintLeft_toRightOf="@+id/logo" + app:layout_constraintLeft_toRightOf="@+id/logo_container" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/divider1"> @@ -110,7 +111,7 @@ android:layout_marginRight="16dp" android:layout_marginStart="16dp" android:background="?android:attr/divider" - app:layout_constraintLeft_toRightOf="@+id/logo" + app:layout_constraintLeft_toRightOf="@+id/logo_container" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/status_container" /> @@ -121,7 +122,7 @@ android:background="?attr/selectable_list_drawable" android:clickable="true" android:padding="16dp" - app:layout_constraintLeft_toRightOf="@+id/logo" + app:layout_constraintLeft_toRightOf="@+id/logo_container" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/divider2"> @@ -152,7 +153,7 @@ android:layout_marginRight="16dp" android:layout_marginStart="16dp" android:background="?android:attr/divider" - app:layout_constraintLeft_toRightOf="@+id/logo" + app:layout_constraintLeft_toRightOf="@+id/logo_container" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/chapters_container" /> @@ -163,7 +164,7 @@ android:background="?attr/selectable_list_drawable" android:clickable="true" android:padding="16dp" - app:layout_constraintLeft_toRightOf="@+id/logo" + app:layout_constraintLeft_toRightOf="@+id/logo_container" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/divider3"> diff --git a/app/src/main/res/layout/track_search_dialog.xml b/app/src/main/res/layout/track_search_dialog.xml index 11d4886a5d..1fdd70ee00 100644 --- a/app/src/main/res/layout/track_search_dialog.xml +++ b/app/src/main/res/layout/track_search_dialog.xml @@ -1,47 +1,64 @@ - - - - - - + android:layout_height="wrap_content" + android:hint="@string/title" + android:layout_marginEnd="16dp" + android:layout_marginStart="16dp" + app:layout_constraintTop_toTopOf="parent"/> + android:layout_height="wrap_content" + android:layout_marginBottom="32dp" + android:layout_marginTop="32dp" + android:visibility="gone" + app:layout_constraintBottom_toTopOf="@id/divider1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/track_search" + tools:visibility="visible"/> + android:paddingBottom="4dp" + android:paddingTop="4dp" + android:scrollbars="none" + android:visibility="gone" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/track_search" + tools:listitem="@layout/track_search_item" + tools:visibility="visible"/> - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/track_search_item.xml b/app/src/main/res/layout/track_search_item.xml index ee0965eac9..51b9a8a9bd 100644 --- a/app/src/main/res/layout/track_search_item.xml +++ b/app/src/main/res/layout/track_search_item.xml @@ -1,14 +1,155 @@ - + - + + > - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ccb7a2aed0..24bbb0fad3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -378,7 +378,7 @@ Download unread Are you sure you want to delete selected chapters? - + Tracking Reading Completed @@ -388,6 +388,11 @@ Score Title Status + Status + Started + Type + Author + Manga url is not set please click title and select manga again A category with this name already exists!