mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-23 00:51:49 +01:00
Add Start/Finish date support for MAL (#2672)
* Started working on MAL support * Added date picker UI * Replaced Date with Calendar * Added MAL remote update functionality * Join url methods listEntryUrl and editUrl * Removed unused methods * Renamed mangaEditPayload to mangaEditPostBody * Moved code to separate method * Uniformed code to project conventions * Removed wildcard import * Moved MyAnimeListTrack to private class * Improved MyAnimeListTrack name * Removed redundant code * Add start/finish date in local database * Fixed format and improved codestyle * Fixed typo and fixed TrackHolder's format * Improved code style * Ran linter * Add database updating methods * Change date format to fit new layout * Review Commits * Improve SetTrackReadingDatesDialog readability * Move private methods after public ones * Fixed SQL error * Fixed remove date button * Updated MaterialDesign methods to latest version * Replaced dismissDialog() with dialog.Dismiss() * Fixed wrong string resource usage.
This commit is contained in:
parent
c967308859
commit
f7c139030f
@ -20,7 +20,7 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||||||
/**
|
/**
|
||||||
* Version of the database.
|
* Version of the database.
|
||||||
*/
|
*/
|
||||||
const val DATABASE_VERSION = 8
|
const val DATABASE_VERSION = 9
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(db: SupportSQLiteDatabase) = with(db) {
|
override fun onCreate(db: SupportSQLiteDatabase) = with(db) {
|
||||||
@ -69,6 +69,10 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||||||
db.execSQL(MangaTable.createLibraryIndexQuery)
|
db.execSQL(MangaTable.createLibraryIndexQuery)
|
||||||
db.execSQL(ChapterTable.createUnreadChaptersIndexQuery)
|
db.execSQL(ChapterTable.createUnreadChaptersIndexQuery)
|
||||||
}
|
}
|
||||||
|
if (oldVersion < 9) {
|
||||||
|
db.execSQL(TrackTable.addStartDate)
|
||||||
|
db.execSQL(TrackTable.addFinishDate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onConfigure(db: SupportSQLiteDatabase) {
|
override fun onConfigure(db: SupportSQLiteDatabase) {
|
||||||
|
@ -11,12 +11,14 @@ import com.pushtorefresh.storio.sqlite.queries.InsertQuery
|
|||||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
||||||
import eu.kanade.tachiyomi.data.database.models.Track
|
import eu.kanade.tachiyomi.data.database.models.Track
|
||||||
import eu.kanade.tachiyomi.data.database.models.TrackImpl
|
import eu.kanade.tachiyomi.data.database.models.TrackImpl
|
||||||
|
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_FINISH_DATE
|
||||||
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_ID
|
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_ID
|
||||||
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_LAST_CHAPTER_READ
|
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_LAST_CHAPTER_READ
|
||||||
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_LIBRARY_ID
|
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_LIBRARY_ID
|
||||||
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_MANGA_ID
|
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_MANGA_ID
|
||||||
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_MEDIA_ID
|
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_MEDIA_ID
|
||||||
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_SCORE
|
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_SCORE
|
||||||
|
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_START_DATE
|
||||||
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_STATUS
|
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
|
||||||
@ -54,6 +56,8 @@ class TrackPutResolver : DefaultPutResolver<Track>() {
|
|||||||
put(COL_STATUS, obj.status)
|
put(COL_STATUS, obj.status)
|
||||||
put(COL_TRACKING_URL, obj.tracking_url)
|
put(COL_TRACKING_URL, obj.tracking_url)
|
||||||
put(COL_SCORE, obj.score)
|
put(COL_SCORE, obj.score)
|
||||||
|
put(COL_START_DATE, obj.started_reading_date)
|
||||||
|
put(COL_FINISH_DATE, obj.finished_reading_date)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,6 +75,8 @@ class TrackGetResolver : DefaultGetResolver<Track>() {
|
|||||||
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))
|
tracking_url = cursor.getString(cursor.getColumnIndex(COL_TRACKING_URL))
|
||||||
|
started_reading_date = cursor.getLong(cursor.getColumnIndex(COL_START_DATE))
|
||||||
|
finished_reading_date = cursor.getLong(cursor.getColumnIndex(COL_FINISH_DATE))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,12 +24,18 @@ interface Track : Serializable {
|
|||||||
|
|
||||||
var status: Int
|
var status: Int
|
||||||
|
|
||||||
|
var started_reading_date: Long
|
||||||
|
|
||||||
|
var finished_reading_date: Long
|
||||||
|
|
||||||
var tracking_url: String
|
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
|
||||||
status = other.status
|
status = other.status
|
||||||
|
started_reading_date = other.started_reading_date
|
||||||
|
finished_reading_date = other.finished_reading_date
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -22,6 +22,10 @@ class TrackImpl : Track {
|
|||||||
|
|
||||||
override var status: Int = 0
|
override var status: Int = 0
|
||||||
|
|
||||||
|
override var started_reading_date: Long = 0
|
||||||
|
|
||||||
|
override var finished_reading_date: Long = 0
|
||||||
|
|
||||||
override var tracking_url: String = ""
|
override var tracking_url: String = ""
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
@ -26,6 +26,10 @@ object TrackTable {
|
|||||||
|
|
||||||
const val COL_TRACKING_URL = "remote_url"
|
const val COL_TRACKING_URL = "remote_url"
|
||||||
|
|
||||||
|
const val COL_START_DATE = "start_date"
|
||||||
|
|
||||||
|
const val COL_FINISH_DATE = "finish_date"
|
||||||
|
|
||||||
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,
|
||||||
@ -39,6 +43,8 @@ object TrackTable {
|
|||||||
$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,
|
$COL_TRACKING_URL TEXT NOT NULL,
|
||||||
|
$COL_START_DATE LONG NOT NULL,
|
||||||
|
$COL_FINISH_DATE LONG 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
|
||||||
@ -49,4 +55,10 @@ object TrackTable {
|
|||||||
|
|
||||||
val addLibraryId: String
|
val addLibraryId: String
|
||||||
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_LIBRARY_ID INTEGER NULL"
|
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_LIBRARY_ID INTEGER NULL"
|
||||||
|
|
||||||
|
val addStartDate: String
|
||||||
|
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_START_DATE LONG NOT NULL DEFAULT 0"
|
||||||
|
|
||||||
|
val addFinishDate: String
|
||||||
|
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_FINISH_DATE LONG NOT NULL DEFAULT 0"
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,9 @@ abstract class TrackService(val id: Int) {
|
|||||||
// Name of the manga sync service to display
|
// Name of the manga sync service to display
|
||||||
abstract val name: String
|
abstract val name: String
|
||||||
|
|
||||||
|
// Application and remote support for reading dates
|
||||||
|
open val supportsReadingDates: Boolean = false
|
||||||
|
|
||||||
@DrawableRes
|
@DrawableRes
|
||||||
abstract fun getLogo(): Int
|
abstract fun getLogo(): Int
|
||||||
|
|
||||||
|
@ -24,6 +24,10 @@ class TrackSearch : Track {
|
|||||||
|
|
||||||
override var status: Int = 0
|
override var status: Int = 0
|
||||||
|
|
||||||
|
override var started_reading_date: Long = 0
|
||||||
|
|
||||||
|
override var finished_reading_date: Long = 0
|
||||||
|
|
||||||
override lateinit var tracking_url: String
|
override lateinit var tracking_url: String
|
||||||
|
|
||||||
var cover_url: String = ""
|
var cover_url: String = ""
|
||||||
|
@ -34,6 +34,8 @@ class MyAnimeList(private val context: Context, id: Int) : TrackService(id) {
|
|||||||
override val name: String
|
override val name: String
|
||||||
get() = "MyAnimeList"
|
get() = "MyAnimeList"
|
||||||
|
|
||||||
|
override val supportsReadingDates: Boolean = true
|
||||||
|
|
||||||
override fun getLogo() = R.drawable.ic_tracker_mal
|
override fun getLogo() = R.drawable.ic_tracker_mal
|
||||||
|
|
||||||
override fun getLogoColor() = Color.rgb(46, 81, 162)
|
override fun getLogoColor() = Color.rgb(46, 81, 162)
|
||||||
|
@ -8,10 +8,15 @@ 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
|
||||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||||
|
import eu.kanade.tachiyomi.util.lang.toCalendar
|
||||||
import eu.kanade.tachiyomi.util.selectInt
|
import eu.kanade.tachiyomi.util.selectInt
|
||||||
import eu.kanade.tachiyomi.util.selectText
|
import eu.kanade.tachiyomi.util.selectText
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.GregorianCalendar
|
||||||
|
import java.util.Locale
|
||||||
import java.util.zip.GZIPInputStream
|
import java.util.zip.GZIPInputStream
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
@ -76,14 +81,29 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
|||||||
|
|
||||||
fun updateLibManga(track: Track): Observable<Track> {
|
fun updateLibManga(track: Track): Observable<Track> {
|
||||||
return Observable.defer {
|
return Observable.defer {
|
||||||
authClient.newCall(POST(url = updateUrl(), body = mangaPostPayload(track)))
|
// Get track data
|
||||||
|
val response = authClient.newCall(GET(url = editPageUrl(track.media_id))).execute()
|
||||||
|
val editData = response.use {
|
||||||
|
val page = Jsoup.parse(it.consumeBody())
|
||||||
|
|
||||||
|
// Extract track data from MAL page
|
||||||
|
extractDataFromEditPage(page).apply {
|
||||||
|
// Apply changes to the just fetched data
|
||||||
|
copyPersonalFrom(track)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update remote
|
||||||
|
authClient.newCall(POST(url = editPageUrl(track.media_id), body = mangaEditPostBody(editData)))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { track }
|
.map {
|
||||||
|
track
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findLibManga(track: Track): Observable<Track?> {
|
fun findLibManga(track: Track): Observable<Track?> {
|
||||||
return authClient.newCall(GET(url = listEntryUrl(track.media_id)))
|
return authClient.newCall(GET(url = editPageUrl(track.media_id)))
|
||||||
.asObservable()
|
.asObservable()
|
||||||
.map { response ->
|
.map { response ->
|
||||||
var libTrack: Track? = null
|
var libTrack: Track? = null
|
||||||
@ -97,6 +117,8 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
|||||||
status = trackForm.select("#add_manga_status > option[selected]").`val`().toInt()
|
status = trackForm.select("#add_manga_status > option[selected]").`val`().toInt()
|
||||||
score = trackForm.select("#add_manga_score > option[selected]").`val`().toFloatOrNull()
|
score = trackForm.select("#add_manga_score > option[selected]").`val`().toFloatOrNull()
|
||||||
?: 0f
|
?: 0f
|
||||||
|
started_reading_date = trackForm.searchDatePicker("#add_manga_start_date")
|
||||||
|
finished_reading_date = trackForm.searchDatePicker("#add_manga_finish_date")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,6 +172,8 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
|||||||
score = it.selectInt("my_score").toFloat()
|
score = it.selectInt("my_score").toFloat()
|
||||||
total_chapters = it.selectInt("manga_chapters")
|
total_chapters = it.selectInt("manga_chapters")
|
||||||
tracking_url = mangaUrl(media_id)
|
tracking_url = mangaUrl(media_id)
|
||||||
|
started_reading_date = it.searchDateXml("my_start_date")
|
||||||
|
finished_reading_date = it.searchDateXml("my_finish_date")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.toList()
|
.toList()
|
||||||
@ -194,6 +218,35 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun extractDataFromEditPage(page: Document): MyAnimeListEditData {
|
||||||
|
val tables = page.select("form#main-form table")
|
||||||
|
|
||||||
|
return MyAnimeListEditData(
|
||||||
|
entry_id = tables[0].select("input[name=entry_id]").`val`(), // Always 0
|
||||||
|
manga_id = tables[0].select("#manga_id").`val`(),
|
||||||
|
status = tables[0].select("#add_manga_status > option[selected]").`val`(),
|
||||||
|
num_read_volumes = tables[0].select("#add_manga_num_read_volumes").`val`(),
|
||||||
|
last_completed_vol = tables[0].select("input[name=last_completed_vol]").`val`(), // Always empty
|
||||||
|
num_read_chapters = tables[0].select("#add_manga_num_read_chapters").`val`(),
|
||||||
|
score = tables[0].select("#add_manga_score > option[selected]").`val`(),
|
||||||
|
start_date_month = tables[0].select("#add_manga_start_date_month > option[selected]").`val`(),
|
||||||
|
start_date_day = tables[0].select("#add_manga_start_date_day > option[selected]").`val`(),
|
||||||
|
start_date_year = tables[0].select("#add_manga_start_date_year > option[selected]").`val`(),
|
||||||
|
finish_date_month = tables[0].select("#add_manga_finish_date_month > option[selected]").`val`(),
|
||||||
|
finish_date_day = tables[0].select("#add_manga_finish_date_day > option[selected]").`val`(),
|
||||||
|
finish_date_year = tables[0].select("#add_manga_finish_date_year > option[selected]").`val`(),
|
||||||
|
tags = tables[1].select("#add_manga_tags").`val`(),
|
||||||
|
priority = tables[1].select("#add_manga_priority > option[selected]").`val`(),
|
||||||
|
storage_type = tables[1].select("#add_manga_storage_type > option[selected]").`val`(),
|
||||||
|
num_retail_volumes = tables[1].select("#add_manga_num_retail_volumes").`val`(),
|
||||||
|
num_read_times = tables[1].select("#add_manga_num_read_times").`val`(),
|
||||||
|
reread_value = tables[1].select("#add_manga_reread_value > option[selected]").`val`(),
|
||||||
|
comments = tables[1].select("#add_manga_comments").`val`(),
|
||||||
|
is_asked_to_discuss = tables[1].select("#add_manga_is_asked_to_discuss > option[selected]").`val`(),
|
||||||
|
sns_post_type = tables[1].select("#add_manga_sns_post_type > option[selected]").`val`()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CSRF = "csrf_token"
|
const val CSRF = "csrf_token"
|
||||||
|
|
||||||
@ -228,19 +281,15 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
|||||||
.appendQueryParameter("go", "export")
|
.appendQueryParameter("go", "export")
|
||||||
.toString()
|
.toString()
|
||||||
|
|
||||||
private fun updateUrl() = Uri.parse(baseModifyListUrl).buildUpon()
|
private fun editPageUrl(mediaId: Int) = Uri.parse(baseModifyListUrl).buildUpon()
|
||||||
.appendPath("edit.json")
|
.appendPath(mediaId.toString())
|
||||||
|
.appendPath("edit")
|
||||||
.toString()
|
.toString()
|
||||||
|
|
||||||
private fun addUrl() = Uri.parse(baseModifyListUrl).buildUpon()
|
private fun addUrl() = Uri.parse(baseModifyListUrl).buildUpon()
|
||||||
.appendPath("add.json")
|
.appendPath("add.json")
|
||||||
.toString()
|
.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 {
|
private fun loginPostBody(username: String, password: String, csrf: String): RequestBody {
|
||||||
return FormBody.Builder()
|
return FormBody.Builder()
|
||||||
.add("user_name", username)
|
.add("user_name", username)
|
||||||
@ -269,6 +318,53 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
|||||||
return body.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
|
return body.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun mangaEditPostBody(track: MyAnimeListEditData): RequestBody {
|
||||||
|
return FormBody.Builder()
|
||||||
|
.add("entry_id", track.entry_id)
|
||||||
|
.add("manga_id", track.manga_id)
|
||||||
|
.add("add_manga[status]", track.status)
|
||||||
|
.add("add_manga[num_read_volumes]", track.num_read_volumes)
|
||||||
|
.add("last_completed_vol", track.last_completed_vol)
|
||||||
|
.add("add_manga[num_read_chapters]", track.num_read_chapters)
|
||||||
|
.add("add_manga[score]", track.score)
|
||||||
|
.add("add_manga[start_date][month]", track.start_date_month)
|
||||||
|
.add("add_manga[start_date][day]", track.start_date_day)
|
||||||
|
.add("add_manga[start_date][year]", track.start_date_year)
|
||||||
|
.add("add_manga[finish_date][month]", track.finish_date_month)
|
||||||
|
.add("add_manga[finish_date][day]", track.finish_date_day)
|
||||||
|
.add("add_manga[finish_date][year]", track.finish_date_year)
|
||||||
|
.add("add_manga[tags]", track.tags)
|
||||||
|
.add("add_manga[priority]", track.priority)
|
||||||
|
.add("add_manga[storage_type]", track.storage_type)
|
||||||
|
.add("add_manga[num_retail_volumes]", track.num_retail_volumes)
|
||||||
|
.add("add_manga[num_read_times]", track.num_read_chapters)
|
||||||
|
.add("add_manga[reread_value]", track.reread_value)
|
||||||
|
.add("add_manga[comments]", track.comments)
|
||||||
|
.add("add_manga[is_asked_to_discuss]", track.is_asked_to_discuss)
|
||||||
|
.add("add_manga[sns_post_type]", track.sns_post_type)
|
||||||
|
.add("submitIt", track.submitIt)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Element.searchDateXml(field: String): Long {
|
||||||
|
val text = selectText(field, "0000-00-00")!!
|
||||||
|
// MAL sets the data to 0000-00-00 when date is invalid or missing
|
||||||
|
if (text == "0000-00-00")
|
||||||
|
return 0L
|
||||||
|
|
||||||
|
return SimpleDateFormat("yyyy-MM-dd", Locale.US).parse(text)?.time ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Element.searchDatePicker(id: String): Long {
|
||||||
|
val month = select(id + "_month > option[selected]").`val`().toIntOrNull()
|
||||||
|
val day = select(id + "_day > option[selected]").`val`().toIntOrNull()
|
||||||
|
val year = select(id + "_year > option[selected]").`val`().toIntOrNull()
|
||||||
|
if (year == null || month == null || day == null)
|
||||||
|
return 0L
|
||||||
|
|
||||||
|
return GregorianCalendar(year, month - 1, day).timeInMillis
|
||||||
|
}
|
||||||
|
|
||||||
private fun Element.searchTitle() = select("strong").text()!!
|
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.searchTotalChapters() = if (select(TD)[4].text() == "-") 0 else select(TD)[4].text().toInt()
|
||||||
@ -302,4 +398,103 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
|||||||
else -> 1
|
else -> 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class MyAnimeListEditData(
|
||||||
|
// entry_id
|
||||||
|
var entry_id: String,
|
||||||
|
|
||||||
|
// manga_id
|
||||||
|
var manga_id: String,
|
||||||
|
|
||||||
|
// add_manga[status]
|
||||||
|
var status: String,
|
||||||
|
|
||||||
|
// add_manga[num_read_volumes]
|
||||||
|
var num_read_volumes: String,
|
||||||
|
|
||||||
|
// last_completed_vol
|
||||||
|
var last_completed_vol: String,
|
||||||
|
|
||||||
|
// add_manga[num_read_chapters]
|
||||||
|
var num_read_chapters: String,
|
||||||
|
|
||||||
|
// add_manga[score]
|
||||||
|
var score: String,
|
||||||
|
|
||||||
|
// add_manga[start_date][month]
|
||||||
|
var start_date_month: String, // [1-12]
|
||||||
|
|
||||||
|
// add_manga[start_date][day]
|
||||||
|
var start_date_day: String,
|
||||||
|
|
||||||
|
// add_manga[start_date][year]
|
||||||
|
var start_date_year: String,
|
||||||
|
|
||||||
|
// add_manga[finish_date][month]
|
||||||
|
var finish_date_month: String, // [1-12]
|
||||||
|
|
||||||
|
// add_manga[finish_date][day]
|
||||||
|
var finish_date_day: String,
|
||||||
|
|
||||||
|
// add_manga[finish_date][year]
|
||||||
|
var finish_date_year: String,
|
||||||
|
|
||||||
|
// add_manga[tags]
|
||||||
|
var tags: String,
|
||||||
|
|
||||||
|
// add_manga[priority]
|
||||||
|
var priority: String,
|
||||||
|
|
||||||
|
// add_manga[storage_type]
|
||||||
|
var storage_type: String,
|
||||||
|
|
||||||
|
// add_manga[num_retail_volumes]
|
||||||
|
var num_retail_volumes: String,
|
||||||
|
|
||||||
|
// add_manga[num_read_times]
|
||||||
|
var num_read_times: String,
|
||||||
|
|
||||||
|
// add_manga[reread_value]
|
||||||
|
var reread_value: String,
|
||||||
|
|
||||||
|
// add_manga[comments]
|
||||||
|
var comments: String,
|
||||||
|
|
||||||
|
// add_manga[is_asked_to_discuss]
|
||||||
|
var is_asked_to_discuss: String,
|
||||||
|
|
||||||
|
// add_manga[sns_post_type]
|
||||||
|
var sns_post_type: String,
|
||||||
|
|
||||||
|
// submitIt
|
||||||
|
val submitIt: String = "0"
|
||||||
|
) {
|
||||||
|
fun copyPersonalFrom(track: Track) {
|
||||||
|
num_read_chapters = track.last_chapter_read.toString()
|
||||||
|
val numScore = track.score.toInt()
|
||||||
|
if (numScore in 1..9)
|
||||||
|
score = numScore.toString()
|
||||||
|
status = track.status.toString()
|
||||||
|
if (track.started_reading_date == 0L) {
|
||||||
|
start_date_month = ""
|
||||||
|
start_date_day = ""
|
||||||
|
start_date_year = ""
|
||||||
|
}
|
||||||
|
if (track.finished_reading_date == 0L) {
|
||||||
|
finish_date_month = ""
|
||||||
|
finish_date_day = ""
|
||||||
|
finish_date_year = ""
|
||||||
|
}
|
||||||
|
track.started_reading_date.toCalendar()?.let { cal ->
|
||||||
|
start_date_month = (cal[Calendar.MONTH] + 1).toString()
|
||||||
|
start_date_day = cal[Calendar.DAY_OF_MONTH].toString()
|
||||||
|
start_date_year = cal[Calendar.YEAR].toString()
|
||||||
|
}
|
||||||
|
track.finished_reading_date.toCalendar()?.let { cal ->
|
||||||
|
finish_date_month = (cal[Calendar.MONTH] + 1).toString()
|
||||||
|
finish_date_day = cal[Calendar.DAY_OF_MONTH].toString()
|
||||||
|
finish_date_year = cal[Calendar.YEAR].toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.manga.track
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.NumberPicker
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.afollestad.materialdialogs.customview.customView
|
||||||
|
import com.afollestad.materialdialogs.customview.getCustomView
|
||||||
|
import com.bluelinelabs.conductor.Controller
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Track
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
|
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||||
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import java.text.DateFormatSymbols
|
||||||
|
import java.util.Calendar
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
class SetTrackReadingDatesDialog<T> : DialogController
|
||||||
|
where T : Controller, T : SetTrackReadingDatesDialog.Listener {
|
||||||
|
|
||||||
|
private val item: TrackItem
|
||||||
|
|
||||||
|
private val dateToUpdate: ReadingDate
|
||||||
|
|
||||||
|
constructor(target: T, dateToUpdate: ReadingDate, item: TrackItem) : super(Bundle().apply {
|
||||||
|
putSerializable(SetTrackReadingDatesDialog.KEY_ITEM_TRACK, item.track)
|
||||||
|
}) {
|
||||||
|
targetController = target
|
||||||
|
this.item = item
|
||||||
|
this.dateToUpdate = dateToUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
constructor(bundle: Bundle) : super(bundle) {
|
||||||
|
val track = bundle.getSerializable(SetTrackReadingDatesDialog.KEY_ITEM_TRACK) as Track
|
||||||
|
val service = Injekt.get<TrackManager>().getService(track.sync_id)!!
|
||||||
|
item = TrackItem(track, service)
|
||||||
|
dateToUpdate = ReadingDate.Start
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||||
|
val item = item
|
||||||
|
|
||||||
|
val dialog = MaterialDialog(activity!!)
|
||||||
|
.title(when (dateToUpdate) {
|
||||||
|
ReadingDate.Start -> R.string.track_started_reading_date
|
||||||
|
ReadingDate.Finish -> R.string.track_finished_reading_date
|
||||||
|
})
|
||||||
|
.customView(R.layout.track_date_dialog, dialogWrapContent = false)
|
||||||
|
.positiveButton(android.R.string.ok) { dialog ->
|
||||||
|
onDialogConfirm(dialog)
|
||||||
|
}
|
||||||
|
.negativeButton(android.R.string.cancel) { dialog ->
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.neutralButton(R.string.action_remove) { dialog ->
|
||||||
|
val listener = (targetController as? Listener)
|
||||||
|
listener?.setReadingDate(item, dateToUpdate, 0L)
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.noAutoDismiss()
|
||||||
|
|
||||||
|
onDialogCreated(dialog)
|
||||||
|
|
||||||
|
return dialog
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onDialogCreated(dialog: MaterialDialog) {
|
||||||
|
val view = dialog.getCustomView()
|
||||||
|
|
||||||
|
val dayPicker: NumberPicker = view.findViewById(R.id.day_picker)
|
||||||
|
val monthPicker: NumberPicker = view.findViewById(R.id.month_picker)
|
||||||
|
val yearPicker: NumberPicker = view.findViewById(R.id.year_picker)
|
||||||
|
|
||||||
|
val monthNames: Array<String> = DateFormatSymbols().months
|
||||||
|
monthPicker.displayedValues = monthNames
|
||||||
|
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
item.track?.let {
|
||||||
|
val date = when (dateToUpdate) {
|
||||||
|
ReadingDate.Start -> it.started_reading_date
|
||||||
|
ReadingDate.Finish -> it.finished_reading_date
|
||||||
|
}
|
||||||
|
if (date != 0L)
|
||||||
|
calendar.timeInMillis = date
|
||||||
|
}
|
||||||
|
dayPicker.value = calendar[Calendar.DAY_OF_MONTH]
|
||||||
|
monthPicker.value = calendar[Calendar.MONTH]
|
||||||
|
yearPicker.maxValue = calendar[Calendar.YEAR]
|
||||||
|
yearPicker.value = calendar[Calendar.YEAR]
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onDialogConfirm(dialog: MaterialDialog) {
|
||||||
|
val view = dialog.getCustomView()
|
||||||
|
|
||||||
|
val dayPicker: NumberPicker = view.findViewById(R.id.day_picker)
|
||||||
|
val monthPicker: NumberPicker = view.findViewById(R.id.month_picker)
|
||||||
|
val yearPicker: NumberPicker = view.findViewById(R.id.year_picker)
|
||||||
|
|
||||||
|
try {
|
||||||
|
val calendar = Calendar.getInstance().apply { isLenient = false }
|
||||||
|
calendar.set(yearPicker.value, monthPicker.value, dayPicker.value)
|
||||||
|
calendar.time = calendar.time // Throws if invalid
|
||||||
|
|
||||||
|
val listener = (targetController as? Listener)
|
||||||
|
listener?.setReadingDate(item, dateToUpdate, calendar.timeInMillis)
|
||||||
|
dialog.dismiss()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
activity?.toast(R.string.error_invalid_date_supplied)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Listener {
|
||||||
|
fun setReadingDate(item: TrackItem, type: ReadingDate, date: Long)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ReadingDate {
|
||||||
|
Start,
|
||||||
|
Finish
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val KEY_ITEM_TRACK = "SetTrackReadingDatesDialog.item.track"
|
||||||
|
}
|
||||||
|
}
|
@ -40,5 +40,7 @@ class TrackAdapter(controller: TrackController) : RecyclerView.Adapter<TrackHold
|
|||||||
fun onStatusClick(position: Int)
|
fun onStatusClick(position: Int)
|
||||||
fun onChaptersClick(position: Int)
|
fun onChaptersClick(position: Int)
|
||||||
fun onScoreClick(position: Int)
|
fun onScoreClick(position: Int)
|
||||||
|
fun onStartDateClick(position: Int)
|
||||||
|
fun onFinishDateClick(position: Int)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,8 @@ class TrackController : NucleusController<TrackControllerBinding, TrackPresenter
|
|||||||
TrackAdapter.OnClickListener,
|
TrackAdapter.OnClickListener,
|
||||||
SetTrackStatusDialog.Listener,
|
SetTrackStatusDialog.Listener,
|
||||||
SetTrackChaptersDialog.Listener,
|
SetTrackChaptersDialog.Listener,
|
||||||
SetTrackScoreDialog.Listener {
|
SetTrackScoreDialog.Listener,
|
||||||
|
SetTrackReadingDatesDialog.Listener {
|
||||||
|
|
||||||
private var adapter: TrackAdapter? = null
|
private var adapter: TrackAdapter? = null
|
||||||
|
|
||||||
@ -123,6 +124,20 @@ class TrackController : NucleusController<TrackControllerBinding, TrackPresenter
|
|||||||
SetTrackScoreDialog(this, item).showDialog(router)
|
SetTrackScoreDialog(this, item).showDialog(router)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStartDateClick(position: Int) {
|
||||||
|
val item = adapter?.getItem(position) ?: return
|
||||||
|
if (item.track == null) return
|
||||||
|
|
||||||
|
SetTrackReadingDatesDialog(this, SetTrackReadingDatesDialog.ReadingDate.Start, item).showDialog(router)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinishDateClick(position: Int) {
|
||||||
|
val item = adapter?.getItem(position) ?: return
|
||||||
|
if (item.track == null) return
|
||||||
|
|
||||||
|
SetTrackReadingDatesDialog(this, SetTrackReadingDatesDialog.ReadingDate.Finish, item).showDialog(router)
|
||||||
|
}
|
||||||
|
|
||||||
override fun setStatus(item: TrackItem, selection: Int) {
|
override fun setStatus(item: TrackItem, selection: Int) {
|
||||||
presenter.setStatus(item, selection)
|
presenter.setStatus(item, selection)
|
||||||
binding.swipeRefresh.isRefreshing = true
|
binding.swipeRefresh.isRefreshing = true
|
||||||
@ -138,6 +153,14 @@ class TrackController : NucleusController<TrackControllerBinding, TrackPresenter
|
|||||||
binding.swipeRefresh.isRefreshing = true
|
binding.swipeRefresh.isRefreshing = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setReadingDate(item: TrackItem, type: SetTrackReadingDatesDialog.ReadingDate, date: Long) {
|
||||||
|
when (type) {
|
||||||
|
SetTrackReadingDatesDialog.ReadingDate.Start -> presenter.setStartDate(item, date)
|
||||||
|
SetTrackReadingDatesDialog.ReadingDate.Finish -> presenter.setFinishDate(item, date)
|
||||||
|
}
|
||||||
|
binding.swipeRefresh.isRefreshing = true
|
||||||
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val TAG_SEARCH_CONTROLLER = "track_search_controller"
|
const val TAG_SEARCH_CONTROLLER = "track_search_controller"
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,34 @@ package eu.kanade.tachiyomi.ui.manga.track
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder
|
import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder
|
||||||
|
import eu.kanade.tachiyomi.util.view.gone
|
||||||
import eu.kanade.tachiyomi.util.view.visibleIf
|
import eu.kanade.tachiyomi.util.view.visibleIf
|
||||||
|
import java.text.DateFormat
|
||||||
|
import kotlinx.android.synthetic.main.track_item.bottom_divider
|
||||||
import kotlinx.android.synthetic.main.track_item.logo_container
|
import kotlinx.android.synthetic.main.track_item.logo_container
|
||||||
import kotlinx.android.synthetic.main.track_item.track_chapters
|
import kotlinx.android.synthetic.main.track_item.track_chapters
|
||||||
import kotlinx.android.synthetic.main.track_item.track_details
|
import kotlinx.android.synthetic.main.track_item.track_details
|
||||||
|
import kotlinx.android.synthetic.main.track_item.track_finish_date
|
||||||
import kotlinx.android.synthetic.main.track_item.track_logo
|
import kotlinx.android.synthetic.main.track_item.track_logo
|
||||||
import kotlinx.android.synthetic.main.track_item.track_score
|
import kotlinx.android.synthetic.main.track_item.track_score
|
||||||
import kotlinx.android.synthetic.main.track_item.track_set
|
import kotlinx.android.synthetic.main.track_item.track_set
|
||||||
|
import kotlinx.android.synthetic.main.track_item.track_start_date
|
||||||
import kotlinx.android.synthetic.main.track_item.track_status
|
import kotlinx.android.synthetic.main.track_item.track_status
|
||||||
import kotlinx.android.synthetic.main.track_item.track_title
|
import kotlinx.android.synthetic.main.track_item.track_title
|
||||||
|
import kotlinx.android.synthetic.main.track_item.vert_divider_3
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
|
class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
|
||||||
|
|
||||||
|
private val preferences: PreferencesHelper by injectLazy()
|
||||||
|
|
||||||
|
private val dateFormat: DateFormat by lazy {
|
||||||
|
preferences.dateFormat().getOrDefault()
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val listener = adapter.rowClickListener
|
val listener = adapter.rowClickListener
|
||||||
|
|
||||||
@ -24,6 +39,8 @@ class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
|
|||||||
track_status.setOnClickListener { listener.onStatusClick(bindingAdapterPosition) }
|
track_status.setOnClickListener { listener.onStatusClick(bindingAdapterPosition) }
|
||||||
track_chapters.setOnClickListener { listener.onChaptersClick(bindingAdapterPosition) }
|
track_chapters.setOnClickListener { listener.onChaptersClick(bindingAdapterPosition) }
|
||||||
track_score.setOnClickListener { listener.onScoreClick(bindingAdapterPosition) }
|
track_score.setOnClickListener { listener.onScoreClick(bindingAdapterPosition) }
|
||||||
|
track_start_date.setOnClickListener { listener.onStartDateClick(bindingAdapterPosition) }
|
||||||
|
track_finish_date.setOnClickListener { listener.onFinishDateClick(bindingAdapterPosition) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
@ -42,6 +59,18 @@ class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
|
|||||||
if (track.total_chapters > 0) track.total_chapters else "-"
|
if (track.total_chapters > 0) track.total_chapters else "-"
|
||||||
track_status.text = item.service.getStatus(track.status)
|
track_status.text = item.service.getStatus(track.status)
|
||||||
track_score.text = if (track.score == 0f) "-" else item.service.displayScore(track)
|
track_score.text = if (track.score == 0f) "-" else item.service.displayScore(track)
|
||||||
|
|
||||||
|
if (item.service.supportsReadingDates) {
|
||||||
|
track_start_date.text =
|
||||||
|
if (track.started_reading_date != 0L) dateFormat.format(track.started_reading_date) else "-"
|
||||||
|
track_finish_date.text =
|
||||||
|
if (track.finished_reading_date != 0L) dateFormat.format(track.finished_reading_date) else "-"
|
||||||
|
} else {
|
||||||
|
bottom_divider.gone()
|
||||||
|
vert_divider_3.gone()
|
||||||
|
track_start_date.gone()
|
||||||
|
track_finish_date.gone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,4 +135,16 @@ class TrackPresenter(
|
|||||||
}
|
}
|
||||||
updateRemote(track, item.service)
|
updateRemote(track, item.service)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setStartDate(item: TrackItem, date: Long) {
|
||||||
|
val track = item.track!!
|
||||||
|
track.started_reading_date = date
|
||||||
|
updateRemote(track, item.service)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFinishDate(item: TrackItem, date: Long) {
|
||||||
|
val track = item.track!!
|
||||||
|
track.finished_reading_date = date
|
||||||
|
updateRemote(track, item.service)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,3 +29,16 @@ fun Long.toDateKey(): Date {
|
|||||||
cal[Calendar.MILLISECOND] = 0
|
cal[Calendar.MILLISECOND] = 0
|
||||||
return cal.time
|
return cal.time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert epoch long to Calendar instance
|
||||||
|
*
|
||||||
|
* @return Calendar instance at supplied epoch time. Null if epoch was 0.
|
||||||
|
*/
|
||||||
|
fun Long.toCalendar(): Calendar? {
|
||||||
|
if (this == 0L)
|
||||||
|
return null
|
||||||
|
val cal = Calendar.getInstance()
|
||||||
|
cal.timeInMillis = this
|
||||||
|
return cal
|
||||||
|
}
|
||||||
|
37
app/src/main/res/layout/track_date_dialog.xml
Normal file
37
app/src/main/res/layout/track_date_dialog.xml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<eu.kanade.tachiyomi.widget.MinMaxNumberPicker
|
||||||
|
android:id="@+id/day_picker"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_margin="3dp"
|
||||||
|
android:descendantFocusability="blocksDescendants"
|
||||||
|
app:max="31"
|
||||||
|
app:min="1" />
|
||||||
|
|
||||||
|
<eu.kanade.tachiyomi.widget.MinMaxNumberPicker
|
||||||
|
android:id="@+id/month_picker"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_margin="3dp"
|
||||||
|
android:descendantFocusability="blocksDescendants"
|
||||||
|
app:max="11"
|
||||||
|
app:min="0" />
|
||||||
|
|
||||||
|
<eu.kanade.tachiyomi.widget.MinMaxNumberPicker
|
||||||
|
android:id="@+id/year_picker"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_margin="3dp"
|
||||||
|
android:descendantFocusability="blocksDescendants"
|
||||||
|
app:min="1900" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -92,7 +92,6 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/vert_divider_1"
|
app:layout_constraintEnd_toStartOf="@+id/vert_divider_1"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/top_divider"
|
app:layout_constraintTop_toBottomOf="@+id/top_divider"
|
||||||
@ -106,7 +105,7 @@
|
|||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:alpha="0.25"
|
android:alpha="0.25"
|
||||||
android:background="?android:attr/textColorHint"
|
android:background="?android:attr/textColorHint"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toTopOf="@+id/bottom_divider"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/track_chapters"
|
app:layout_constraintEnd_toStartOf="@+id/track_chapters"
|
||||||
app:layout_constraintStart_toEndOf="@+id/track_status"
|
app:layout_constraintStart_toEndOf="@+id/track_status"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
@ -121,7 +120,6 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/vert_divider_2"
|
app:layout_constraintEnd_toStartOf="@+id/vert_divider_2"
|
||||||
app:layout_constraintStart_toEndOf="@+id/vert_divider_1"
|
app:layout_constraintStart_toEndOf="@+id/vert_divider_1"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/top_divider"
|
app:layout_constraintTop_toBottomOf="@+id/top_divider"
|
||||||
@ -135,7 +133,7 @@
|
|||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:alpha="0.25"
|
android:alpha="0.25"
|
||||||
android:background="?android:attr/textColorHint"
|
android:background="?android:attr/textColorHint"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toTopOf="@+id/bottom_divider"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/track_score"
|
app:layout_constraintEnd_toStartOf="@+id/track_score"
|
||||||
app:layout_constraintStart_toEndOf="@+id/track_chapters"
|
app:layout_constraintStart_toEndOf="@+id/track_chapters"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
@ -150,12 +148,64 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/vert_divider_2"
|
app:layout_constraintStart_toEndOf="@+id/vert_divider_2"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/top_divider"
|
app:layout_constraintTop_toBottomOf="@+id/top_divider"
|
||||||
tools:text="10" />
|
tools:text="10" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/bottom_divider"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:alpha="0.25"
|
||||||
|
android:background="?android:attr/textColorHint"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/track_score" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/track_start_date"
|
||||||
|
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/list_item_selector"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:padding="16dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/vert_divider_3"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/bottom_divider"
|
||||||
|
tools:text="4/16/2020" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/vert_divider_3"
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:alpha="0.25"
|
||||||
|
android:background="?android:attr/textColorHint"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/track_start_date"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/track_finish_date"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/bottom_divider" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/track_finish_date"
|
||||||
|
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/list_item_selector"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:padding="16dp"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/vert_divider_3"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/bottom_divider"
|
||||||
|
tools:text="4/16/2020" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -475,8 +475,11 @@
|
|||||||
<string name="status">Status</string>
|
<string name="status">Status</string>
|
||||||
<string name="track_status">Status</string>
|
<string name="track_status">Status</string>
|
||||||
<string name="track_start_date">Started</string>
|
<string name="track_start_date">Started</string>
|
||||||
|
<string name="track_started_reading_date">Started reading date</string>
|
||||||
|
<string name="track_finished_reading_date">Finished reading date</string>
|
||||||
<string name="track_type">Type</string>
|
<string name="track_type">Type</string>
|
||||||
<string name="track_author">Author</string>
|
<string name="track_author">Author</string>
|
||||||
|
<string name="error_invalid_date_supplied">Invalid date supplied</string>
|
||||||
<string name="url_not_set">Manga URL not set, please click title and select manga again</string>
|
<string name="url_not_set">Manga URL not set, please click title and select manga again</string>
|
||||||
|
|
||||||
<!-- Category activity -->
|
<!-- Category activity -->
|
||||||
|
Loading…
Reference in New Issue
Block a user