Added tracking sheet to manga details

Using the compact card view dev tachi is for now, maybe forever
Also finally fixed the anilist bug

Co-Authored-By: arkon <arkon@users.noreply.github.com>
This commit is contained in:
Jay 2020-03-09 23:34:00 -07:00
parent 8134d4c2fa
commit 29134f6bb0
17 changed files with 526 additions and 158 deletions

View File

@ -1,18 +1,13 @@
package eu.kanade.tachiyomi.data.track.anilist
import android.app.DownloadManager
import android.content.Context
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.*
import java.util.Locale
data class ALManga(
val media_id: Int,
@ -45,12 +40,11 @@ data class ALManga(
}
data class ALUserManga(
val library_id: Long,
val list_status: String,
val score_raw: Int,
val chapters_read: Int,
val manga: ALManga,
val context: Context = Injekt.get<PreferencesHelper>().context
val library_id: Long,
val list_status: String,
val score_raw: Int,
val chapters_read: Int,
val manga: ALManga
) {
fun toTrack() = Track.create(TrackManager.ANILIST).apply {
@ -62,16 +56,14 @@ data class ALUserManga(
total_chapters = manga.total_chapters
}
fun toTrackStatus() = with(context) {
when (list_status) {
getString(R.string.reading) -> Anilist.READING
getString(R.string.completed) -> Anilist.COMPLETED
getString(R.string.paused) -> Anilist.PAUSED
getString(R.string.dropped) -> Anilist.DROPPED
getString(R.string.plan_to_read) -> Anilist.PLANNING
getString(R.string.repeating)-> Anilist.REPEATING
else -> throw NotImplementedError("Unknown status")
}
fun toTrackStatus() = when (list_status) {
"CURRENT" -> Anilist.READING
"COMPLETED" -> Anilist.COMPLETED
"PAUSED" -> Anilist.PAUSED
"DROPPED" -> Anilist.DROPPED
"PLANNING" -> Anilist.PLANNING
"REPEATING" -> Anilist.REPEATING
else -> throw NotImplementedError("Unknown status")
}
}

View File

@ -61,6 +61,7 @@ import eu.kanade.tachiyomi.data.download.DownloadService
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource
@ -78,6 +79,7 @@ import eu.kanade.tachiyomi.ui.manga.chapter.ChapterMatHolder
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersAdapter
import eu.kanade.tachiyomi.ui.manga.chapter.DownloadCustomChaptersDialog
import eu.kanade.tachiyomi.ui.manga.info.EditMangaDialog
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
@ -143,6 +145,7 @@ class MangaDetailsController : BaseController,
private var snack: Snackbar? = null
val fromCatalogue = args.getBoolean(FROM_CATALOGUE_EXTRA, false)
var coverDrawable:Drawable? = null
var trackingBottomSheet: TrackingBottomSheet? = null
/**
* Adapter containing a list of chapters.
*/
@ -444,6 +447,8 @@ class MangaDetailsController : BaseController,
override fun onDestroyView(view: View) {
snack?.dismiss()
presenter.onDestroy()
adapter = null
trackingBottomSheet = null
super.onDestroyView(view)
}
@ -869,6 +874,36 @@ class MangaDetailsController : BaseController,
return super.handleBack()
}
override fun showTrackingSheet() {
trackingBottomSheet = TrackingBottomSheet(this)
trackingBottomSheet?.show()
}
fun refreshTracking(trackings: List<TrackItem>) {
trackingBottomSheet?.onNextTrackings(trackings)
}
fun onTrackSearchResults(results: List<TrackSearch>) {
trackingBottomSheet?.onSearchResults(results)
}
fun refreshTracker() {
(recycler.findViewHolderForAdapterPosition(0) as? MangaHeaderHolder)
?.updateTracking()
}
fun trackRefreshDone() {
trackingBottomSheet?.onRefreshDone()
}
fun trackRefreshError(error: Exception) {
trackingBottomSheet?.onRefreshError(error)
}
fun trackSearchError(error: Exception) {
trackingBottomSheet?.onSearchResultsError(error)
}
override fun zoomImageFromThumb(thumbView: View) {
// If there's an animation in progress, cancel it immediately and proceed with this one.
currentAnimator?.cancel()

View File

@ -20,11 +20,13 @@ import eu.kanade.tachiyomi.data.library.LibraryServiceListener
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.storage.DiskUtil
@ -62,6 +64,7 @@ class MangaDetailsPresenter(private val controller: MangaDetailsController,
private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLogged } }
var tracks = emptyList<Track>()
var trackList: List<TrackItem> = emptyList()
var chapters:List<ChapterItem> = emptyList()
private set
@ -73,6 +76,7 @@ class MangaDetailsPresenter(private val controller: MangaDetailsController,
headerItem.isLocked = isLockedFromSearch
downloadManager.addListener(this)
LibraryUpdateService.setListener(this)
tracks = db.getTracks(manga).executeAsBlocking()
if (!manga.initialized) {
isLoading = true
controller.setRefresh(true)
@ -81,9 +85,9 @@ class MangaDetailsPresenter(private val controller: MangaDetailsController,
}
else {
updateChapters()
tracks = db.getTracks(manga).executeAsBlocking()
controller.updateChapters(this.chapters)
}
fetchTrackings()
}
fun onDestroy() {
@ -161,7 +165,7 @@ class MangaDetailsPresenter(private val controller: MangaDetailsController,
}
/**
* Sets the active display mode.
* @param mode the mode to set.
* @param hide set title to hidden
*/
fun hideTitle(hide: Boolean) {
manga.displayMode = if (hide) Manga.DISPLAY_NUMBER else Manga.DISPLAY_NAME
@ -658,7 +662,124 @@ class MangaDetailsPresenter(private val controller: MangaDetailsController,
return false
}
fun isTracked(): Boolean {
return loggedServices.any { service -> tracks.any { it.sync_id == service.id } }
fun isTracked(): Boolean = loggedServices.any { service -> tracks.any { it.sync_id == service.id } }
fun hasTrackers(): Boolean = loggedServices.isNotEmpty()
// Tracking
private fun fetchTrackings() {
launch {
trackList = loggedServices.map { service ->
TrackItem(tracks.find { it.sync_id == service.id }, service)
}
}
}
private suspend fun refreshTracking() {
tracks = withContext(Dispatchers.IO) { db.getTracks(manga).executeAsBlocking() }
trackList = loggedServices.map { service ->
TrackItem(tracks.find { it.sync_id == service.id }, service)
}
withContext(Dispatchers.Main) { controller.refreshTracking(trackList) }
}
fun refreshTrackers() {
launch {
val list = trackList.filter { it.track != null }.map { item ->
withContext(Dispatchers.IO) {
val trackItem = try {
item.service.refresh(item.track!!).toBlocking().single()
} catch (e: Exception) {
trackError(e)
null
}
if (trackItem != null) {
db.insertTrack(trackItem).executeAsBlocking()
trackItem
}
else
item.track
}
}
refreshTracking()
}
}
fun trackSearch(query: String, service: TrackService) {
launch(Dispatchers.IO) {
val results = try {service.search(query).toBlocking().single() }
catch (e: Exception) {
withContext(Dispatchers.Main) { controller.trackSearchError(e) }
null }
if (!results.isNullOrEmpty()) {
withContext(Dispatchers.Main) { controller.onTrackSearchResults(results) }
}
}
}
fun registerTracking(item: Track?, service: TrackService) {
if (item != null) {
item.manga_id = manga.id!!
launch {
val binding = try { service.bind(item).toBlocking().single() }
catch (e: Exception) {
trackError(e)
null
}
withContext(Dispatchers.IO) {
if (binding != null) db.insertTrack(binding).executeAsBlocking() }
refreshTracking()
}
} else {
launch {
withContext(Dispatchers.IO) { db.deleteTrackForManga(manga, service)
.executeAsBlocking() }
refreshTracking()
}
}
}
private fun updateRemote(track: Track, service: TrackService) {
launch {
val binding = try { service.update(track).toBlocking().single() }
catch (e: Exception) {
trackError(e)
null
}
if (binding != null) {
withContext(Dispatchers.IO) { db.insertTrack(binding).executeAsBlocking() }
refreshTracking()
}
else trackRefreshDone()
}
}
private suspend fun trackRefreshDone() {
async(Dispatchers.Main) { controller.trackRefreshDone() }
}
private suspend fun trackError(error: Exception) {
async(Dispatchers.Main) { controller.trackRefreshError(error) }
}
fun setStatus(item: TrackItem, index: Int) {
val track = item.track!!
track.status = item.service.getStatusList()[index]
updateRemote(track, item.service)
}
fun setScore(item: TrackItem, index: Int) {
val track = item.track!!
track.score = item.service.indexToScore(index)
updateRemote(track, item.service)
}
fun setLastChapterRead(item: TrackItem, chapterNumber: Int) {
val track = item.track!!
track.last_chapter_read = chapterNumber
updateRemote(track, item.service)
}
}

View File

@ -54,7 +54,7 @@ class MangaHeaderHolder(
filters_text.setOnClickListener { adapter.coverListener?.showChapterFilter() }
chapters_title.setOnClickListener { adapter.coverListener?.showChapterFilter() }
webview_button.setOnClickListener { adapter.coverListener?.openInWebView() }
share_button.setOnClickListener { adapter.coverListener?.prepareToShareManga() }
share_button.setOnClickListener { adapter.coverListener?.prepareToShareManga() }
favorite_button.setOnClickListener {
adapter.coverListener?.favoriteManga(false)
}
@ -71,6 +71,7 @@ class MangaHeaderHolder(
true
}
manga_cover.setOnClickListener { adapter.coverListener?.zoomImageFromThumb(cover_card) }
track_button.setOnClickListener { adapter.coverListener?.showTrackingSheet() }
if (startExpanded)
expandDesc()
}
@ -144,6 +145,7 @@ class MangaHeaderHolder(
val tracked = presenter.isTracked() && !item.isLocked
with(track_button) {
visibleIf(presenter.hasTrackers())
text = itemView.context.getString(if (tracked) R.string.action_filter_tracked
else R.string.tracking)
@ -232,6 +234,19 @@ class MangaHeaderHolder(
true_backdrop.setBackgroundColor(color)
}
fun updateTracking() {
val presenter = adapter.coverListener?.mangaPresenter() ?: return
val tracked = presenter.isTracked()
with(track_button) {
text = itemView.context.getString(if (tracked) R.string.action_filter_tracked
else R.string.tracking)
icon = ContextCompat.getDrawable(itemView.context, if (tracked) R.drawable
.ic_check_white_24dp else R.drawable.ic_sync_black_24dp)
checked(tracked)
}
}
override fun onLongClick(view: View?): Boolean {
super.onLongClick(view)
return false

View File

@ -0,0 +1,185 @@
package eu.kanade.tachiyomi.ui.manga
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.ui.manga.track.SetTrackChaptersDialog
import eu.kanade.tachiyomi.ui.manga.track.SetTrackScoreDialog
import eu.kanade.tachiyomi.ui.manga.track.SetTrackStatusDialog
import eu.kanade.tachiyomi.ui.manga.track.TrackAdapter
import eu.kanade.tachiyomi.ui.manga.track.TrackHolder
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
import eu.kanade.tachiyomi.ui.manga.track.TrackSearchDialog
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
import eu.kanade.tachiyomi.util.view.setEdgeToEdge
import kotlinx.android.synthetic.main.tracking_bottom_sheet.*
import timber.log.Timber
class TrackingBottomSheet(private val controller: MangaDetailsController) : BottomSheetDialog
(controller.activity!!, R.style.BottomSheetDialogTheme),
TrackAdapter.OnClickListener,
SetTrackStatusDialog.Listener,
SetTrackChaptersDialog.Listener,
SetTrackScoreDialog.Listener {
val activity = controller.activity!!
private var sheetBehavior: BottomSheetBehavior<*>
val presenter = controller.presenter
private var adapter: TrackAdapter? = null
init {
// Use activity theme for this layout
val view = activity.layoutInflater.inflate(R.layout.tracking_bottom_sheet, null)
setContentView(view)
sheetBehavior = BottomSheetBehavior.from(view.parent as ViewGroup)
setEdgeToEdge(activity, display_bottom_sheet, view, false)
val height = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.window.decorView.rootWindowInsets.systemWindowInsetBottom
} else 0
sheetBehavior.peekHeight = 380.dpToPx + height
sheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, progress: Float) { }
override fun onStateChanged(p0: View, state: Int) {
if (state == BottomSheetBehavior.STATE_EXPANDED) {
sheetBehavior.skipCollapsed = true
}
}
})
}
override fun onStart() {
super.onStart()
sheetBehavior.skipCollapsed = true
}
/**
* Called when the sheet is created. It initializes the listeners and values of the preferences.
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
adapter = TrackAdapter(this)
track_recycler.layoutManager = LinearLayoutManager(context)
track_recycler.adapter = adapter
track_recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
adapter?.items = presenter.trackList
}
fun onNextTrackings(trackings: List<TrackItem>) {
onRefreshDone()
adapter?.items = trackings
controller.refreshTracker()
}
fun onSearchResults(results: List<TrackSearch>) {
getSearchDialog()?.onSearchResults(results)
}
fun onSearchResultsError(error: Throwable) {
Timber.e(error)
getSearchDialog()?.onSearchResultsError()
}
private fun getSearchDialog(): TrackSearchDialog? {
return controller.router.getControllerWithTag(TAG_SEARCH_CONTROLLER) as? TrackSearchDialog
}
fun onRefreshDone() {
for (i in adapter!!.items.indices) {
(track_recycler.findViewHolderForAdapterPosition(i) as? TrackHolder)?.setProgress(false)
}
}
fun onRefreshError(error: Throwable) {
for (i in adapter!!.items.indices) {
(track_recycler.findViewHolderForAdapterPosition(i) as? TrackHolder)?.setProgress(false)
}
activity.toast(error.message)
}
override fun onLogoClick(position: Int) {
val track = adapter?.getItem(position)?.track ?: return
if (track.tracking_url.isBlank()) {
activity.toast(R.string.url_not_set)
} else {
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(track.tracking_url)))
}
}
override fun onSetClick(position: Int) {
val item = adapter?.getItem(position) ?: return
TrackSearchDialog(this, item.service, item.track != null).showDialog(
controller.router, TAG_SEARCH_CONTROLLER)
}
override fun onStatusClick(position: Int) {
val item = adapter?.getItem(position) ?: return
if (item.track == null) return
SetTrackStatusDialog(this, item).showDialog(controller.router)
}
override fun onChaptersClick(position: Int) {
val item = adapter?.getItem(position) ?: return
if (item.track == null) return
SetTrackChaptersDialog(this, item).showDialog(controller.router)
}
override fun onScoreClick(position: Int) {
val item = adapter?.getItem(position) ?: return
if (item.track == null) return
SetTrackScoreDialog(this, item).showDialog(controller.router)
}
override fun setStatus(item: TrackItem, selection: Int) {
presenter.setStatus(item, selection)
refreshItem(item)
}
private fun refreshItem(item: TrackItem) {
refreshTrack(item.service)
}
fun refreshTrack(item: TrackService?) {
val index = adapter?.indexOf(item) ?: -1
if (index > -1 ){
(track_recycler.findViewHolderForAdapterPosition(index) as? TrackHolder)
?.setProgress(true)
}
}
override fun setScore(item: TrackItem, score: Int) {
presenter.setScore(item, score)
refreshItem(item)
}
override fun setChaptersRead(item: TrackItem, chaptersRead: Int) {
presenter.setLastChapterRead(item, chaptersRead)
refreshItem(item)
}
private companion object {
const val TAG_SEARCH_CONTROLLER = "track_search_controller"
}
}

View File

@ -71,5 +71,6 @@ class ChaptersAdapter(
fun favoriteManga(longPress: Boolean)
fun copyToClipboard(content: String, label: Int)
fun zoomImageFromThumb(thumbView: View)
fun showTrackingSheet()
}
}

View File

@ -6,7 +6,6 @@ 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
@ -15,14 +14,15 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class SetTrackChaptersDialog<T> : DialogController
where T : Controller, T : SetTrackChaptersDialog.Listener {
where T : SetTrackChaptersDialog.Listener {
private val item: TrackItem
private lateinit var listener: Listener
constructor(target: T, item: TrackItem) : super(Bundle().apply {
putSerializable(KEY_ITEM_TRACK, item.track)
}) {
targetController = target
listener = target
this.item = item
}
@ -45,7 +45,7 @@ class SetTrackChaptersDialog<T> : DialogController
// Remove focus to update selected number
val np: NumberPicker = view.findViewById(R.id.chapters_picker)
np.clearFocus()
(targetController as? Listener)?.setChaptersRead(item, np.value)
listener.setChaptersRead(item, np.value)
}
val view = dialog.getCustomView()

View File

@ -15,14 +15,15 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class SetTrackScoreDialog<T> : DialogController
where T : Controller, T : SetTrackScoreDialog.Listener {
where T : SetTrackScoreDialog.Listener {
private val item: TrackItem
private lateinit var listener: Listener
constructor(target: T, item: TrackItem) : super(Bundle().apply {
putSerializable(KEY_ITEM_TRACK, item.track)
}) {
targetController = target
listener = target
this.item = item
}
@ -46,8 +47,7 @@ class SetTrackScoreDialog<T> : DialogController
val np: NumberPicker = view.findViewById(R.id.score_picker)
np.clearFocus()
(targetController as? Listener)?.setScore(item, np.value)
listener.setScore(item, np.value)
}

View File

@ -4,7 +4,6 @@ import android.app.Dialog
import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsSingleChoice
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
@ -13,14 +12,16 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class SetTrackStatusDialog<T> : DialogController
where T : Controller, T : SetTrackStatusDialog.Listener {
where T : SetTrackStatusDialog.Listener {
private val item: TrackItem
private lateinit var listener: Listener
constructor(target: T, item: TrackItem) : super(Bundle().apply {
putSerializable(KEY_ITEM_TRACK, item.track)
}) {
targetController = target
listener = target
// targetController = target
this.item = item
}
@ -43,7 +44,7 @@ class SetTrackStatusDialog<T> : DialogController
.listItemsSingleChoice(items = statusString, initialSelection = selectedIndex,
waitForPositiveButton = false)
{ dialog, position, _ ->
(targetController as? Listener)?.setStatus(item, position)
listener.setStatus(item, position)
dialog.dismiss()
}
}

View File

@ -1,11 +1,12 @@
package eu.kanade.tachiyomi.ui.manga.track
import androidx.recyclerview.widget.RecyclerView
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.util.view.inflate
class TrackAdapter(controller: TrackController) : RecyclerView.Adapter<TrackHolder>() {
class TrackAdapter(controller: OnClickListener) : RecyclerView.Adapter<TrackHolder>() {
var items = emptyList<TrackItem>()
set(value) {
@ -34,9 +35,13 @@ class TrackAdapter(controller: TrackController) : RecyclerView.Adapter<TrackHold
holder.bind(items[position])
}
fun indexOf(item: TrackService?):Int {
return items.indexOfFirst { item?.id == it.service.id }
}
interface OnClickListener {
fun onLogoClick(position: Int)
fun onTitleClick(position: Int)
fun onSetClick(position: Int)
fun onStatusClick(position: Int)
fun onChaptersClick(position: Int)
fun onScoreClick(position: Int)

View File

@ -119,7 +119,7 @@ class TrackController : NucleusController<TrackPresenter>(),
}
}
override fun onTitleClick(position: Int) {
override fun onSetClick(position: Int) {
val item = adapter?.getItem(position) ?: return
TrackSearchDialog(this, item.service, item.track != null).showDialog(router,
TAG_SEARCH_CONTROLLER)

View File

@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.annotation.SuppressLint
import android.view.View
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder
import eu.kanade.tachiyomi.util.view.visibleIf
import kotlinx.android.synthetic.main.track_item.*
class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
@ -11,32 +11,28 @@ class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
init {
val listener = adapter.rowClickListener
logo_container.setOnClickListener { listener.onLogoClick(adapterPosition) }
title_container.setOnClickListener { listener.onTitleClick(adapterPosition) }
track_set.setOnClickListener { listener.onSetClick(adapterPosition) }
status_container.setOnClickListener { listener.onStatusClick(adapterPosition) }
chapters_container.setOnClickListener { listener.onChaptersClick(adapterPosition) }
score_container.setOnClickListener { listener.onScoreClick(adapterPosition) }
}
@SuppressLint("SetTextI18n")
@Suppress("DEPRECATION")
fun bind(item: TrackItem) {
val track = item.track
track_logo.setImageResource(item.service.getLogo())
logo_container.setBackgroundColor(item.service.getLogoColor())
track_group.visibleIf(track != null)
if (track != null) {
track_title.setTextAppearance(itemView.context, R.style.TextAppearance_Regular_Body1_Secondary)
track_title.isAllCaps = false
track_title.text = track.title
track_chapters.text = "${track.last_chapter_read}/" +
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)
} else {
track_title.setTextAppearance(itemView.context, R.style.TextAppearance_Medium_Button)
track_title.setText(R.string.action_edit)
track_chapters.text = ""
track_score.text = ""
track_status.text = ""
}
}
fun setProgress(enabled: Boolean) {
progress.visibleIf(enabled)
track_logo.visibleIf(!enabled)
}
}

View File

@ -15,11 +15,10 @@ import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import kotlinx.android.synthetic.main.track_controller.*
import eu.kanade.tachiyomi.ui.manga.MangaDetailsPresenter
import eu.kanade.tachiyomi.ui.manga.TrackingBottomSheet
import eu.kanade.tachiyomi.util.lang.plusAssign
import kotlinx.android.synthetic.main.track_search_dialog.view.progress
import kotlinx.android.synthetic.main.track_search_dialog.view.track_search
import kotlinx.android.synthetic.main.track_search_dialog.view.track_search_list
import kotlinx.android.synthetic.main.track_search_dialog.view.*
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import rx.subscriptions.CompositeSubscription
@ -41,10 +40,14 @@ class TrackSearchDialog : DialogController {
private var searchTextSubscription: Subscription? = null
private val trackController
get() = targetController as TrackController
private lateinit var bottomSheet: TrackingBottomSheet
//private val trackController
// get() = targetController as TrackController
private var wasPreviouslyTracked:Boolean = false
private lateinit var presenter:MangaDetailsPresenter
constructor(target: TrackController, service: TrackService, wasTracked:Boolean) : super(Bundle()
.apply {
@ -55,6 +58,16 @@ class TrackSearchDialog : DialogController {
this.service = service
}
constructor(target: TrackingBottomSheet, service: TrackService, wasTracked:Boolean) : super(Bundle()
.apply {
putInt(KEY_SERVICE, service.id)
}) {
wasPreviouslyTracked = wasTracked
bottomSheet = target
presenter = target.presenter
this.service = service
}
@Suppress("unused")
constructor(bundle: Bundle) : super(bundle) {
service = Injekt.get<TrackManager>().getService(bundle.getInt(KEY_SERVICE))!!
@ -97,7 +110,7 @@ class TrackSearchDialog : DialogController {
// Do an initial search based on the manga's title
if (savedState == null) {
val title = trackController.presenter.manga.originalTitle()
val title = presenter.manga.originalTitle()
view.track_search.append(title)
search(title)
}
@ -129,7 +142,7 @@ class TrackSearchDialog : DialogController {
val view = dialogView ?: return
view.progress.visibility = View.VISIBLE
view.track_search_list.visibility = View.INVISIBLE
trackController.presenter.search(query, service)
presenter.trackSearch(query, service)
}
fun onSearchResults(results: List<TrackSearch>) {
@ -153,8 +166,10 @@ class TrackSearchDialog : DialogController {
}
private fun onPositiveButtonClick() {
trackController.swipe_refresh.isRefreshing = true
trackController.presenter.registerTracking(selectedItem, service)
// trackController.swipe_refresh.isRefreshing = true
bottomSheet.refreshTrack(service)
presenter.registerTracking(selectedItem,
service)
}
private companion object {

View File

@ -169,7 +169,7 @@ inline val View.marginLeft: Int
object RecyclerWindowInsetsListener : View.OnApplyWindowInsetsListener {
override fun onApplyWindowInsets(v: View, insets: WindowInsets): WindowInsets {
v.setPadding(0,0,0,insets.systemWindowInsetBottom)
v.updatePaddingRelative(bottom = insets.systemWindowInsetBottom)
//v.updatePaddingRelative(bottom = v.paddingBottom + insets.systemWindowInsetBottom)
return insets
}

View File

@ -1,22 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
<com.google.android.material.card.MaterialCardView 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:id="@+id/track"
style="@style/Theme.Widget.CardView.Item"
android:padding="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:background="?android:attr/colorBackground"
android:minHeight="?attr/actionBarSize">
<FrameLayout
android:id="@+id/logo_container"
android:layout_width="48dp"
android:layout_height="0dp"
android:foreground="?selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/status_container"
app:layout_constraintTop_toTopOf="parent"
android:clickable="true"
tools:background="#2E51A2">
<ImageView
@ -26,48 +33,21 @@
android:layout_gravity="center"
tools:src="@drawable/tracker_mal" />
</FrameLayout>
<LinearLayout
android:id="@+id/title_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?attr/selectable_list_drawable"
android:clickable="true"
android:padding="16dp"
app:layout_constraintStart_toEndOf="@+id/logo_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
style="@style/TextAppearance.Regular.Body1"
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title" />
android:layout_gravity="center"
android:padding="4dp"
android:visibility="gone"/>
<TextView
android:id="@+id/track_title"
style="@style/TextAppearance.Medium.Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:ellipsize="middle"
android:gravity="end"
android:maxLines="1"
android:text="@string/action_edit" />
</FrameLayout>
</LinearLayout>
<View
android:id="@+id/divider1"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:background="?android:attr/divider"
app:layout_constraintStart_toEndOf="@+id/logo_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title_container" />
<androidx.constraintlayout.widget.Group
android:id="@+id/track_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="chapters_container,status_container,score_container" />
<LinearLayout
android:id="@+id/status_container"
@ -75,106 +55,114 @@
android:layout_height="wrap_content"
android:background="?attr/selectable_list_drawable"
android:clickable="true"
android:padding="16dp"
app:layout_constraintStart_toEndOf="@+id/logo_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider1">
android:focusable="true"
android:orientation="vertical"
app:layout_constraintStart_toEndOf="@id/logo_container"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/chapters_container"
app:layout_constraintBottom_toBottomOf="parent"
android:paddingStart="16dp"
android:paddingTop="16dp"
android:paddingEnd="8dp"
android:paddingBottom="16dp">
<TextView
style="@style/TextAppearance.Regular.Body1"
style="@style/TextAppearance.MaterialComponents.Body1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/status" />
<TextView
android:id="@+id/track_status"
style="@style/TextAppearance.Regular.Body1.Secondary"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:gravity="end"
tools:text="Reading" />
android:layout_marginTop="8dp"
tools:text="Currently Reading" />
</LinearLayout>
<View
android:id="@+id/divider2"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:background="?android:attr/divider"
app:layout_constraintStart_toEndOf="@+id/logo_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/status_container" />
<LinearLayout
android:id="@+id/chapters_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="0dp"
app:layout_constrainedHeight="true"
android:background="?attr/selectable_list_drawable"
android:clickable="true"
android:padding="16dp"
app:layout_constraintStart_toEndOf="@+id/logo_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider2">
android:focusable="true"
android:orientation="vertical"
android:paddingStart="8dp"
android:paddingTop="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/status_container"
app:layout_constraintEnd_toStartOf="@id/score_container"
app:layout_constraintBottom_toBottomOf="parent"
android:paddingEnd="8dp"
android:paddingBottom="16dp">
<TextView
style="@style/TextAppearance.Regular.Body1"
style="@style/TextAppearance.MaterialComponents.Body1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/chapters" />
<TextView
android:id="@+id/track_chapters"
style="@style/TextAppearance.Regular.Body1.Secondary"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:gravity="end"
android:layout_marginTop="8dp"
tools:text="12/24" />
</LinearLayout>
<View
android:id="@+id/divider3"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:background="?android:attr/divider"
app:layout_constraintStart_toEndOf="@+id/logo_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/chapters_container" />
<LinearLayout
android:id="@+id/score_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="0dp"
android:background="?attr/selectable_list_drawable"
app:layout_constraintBottom_toBottomOf="parent"
android:clickable="true"
android:padding="16dp"
app:layout_constraintStart_toEndOf="@+id/logo_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider3">
android:focusable="true"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/chapters_container"
app:layout_constraintEnd_toStartOf="@id/track_set"
android:paddingStart="8dp"
android:paddingTop="16dp"
android:paddingEnd="16dp"
android:paddingBottom="16dp">
<TextView
style="@style/TextAppearance.Regular.Body1"
style="@style/TextAppearance.MaterialComponents.Body1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/score" />
<TextView
android:id="@+id/track_score"
style="@style/TextAppearance.Regular.Body1.Secondary"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:gravity="end"
android:layout_marginTop="8dp"
tools:text="10" />
</LinearLayout>
<ImageButton
android:id="@+id/track_set"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/action_edit"
android:padding="8dp"
android:tint="?android:attr/textColorSecondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/score_container"
app:srcCompat="@drawable/ic_edit_white_24dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/display_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/track_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_bottom_sheet_dialog_fragment"
tools:listitem="@layout/track_item" />
</FrameLayout>

View File

@ -149,7 +149,7 @@
<item name="layout_behavior">eu.kanade.tachiyomi.widget.FABMoveBehaviour</item>
</style>
<style name="Theme.Widget.CardView" parent="CardView">
<style name="Theme.Widget.CardView" parent="Widget.MaterialComponents.CardView">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:padding">@dimen/material_component_cards_top_and_bottom_padding</item>