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:
Hawk of the Death
2020-04-23 03:23:23 +02:00
committed by GitHub
parent c967308859
commit f7c139030f
18 changed files with 550 additions and 18 deletions

View File

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

View File

@@ -40,5 +40,7 @@ class TrackAdapter(controller: TrackController) : RecyclerView.Adapter<TrackHold
fun onStatusClick(position: Int)
fun onChaptersClick(position: Int)
fun onScoreClick(position: Int)
fun onStartDateClick(position: Int)
fun onFinishDateClick(position: Int)
}
}

View File

@@ -21,7 +21,8 @@ class TrackController : NucleusController<TrackControllerBinding, TrackPresenter
TrackAdapter.OnClickListener,
SetTrackStatusDialog.Listener,
SetTrackChaptersDialog.Listener,
SetTrackScoreDialog.Listener {
SetTrackScoreDialog.Listener,
SetTrackReadingDatesDialog.Listener {
private var adapter: TrackAdapter? = null
@@ -123,6 +124,20 @@ class TrackController : NucleusController<TrackControllerBinding, TrackPresenter
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) {
presenter.setStatus(item, selection)
binding.swipeRefresh.isRefreshing = true
@@ -138,6 +153,14 @@ class TrackController : NucleusController<TrackControllerBinding, TrackPresenter
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 {
const val TAG_SEARCH_CONTROLLER = "track_search_controller"
}

View File

@@ -2,19 +2,34 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.annotation.SuppressLint
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.util.view.gone
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.track_chapters
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_score
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_title
import kotlinx.android.synthetic.main.track_item.vert_divider_3
import uy.kohesive.injekt.injectLazy
class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
private val preferences: PreferencesHelper by injectLazy()
private val dateFormat: DateFormat by lazy {
preferences.dateFormat().getOrDefault()
}
init {
val listener = adapter.rowClickListener
@@ -24,6 +39,8 @@ class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
track_status.setOnClickListener { listener.onStatusClick(bindingAdapterPosition) }
track_chapters.setOnClickListener { listener.onChaptersClick(bindingAdapterPosition) }
track_score.setOnClickListener { listener.onScoreClick(bindingAdapterPosition) }
track_start_date.setOnClickListener { listener.onStartDateClick(bindingAdapterPosition) }
track_finish_date.setOnClickListener { listener.onFinishDateClick(bindingAdapterPosition) }
}
@SuppressLint("SetTextI18n")
@@ -42,6 +59,18 @@ class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
if (track.total_chapters > 0) track.total_chapters else "-"
track_status.text = item.service.getStatus(track.status)
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()
}
}
}
}

View File

@@ -135,4 +135,16 @@ class TrackPresenter(
}
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)
}
}