Improve tracking search results (#1178)

* initial commit
changed tracking info screen
added ability to click logo to launch website

* added publishing status and type to description.
adjusted layout some

* added start date to track info

* tweaked layout

* tweaked layout

* tweaked layout

* code review changes

* code review changes part 2

* code review changes
This commit is contained in:
Carlos 2018-02-17 07:04:49 -05:00 committed by inorichi
parent aa7dfb7bee
commit 40b222f8bc
25 changed files with 466 additions and 107 deletions

View File

@ -17,7 +17,7 @@ class DbOpenHelper(context: Context)
/** /**
* Version of the database. * Version of the database.
*/ */
const val DATABASE_VERSION = 5 const val DATABASE_VERSION = 6
} }
override fun onCreate(db: SQLiteDatabase) = with(db) { override fun onCreate(db: SQLiteDatabase) = with(db) {
@ -54,6 +54,9 @@ class DbOpenHelper(context: Context)
if (oldVersion < 5) { if (oldVersion < 5) {
db.execSQL(ChapterTable.addScanlator) db.execSQL(ChapterTable.addScanlator)
} }
if (oldVersion < 6) {
db.execSQL(TrackTable.addTrackingUrl)
}
} }
override fun onConfigure(db: SQLiteDatabase) { override fun onConfigure(db: SQLiteDatabase) {

View File

@ -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_SYNC_ID
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_TITLE 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_TOTAL_CHAPTERS
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_TRACKING_URL
import eu.kanade.tachiyomi.data.database.tables.TrackTable.TABLE import eu.kanade.tachiyomi.data.database.tables.TrackTable.TABLE
class TrackTypeMapping : SQLiteTypeMapping<Track>( class TrackTypeMapping : SQLiteTypeMapping<Track>(
@ -40,7 +41,7 @@ class TrackPutResolver : DefaultPutResolver<Track>() {
.whereArgs(obj.id) .whereArgs(obj.id)
.build() .build()
override fun mapToContentValues(obj: Track) = ContentValues(9).apply { override fun mapToContentValues(obj: Track) = ContentValues(10).apply {
put(COL_ID, obj.id) put(COL_ID, obj.id)
put(COL_MANGA_ID, obj.manga_id) put(COL_MANGA_ID, obj.manga_id)
put(COL_SYNC_ID, obj.sync_id) put(COL_SYNC_ID, obj.sync_id)
@ -49,7 +50,9 @@ class TrackPutResolver : DefaultPutResolver<Track>() {
put(COL_LAST_CHAPTER_READ, obj.last_chapter_read) put(COL_LAST_CHAPTER_READ, obj.last_chapter_read)
put(COL_TOTAL_CHAPTERS, obj.total_chapters) put(COL_TOTAL_CHAPTERS, obj.total_chapters)
put(COL_STATUS, obj.status) put(COL_STATUS, obj.status)
put(COL_TRACKING_URL, obj.tracking_url)
put(COL_SCORE, obj.score) put(COL_SCORE, obj.score)
} }
} }
@ -65,6 +68,7 @@ class TrackGetResolver : DefaultGetResolver<Track>() {
total_chapters = cursor.getInt(cursor.getColumnIndex(COL_TOTAL_CHAPTERS)) total_chapters = cursor.getInt(cursor.getColumnIndex(COL_TOTAL_CHAPTERS))
status = cursor.getInt(cursor.getColumnIndex(COL_STATUS)) status = cursor.getInt(cursor.getColumnIndex(COL_STATUS))
score = cursor.getFloat(cursor.getColumnIndex(COL_SCORE)) score = cursor.getFloat(cursor.getColumnIndex(COL_SCORE))
tracking_url = cursor.getString(cursor.getColumnIndex(COL_TRACKING_URL))
} }
} }

View File

@ -22,6 +22,8 @@ interface Track : Serializable {
var status: Int var status: Int
var tracking_url: String
fun copyPersonalFrom(other: Track) { fun copyPersonalFrom(other: Track) {
last_chapter_read = other.last_chapter_read last_chapter_read = other.last_chapter_read
score = other.score score = other.score
@ -29,7 +31,6 @@ interface Track : Serializable {
} }
companion object { companion object {
fun create(serviceId: Int): Track = TrackImpl().apply { fun create(serviceId: Int): Track = TrackImpl().apply {
sync_id = serviceId sync_id = serviceId
} }

View File

@ -20,6 +20,8 @@ class TrackImpl : Track {
override var status: Int = 0 override var status: Int = 0
override lateinit var tracking_url: String
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other == null || javaClass != other.javaClass) return false if (other == null || javaClass != other.javaClass) return false

View File

@ -22,6 +22,8 @@ object TrackTable {
const val COL_TOTAL_CHAPTERS = "total_chapters" const val COL_TOTAL_CHAPTERS = "total_chapters"
const val COL_TRACKING_URL = "remote_url"
val createTableQuery: String val createTableQuery: String
get() = """CREATE TABLE $TABLE( get() = """CREATE TABLE $TABLE(
$COL_ID INTEGER NOT NULL PRIMARY KEY, $COL_ID INTEGER NOT NULL PRIMARY KEY,
@ -33,9 +35,12 @@ object TrackTable {
$COL_TOTAL_CHAPTERS INTEGER NOT NULL, $COL_TOTAL_CHAPTERS INTEGER NOT NULL,
$COL_STATUS INTEGER NOT NULL, $COL_STATUS INTEGER NOT NULL,
$COL_SCORE FLOAT NOT NULL, $COL_SCORE FLOAT NOT NULL,
$COL_TRACKING_URL TEXT NOT NULL,
UNIQUE ($COL_MANGA_ID, $COL_SYNC_ID) ON CONFLICT REPLACE, UNIQUE ($COL_MANGA_ID, $COL_SYNC_ID) ON CONFLICT REPLACE,
FOREIGN KEY($COL_MANGA_ID) REFERENCES ${MangaTable.TABLE} (${MangaTable.COL_ID}) FOREIGN KEY($COL_MANGA_ID) REFERENCES ${MangaTable.TABLE} (${MangaTable.COL_ID})
ON DELETE CASCADE ON DELETE CASCADE
)""" )"""
val addTrackingUrl: String
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_TRACKING_URL TEXT DEFAULT ''"
} }

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.track
import android.support.annotation.CallSuper import android.support.annotation.CallSuper
import android.support.annotation.DrawableRes import android.support.annotation.DrawableRes
import eu.kanade.tachiyomi.data.database.models.Track 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.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -44,7 +45,7 @@ abstract class TrackService(val id: Int) {
abstract fun bind(track: Track): Observable<Track> abstract fun bind(track: Track): Observable<Track>
abstract fun search(query: String): Observable<List<Track>> abstract fun search(query: String): Observable<List<TrackSearch>>
abstract fun refresh(track: Track): Observable<Track> abstract fun refresh(track: Track): Observable<Track>

View File

@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import rx.Completable import rx.Completable
import rx.Observable import rx.Observable
@ -120,7 +121,7 @@ class Anilist(private val context: Context, id: Int) : TrackService(id) {
} }
} }
override fun search(query: String): Observable<List<Track>> { override fun search(query: String): Observable<List<TrackSearch>> {
return api.search(query) return api.search(query)
} }

View File

@ -5,6 +5,7 @@ import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.string import com.github.salomonbrys.kotson.string
import com.google.gson.JsonObject import com.google.gson.JsonObject
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -46,7 +47,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
} }
} }
fun search(query: String): Observable<List<Track>> { fun search(query: String): Observable<List<TrackSearch>> {
return rest.search(query, 1) return rest.search(query, 1)
.map { list -> .map { list ->
list.filter { it.type != "Novel" }.map { it.toTrack() } 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 clientSecret = "nlGB5OmgE9YWq5dr3gIDbTQV0C"
private const val clientUrl = "tachiyomi://anilist-auth" private const val clientUrl = "tachiyomi://anilist-auth"
private const val baseUrl = "https://anilist.co/api/" 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() fun authUrl() = Uri.parse("${baseUrl}auth/authorize").buildUpon()
.appendQueryParameter("grant_type", "authorization_code") .appendQueryParameter("grant_type", "authorization_code")

View File

@ -1,21 +1,45 @@
package eu.kanade.tachiyomi.data.track.anilist 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.database.models.Track
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.*
data class ALManga( data class ALManga(
val id: Int, val id: Int,
val title_romaji: String, val title_romaji: String,
val image_url_lge: String,
val description: String,
val type: String, val type: String,
val publishing_status: String,
val start_date_fuzzy: String,
val total_chapters: Int) { val total_chapters: Int) {
fun toTrack() = Track.create(TrackManager.ANILIST).apply { fun toTrack() = TrackSearch.create(TrackManager.ANILIST).apply {
remote_id = this@ALManga.id remote_id = this@ALManga.id
title = title_romaji title = title_romaji
total_chapters = this@ALManga.total_chapters 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() private val preferences: PreferencesHelper by injectLazy()
fun Track.toAnilistScore(): String = when (preferences.anilistScoreType().getOrDefault()) { fun Track.toAnilistScore(): String = when (preferences.anilistScoreType().getOrDefault()) {
// 10 point // 10 point
0 -> (score.toInt() / 10).toString() 0 -> (score.toInt() / 10).toString()
// 100 point // 100 point
1 -> score.toInt().toString() 1 -> score.toInt().toString()
// 5 stars // 5 stars
2 -> when { 2 -> when {
score == 0f -> "0" score == 0f -> "0"
score < 30 -> "1" score < 30 -> "1"
@ -73,14 +97,14 @@ fun Track.toAnilistScore(): String = when (preferences.anilistScoreType().getOrD
score < 90 -> "4" score < 90 -> "4"
else -> "5" else -> "5"
} }
// Smiley // Smiley
3 -> when { 3 -> when {
score == 0f -> "0" score == 0f -> "0"
score <= 30 -> ":(" score <= 30 -> ":("
score <= 60 -> ":|" score <= 60 -> ":|"
else -> ":)" else -> ":)"
} }
// 10 point decimal // 10 point decimal
4 -> (score / 10).toString() 4 -> (score / 10).toString()
else -> throw Exception("Unknown score type") else -> throw Exception("Unknown score type")
} }

View File

@ -6,6 +6,7 @@ import com.google.gson.Gson
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import rx.Completable import rx.Completable
import rx.Observable import rx.Observable
import uy.kohesive.injekt.injectLazy 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<List<Track>> { override fun search(query: String): Observable<List<TrackSearch>> {
return api.search(query) return api.search(query)
} }

View File

@ -4,6 +4,7 @@ import com.github.salomonbrys.kotson.*
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import com.google.gson.JsonObject import com.google.gson.JsonObject
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -27,25 +28,25 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
return Observable.defer { return Observable.defer {
// @formatter:off // @formatter:off
val data = jsonObject( val data = jsonObject(
"type" to "libraryEntries", "type" to "libraryEntries",
"attributes" to jsonObject( "attributes" to jsonObject(
"status" to track.toKitsuStatus(), "status" to track.toKitsuStatus(),
"progress" to track.last_chapter_read "progress" to track.last_chapter_read
),
"relationships" to jsonObject(
"user" to jsonObject(
"data" to jsonObject(
"id" to userId,
"type" to "users"
)
), ),
"media" to jsonObject( "relationships" to jsonObject(
"data" to jsonObject( "user" to jsonObject(
"id" to track.remote_id, "data" to jsonObject(
"type" to "manga" "id" to userId,
) "type" to "users"
)
),
"media" to jsonObject(
"data" to jsonObject(
"id" to track.remote_id,
"type" to "manga"
)
)
) )
)
) )
// @formatter:on // @formatter:on
@ -61,13 +62,13 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
return Observable.defer { return Observable.defer {
// @formatter:off // @formatter:off
val data = jsonObject( val data = jsonObject(
"type" to "libraryEntries", "type" to "libraryEntries",
"id" to track.remote_id, "id" to track.remote_id,
"attributes" to jsonObject( "attributes" to jsonObject(
"status" to track.toKitsuStatus(), "status" to track.toKitsuStatus(),
"progress" to track.last_chapter_read, "progress" to track.last_chapter_read,
"ratingTwenty" to track.toKitsuScore() "ratingTwenty" to track.toKitsuScore()
) )
) )
// @formatter:on // @formatter:on
@ -76,7 +77,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
} }
} }
fun search(query: String): Observable<List<Track>> { fun search(query: String): Observable<List<TrackSearch>> {
return rest.search(query) return rest.search(query)
.map { json -> .map { json ->
val data = json["data"].array 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 clientSecret = "54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151"
private const val baseUrl = "https://kitsu.io/api/edge/" private const val baseUrl = "https://kitsu.io/api/edge/"
private const val loginUrl = "https://kitsu.io/api/" 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", fun refreshTokenRequest(token: String) = POST("${loginUrl}oauth/token",

View File

@ -5,24 +5,35 @@ import com.github.salomonbrys.kotson.*
import com.google.gson.JsonObject import com.google.gson.JsonObject
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.model.TrackSearch
open class KitsuManga(obj: JsonObject) { open class KitsuManga(obj: JsonObject) {
val id by obj.byInt val id by obj.byInt
val canonicalTitle by obj["attributes"].byString val canonicalTitle by obj["attributes"].byString
val chapterCount = obj["attributes"].obj.get("chapterCount").nullInt 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 @CallSuper
open fun toTrack() = Track.create(TrackManager.KITSU).apply { open fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply {
remote_id = this@KitsuManga.id remote_id = this@KitsuManga.id
title = canonicalTitle title = canonicalTitle
total_chapters = chapterCount ?: 0 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) { class KitsuLibManga(obj: JsonObject, manga: JsonObject) : KitsuManga(manga) {
val remoteId by obj.byInt("id") 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 ratingTwenty = obj["attributes"].obj.get("ratingTwenty").nullString
val progress by obj["attributes"].byInt val progress by obj["attributes"].byInt

View File

@ -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
}
}
}

View File

@ -4,6 +4,7 @@ import android.content.Context
import android.graphics.Color import android.graphics.Color
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import rx.Completable import rx.Completable
import rx.Observable import rx.Observable
@ -81,7 +82,7 @@ class Myanimelist(private val context: Context, id: Int) : TrackService(id) {
} }
} }
override fun search(query: String): Observable<List<Track>> { override fun search(query: String): Observable<List<TrackSearch>> {
return api.search(query, getUsername()) return api.search(query, getUsername())
} }

View File

@ -4,6 +4,7 @@ import android.net.Uri
import android.util.Xml import android.util.Xml
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager 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.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservable import eu.kanade.tachiyomi.network.asObservable
@ -12,6 +13,7 @@ import eu.kanade.tachiyomi.util.selectInt
import eu.kanade.tachiyomi.util.selectText import eu.kanade.tachiyomi.util.selectText
import okhttp3.* import okhttp3.*
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.parser.Parser
import org.xmlpull.v1.XmlSerializer import org.xmlpull.v1.XmlSerializer
import rx.Observable import rx.Observable
import java.io.StringWriter import java.io.StringWriter
@ -36,7 +38,7 @@ class MyanimelistApi(private val client: OkHttpClient, username: String, passwor
} }
} }
fun search(query: String, username: String): Observable<List<Track>> { fun search(query: String, username: String): Observable<List<TrackSearch>> {
return if (query.startsWith(PREFIX_MY)) { return if (query.startsWith(PREFIX_MY)) {
val realQuery = query.substring(PREFIX_MY.length).toLowerCase().trim() val realQuery = query.substring(PREFIX_MY.length).toLowerCase().trim()
getList(username) getList(username)
@ -46,34 +48,42 @@ class MyanimelistApi(private val client: OkHttpClient, username: String, passwor
} else { } else {
client.newCall(GET(getSearchUrl(query), headers)) client.newCall(GET(getSearchUrl(query), headers))
.asObservable() .asObservable()
.map { Jsoup.parse(it.body()!!.string()) } .map { Jsoup.parse(Parser.unescapeEntities(it.body()!!.string(), false), "", Parser.xmlParser()) }
.flatMap { Observable.from(it.select("entry")) } .flatMap { Observable.from(it.select("entry")) }
.filter { it.select("type").text() != "Novel" } .filter { it.select("type").text() != "Novel" }
.map { .map {
Track.create(TrackManager.MYANIMELIST).apply { TrackSearch.create(TrackManager.MYANIMELIST).apply {
title = it.selectText("title")!! title = it.selectText("title")!!
remote_id = it.selectInt("id") remote_id = it.selectInt("id")
total_chapters = it.selectInt("chapters") 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() .toList()
} }
} }
fun getList(username: String): Observable<List<Track>> { fun getList(username: String): Observable<List<TrackSearch>> {
return client return client
.newCall(GET(getListUrl(username), headers)) .newCall(GET(getListUrl(username), headers))
.asObservable() .asObservable()
.map { Jsoup.parse(it.body()!!.string()) } .map { Jsoup.parse(Parser.unescapeEntities(it.body()!!.string(), false), "", Parser.xmlParser()) }
.flatMap { Observable.from(it.select("manga")) } .flatMap { Observable.from(it.select("manga")) }
.map { .map {
Track.create(TrackManager.MYANIMELIST).apply { TrackSearch.create(TrackManager.MYANIMELIST).apply {
title = it.selectText("series_title")!! title = it.selectText("series_title")!!
remote_id = it.selectInt("series_mangadb_id") remote_id = it.selectInt("series_mangadb_id")
last_chapter_read = it.selectInt("my_read_chapters") last_chapter_read = it.selectInt("my_read_chapters")
status = it.selectInt("my_status") status = it.selectInt("my_status")
score = it.selectInt("my_score").toFloat() score = it.selectInt("my_score").toFloat()
total_chapters = it.selectInt("series_chapters") total_chapters = it.selectInt("series_chapters")
cover_url = it.selectText("series_image")!!
tracking_url = MyanimelistApi.mangaUrl(remote_id)
} }
} }
.toList() .toList()
@ -176,6 +186,11 @@ class MyanimelistApi(private val client: OkHttpClient, username: String, passwor
companion object { companion object {
const val baseUrl = "https://myanimelist.net" 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 ENTRY_TAG = "entry"
private val CHAPTER_TAG = "chapter" private val CHAPTER_TAG = "chapter"

View File

@ -15,7 +15,7 @@ class TrackAdapter(controller: TrackController) : RecyclerView.Adapter<TrackHold
} }
} }
val rowClickListener: OnRowClickListener = controller val rowClickListener: OnClickListener = controller
fun getItem(index: Int): TrackItem? { fun getItem(index: Int): TrackItem? {
return items.getOrNull(index) return items.getOrNull(index)
@ -34,7 +34,8 @@ class TrackAdapter(controller: TrackController) : RecyclerView.Adapter<TrackHold
holder.bind(items[position]) holder.bind(items[position])
} }
interface OnRowClickListener { interface OnClickListener {
fun onLogoClick(position: Int)
fun onTitleClick(position: Int) fun onTitleClick(position: Int)
fun onStatusClick(position: Int) fun onStatusClick(position: Int)
fun onChaptersClick(position: Int) fun onChaptersClick(position: Int)

View File

@ -1,19 +1,22 @@
package eu.kanade.tachiyomi.ui.manga.track package eu.kanade.tachiyomi.ui.manga.track
import android.content.Intent
import android.net.Uri
import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.jakewharton.rxbinding.support.v4.widget.refreshes import com.jakewharton.rxbinding.support.v4.widget.refreshes
import eu.kanade.tachiyomi.R 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.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.toast import eu.kanade.tachiyomi.util.toast
import kotlinx.android.synthetic.main.track_controller.* import kotlinx.android.synthetic.main.track_controller.*
import timber.log.Timber
class TrackController : NucleusController<TrackPresenter>(), class TrackController : NucleusController<TrackPresenter>(),
TrackAdapter.OnRowClickListener, TrackAdapter.OnClickListener,
SetTrackStatusDialog.Listener, SetTrackStatusDialog.Listener,
SetTrackChaptersDialog.Listener, SetTrackChaptersDialog.Listener,
SetTrackScoreDialog.Listener { SetTrackScoreDialog.Listener {
@ -58,12 +61,13 @@ class TrackController : NucleusController<TrackPresenter>(),
(parentController as? MangaController)?.setTrackingIcon(atLeastOneLink) (parentController as? MangaController)?.setTrackingIcon(atLeastOneLink)
} }
fun onSearchResults(results: List<Track>) { fun onSearchResults(results: List<TrackSearch>) {
getSearchDialog()?.onSearchResults(results) getSearchDialog()?.onSearchResults(results)
} }
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
fun onSearchResultsError(error: Throwable) { fun onSearchResultsError(error: Throwable) {
Timber.e(error)
getSearchDialog()?.onSearchResultsError() getSearchDialog()?.onSearchResultsError()
} }
@ -80,6 +84,16 @@ class TrackController : NucleusController<TrackPresenter>(),
activity?.toast(error.message) 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) { override fun onTitleClick(position: Int) {
val item = adapter?.getItem(position) ?: return val item = adapter?.getItem(position) ?: return
TrackSearchDialog(this, item.service).showDialog(router, TAG_SEARCH_CONTROLLER) TrackSearchDialog(this, item.service).showDialog(router, TAG_SEARCH_CONTROLLER)

View File

@ -7,9 +7,10 @@ import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder
import kotlinx.android.synthetic.main.track_item.* import kotlinx.android.synthetic.main.track_item.*
class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) { class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
init { init {
val listener = adapter.rowClickListener val listener = adapter.rowClickListener
logo_container.setOnClickListener { listener.onLogoClick(adapterPosition) }
title_container.setOnClickListener { listener.onTitleClick(adapterPosition) } title_container.setOnClickListener { listener.onTitleClick(adapterPosition) }
status_container.setOnClickListener { listener.onStatusClick(adapterPosition) } status_container.setOnClickListener { listener.onStatusClick(adapterPosition) }
chapters_container.setOnClickListener { listener.onChaptersClick(adapterPosition) } chapters_container.setOnClickListener { listener.onChaptersClick(adapterPosition) }
@ -21,7 +22,7 @@ class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
fun bind(item: TrackItem) { fun bind(item: TrackItem) {
val track = item.track val track = item.track
track_logo.setImageResource(item.service.getLogo()) track_logo.setImageResource(item.service.getLogo())
logo.setBackgroundColor(item.service.getLogoColor()) logo_container.setBackgroundColor(item.service.getLogoColor())
if (track != null) { if (track != null) {
track_title.setTextAppearance(itemView.context, R.style.TextAppearance_Regular_Body1_Secondary) track_title.setTextAppearance(itemView.context, R.style.TextAppearance_Regular_Body1_Secondary)
track_title.setAllCaps(false) track_title.setAllCaps(false)

View File

@ -16,6 +16,7 @@ import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
class TrackPresenter( class TrackPresenter(
val manga: Manga, val manga: Manga,
preferences: PreferencesHelper = Injekt.get(), preferences: PreferencesHelper = Injekt.get(),

View File

@ -4,14 +4,17 @@ import android.content.Context
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import com.bumptech.glide.load.engine.DiskCacheStrategy
import eu.kanade.tachiyomi.R 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 eu.kanade.tachiyomi.util.inflate
import kotlinx.android.synthetic.main.track_search_item.view.* import kotlinx.android.synthetic.main.track_search_item.view.*
import java.util.* import java.util.*
class TrackSearchAdapter(context: Context) class TrackSearchAdapter(context: Context)
: ArrayAdapter<Track>(context, R.layout.track_search_item, ArrayList<Track>()) { : ArrayAdapter<TrackSearch>(context, R.layout.track_search_item, ArrayList<TrackSearch>()) {
override fun getView(position: Int, view: View?, parent: ViewGroup): View { override fun getView(position: Int, view: View?, parent: ViewGroup): View {
var v = view var v = view
@ -30,7 +33,7 @@ class TrackSearchAdapter(context: Context)
return v return v
} }
fun setItems(syncs: List<Track>) { fun setItems(syncs: List<TrackSearch>) {
setNotifyOnChange(false) setNotifyOnChange(false)
clear() clear()
addAll(syncs) addAll(syncs)
@ -39,8 +42,40 @@ class TrackSearchAdapter(context: Context)
class TrackSearchHolder(private val view: View) { class TrackSearchHolder(private val view: View) {
fun onSetValues(track: Track) { fun onSetValues(track: TrackSearch) {
view.track_search_title.text = track.title 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
}
}
} }
} }

View File

@ -8,6 +8,7 @@ import com.jakewharton.rxbinding.widget.itemClicks
import com.jakewharton.rxbinding.widget.textChanges import com.jakewharton.rxbinding.widget.textChanges
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track 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.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
@ -114,11 +115,10 @@ class TrackSearchDialog : DialogController {
val view = dialogView ?: return val view = dialogView ?: return
view.progress.visibility = View.VISIBLE view.progress.visibility = View.VISIBLE
view.track_search_list.visibility = View.GONE view.track_search_list.visibility = View.GONE
trackController.presenter.search(query, service) trackController.presenter.search(query, service)
} }
fun onSearchResults(results: List<Track>) { fun onSearchResults(results: List<TrackSearch>) {
selectedItem = null selectedItem = null
val view = dialogView ?: return val view = dialogView ?: return
view.progress.visibility = View.GONE view.progress.visibility = View.GONE

View File

@ -11,12 +11,13 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<FrameLayout <FrameLayout
android:id="@+id/logo" android:id="@+id/logo_container"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
android:clickable="true"
tools:background="#2E51A2"> tools:background="#2E51A2">
<ImageView <ImageView
@ -35,7 +36,7 @@
android:background="?attr/selectable_list_drawable" android:background="?attr/selectable_list_drawable"
android:clickable="true" android:clickable="true"
android:padding="16dp" android:padding="16dp"
app:layout_constraintLeft_toRightOf="@+id/logo" app:layout_constraintLeft_toRightOf="@+id/logo_container"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
@ -68,7 +69,7 @@
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:background="?android:attr/divider" 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_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title_container" /> app:layout_constraintTop_toBottomOf="@+id/title_container" />
@ -79,7 +80,7 @@
android:background="?attr/selectable_list_drawable" android:background="?attr/selectable_list_drawable"
android:clickable="true" android:clickable="true"
android:padding="16dp" android:padding="16dp"
app:layout_constraintLeft_toRightOf="@+id/logo" app:layout_constraintLeft_toRightOf="@+id/logo_container"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider1"> app:layout_constraintTop_toBottomOf="@+id/divider1">
@ -110,7 +111,7 @@
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:background="?android:attr/divider" 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_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/status_container" /> app:layout_constraintTop_toBottomOf="@+id/status_container" />
@ -121,7 +122,7 @@
android:background="?attr/selectable_list_drawable" android:background="?attr/selectable_list_drawable"
android:clickable="true" android:clickable="true"
android:padding="16dp" android:padding="16dp"
app:layout_constraintLeft_toRightOf="@+id/logo" app:layout_constraintLeft_toRightOf="@+id/logo_container"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider2"> app:layout_constraintTop_toBottomOf="@+id/divider2">
@ -152,7 +153,7 @@
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:background="?android:attr/divider" 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_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/chapters_container" /> app:layout_constraintTop_toBottomOf="@+id/chapters_container" />
@ -163,7 +164,7 @@
android:background="?attr/selectable_list_drawable" android:background="?attr/selectable_list_drawable"
android:clickable="true" android:clickable="true"
android:padding="16dp" android:padding="16dp"
app:layout_constraintLeft_toRightOf="@+id/logo" app:layout_constraintLeft_toRightOf="@+id/logo_container"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider3"> app:layout_constraintTop_toBottomOf="@+id/divider3">

View File

@ -1,47 +1,64 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <EditText
android:id="@+id/track_search"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="wrap_content"
android:gravity="center" android:hint="@string/title"
android:orientation="horizontal" android:layout_marginEnd="16dp"
android:paddingLeft="@dimen/margin_left" android:layout_marginStart="16dp"
android:paddingRight="@dimen/margin_right"> app:layout_constraintTop_toTopOf="parent"/>
<EditText
android:id="@+id/track_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/title"/>
</LinearLayout>
<ProgressBar <ProgressBar
android:id="@+id/progress" android:id="@+id/progress"
style="?android:attr/progressBarStyle" style="?android:attr/progressBarStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="fill_parent" android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal" android:layout_marginBottom="32dp"
android:paddingBottom="32dp" android:layout_marginTop="32dp"
android:paddingTop="32dp" android:visibility="gone"
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"/>
<ListView <ListView
android:id="@+id/track_search_list" android:id="@+id/track_search_list"
style="@style/Theme.Widget.CardView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="0dp"
android:layout_marginTop="40dp"
android:choiceMode="singleChoice" android:choiceMode="singleChoice"
android:clipToPadding="false" android:clipToPadding="false"
android:divider="@null" android:divider="@null"
android:dividerHeight="0dp" android:dividerHeight="10dp"
android:footerDividersEnabled="true"
android:headerDividersEnabled="true"
android:listSelector="?attr/selectable_list_drawable" android:listSelector="?attr/selectable_list_drawable"
android:scrollbarStyle="outsideOverlay" android:paddingBottom="4dp"
android:visibility="gone"/> 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"/>
</LinearLayout> <View
android:id="@+id/divider1"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/divider"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/track_search_list"/>
</android.support.constraint.ConstraintLayout>

View File

@ -1,14 +1,155 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.support.v7.widget.CardView android:id="@+id/cv_manga"
android:layout_width="match_parent" style="@style/Theme.Widget.CardView.Item"
android:layout_height="wrap_content" xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?attr/selectable_list_drawable"> xmlns:tools="http://schemas.android.com/tools"
android:padding="0dp">
<TextView <android.support.constraint.ConstraintLayout
android:id="@+id/track_search_title" android:id="@+id/linearLayout"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="216dp"
android:padding="10dp"/> android:background="?attr/selectable_list_drawable"
android:orientation="horizontal">
>
</LinearLayout> <ImageView
android:id="@+id/track_search_cover"
android:layout_width="135dp"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:contentDescription="@string/description_cover"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
tools:src="@drawable/branded_logo_icon"/>
<TextView
android:id="@+id/track_search_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:maxLines="3"
android:textAppearance="@style/TextAppearance.Regular.Body1.Bold"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/track_search_cover"
app:layout_constraintTop_toTopOf="parent"
tools:text="One Piece"/>
<TextView
android:id="@+id/track_search_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:maxLines="1"
android:text="@string/track_type"
android:textAppearance="@style/TextAppearance.Regular.Body1.Bold"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/track_search_cover"
app:layout_constraintTop_toBottomOf="@id/track_search_title"
/>
<TextView
android:id="@+id/track_search_type_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Regular.Body1.Secondary"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/track_search_type"
app:layout_constraintTop_toBottomOf="@id/track_search_title"
tools:text="Manga"
/>
<TextView
android:id="@+id/track_search_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:maxLines="1"
android:text="@string/track_start_date"
android:textAppearance="@style/TextAppearance.Regular.Body1.Bold"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/track_search_cover"
app:layout_constraintTop_toBottomOf="@id/track_search_type"
/>
<TextView
android:id="@+id/track_search_start_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Regular.Body1.Secondary"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/track_search_start"
app:layout_constraintTop_toBottomOf="@id/track_search_type"
tools:text="2018-10-01"
/>
<TextView
android:id="@+id/track_search_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:maxLines="1"
android:text="@string/track_status"
android:textAppearance="@style/TextAppearance.Regular.Body1.Bold"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/track_search_cover"
app:layout_constraintTop_toBottomOf="@id/track_search_start"
/>
<TextView
android:id="@+id/track_search_status_result"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Regular.Body1.Secondary"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/track_search_status"
app:layout_constraintTop_toBottomOf="@id/track_search_start"
tools:text="Ongoing"
/>
<TextView
android:id="@+id/track_search_summary"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:ellipsize="end"
android:maxLines="7"
android:textAppearance="@style/TextAppearance.Regular.Body1.Secondary"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/track_search_cover"
app:layout_constraintTop_toBottomOf="@+id/track_search_status"
app:layout_constraintVertical_bias="0.333"
tools:text="This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits "/>
<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="150dp"/>
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>

View File

@ -378,7 +378,7 @@
<string name="download_unread">Download unread</string> <string name="download_unread">Download unread</string>
<string name="confirm_delete_chapters">Are you sure you want to delete selected chapters?</string> <string name="confirm_delete_chapters">Are you sure you want to delete selected chapters?</string>
<!-- MyAnimeList fragment --> <!-- Tracking Screen -->
<string name="manga_tracking_tab">Tracking</string> <string name="manga_tracking_tab">Tracking</string>
<string name="reading">Reading</string> <string name="reading">Reading</string>
<string name="completed">Completed</string> <string name="completed">Completed</string>
@ -388,6 +388,11 @@
<string name="score">Score</string> <string name="score">Score</string>
<string name="title">Title</string> <string name="title">Title</string>
<string name="status">Status</string> <string name="status">Status</string>
<string name="track_status">Status</string>
<string name="track_start_date">Started</string>
<string name="track_type">Type</string>
<string name="track_author">Author</string>
<string name="url_not_set">Manga url is not set please click title and select manga again</string>
<!-- Category activity --> <!-- Category activity -->
<string name="error_category_exists">A category with this name already exists!</string> <string name="error_category_exists">A category with this name already exists!</string>