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:
parent
8134d4c2fa
commit
29134f6bb0
@ -1,18 +1,13 @@
|
|||||||
package eu.kanade.tachiyomi.data.track.anilist
|
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.database.models.Track
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.Locale
|
||||||
|
|
||||||
data class ALManga(
|
data class ALManga(
|
||||||
val media_id: Int,
|
val media_id: Int,
|
||||||
@ -45,12 +40,11 @@ data class ALManga(
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class ALUserManga(
|
data class ALUserManga(
|
||||||
val library_id: Long,
|
val library_id: Long,
|
||||||
val list_status: String,
|
val list_status: String,
|
||||||
val score_raw: Int,
|
val score_raw: Int,
|
||||||
val chapters_read: Int,
|
val chapters_read: Int,
|
||||||
val manga: ALManga,
|
val manga: ALManga
|
||||||
val context: Context = Injekt.get<PreferencesHelper>().context
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun toTrack() = Track.create(TrackManager.ANILIST).apply {
|
fun toTrack() = Track.create(TrackManager.ANILIST).apply {
|
||||||
@ -62,16 +56,14 @@ data class ALUserManga(
|
|||||||
total_chapters = manga.total_chapters
|
total_chapters = manga.total_chapters
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toTrackStatus() = with(context) {
|
fun toTrackStatus() = when (list_status) {
|
||||||
when (list_status) {
|
"CURRENT" -> Anilist.READING
|
||||||
getString(R.string.reading) -> Anilist.READING
|
"COMPLETED" -> Anilist.COMPLETED
|
||||||
getString(R.string.completed) -> Anilist.COMPLETED
|
"PAUSED" -> Anilist.PAUSED
|
||||||
getString(R.string.paused) -> Anilist.PAUSED
|
"DROPPED" -> Anilist.DROPPED
|
||||||
getString(R.string.dropped) -> Anilist.DROPPED
|
"PLANNING" -> Anilist.PLANNING
|
||||||
getString(R.string.plan_to_read) -> Anilist.PLANNING
|
"REPEATING" -> Anilist.REPEATING
|
||||||
getString(R.string.repeating)-> Anilist.REPEATING
|
else -> throw NotImplementedError("Unknown status")
|
||||||
else -> throw NotImplementedError("Unknown status")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ import eu.kanade.tachiyomi.data.download.DownloadService
|
|||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.data.glide.GlideApp
|
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
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.Source
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
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.ChaptersAdapter
|
||||||
import eu.kanade.tachiyomi.ui.manga.chapter.DownloadCustomChaptersDialog
|
import eu.kanade.tachiyomi.ui.manga.chapter.DownloadCustomChaptersDialog
|
||||||
import eu.kanade.tachiyomi.ui.manga.info.EditMangaDialog
|
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.reader.ReaderActivity
|
||||||
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||||
@ -143,6 +145,7 @@ class MangaDetailsController : BaseController,
|
|||||||
private var snack: Snackbar? = null
|
private var snack: Snackbar? = null
|
||||||
val fromCatalogue = args.getBoolean(FROM_CATALOGUE_EXTRA, false)
|
val fromCatalogue = args.getBoolean(FROM_CATALOGUE_EXTRA, false)
|
||||||
var coverDrawable:Drawable? = null
|
var coverDrawable:Drawable? = null
|
||||||
|
var trackingBottomSheet: TrackingBottomSheet? = null
|
||||||
/**
|
/**
|
||||||
* Adapter containing a list of chapters.
|
* Adapter containing a list of chapters.
|
||||||
*/
|
*/
|
||||||
@ -444,6 +447,8 @@ class MangaDetailsController : BaseController,
|
|||||||
override fun onDestroyView(view: View) {
|
override fun onDestroyView(view: View) {
|
||||||
snack?.dismiss()
|
snack?.dismiss()
|
||||||
presenter.onDestroy()
|
presenter.onDestroy()
|
||||||
|
adapter = null
|
||||||
|
trackingBottomSheet = null
|
||||||
super.onDestroyView(view)
|
super.onDestroyView(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -869,6 +874,36 @@ class MangaDetailsController : BaseController,
|
|||||||
return super.handleBack()
|
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) {
|
override fun zoomImageFromThumb(thumbView: View) {
|
||||||
// If there's an animation in progress, cancel it immediately and proceed with this one.
|
// If there's an animation in progress, cancel it immediately and proceed with this one.
|
||||||
currentAnimator?.cancel()
|
currentAnimator?.cancel()
|
||||||
|
@ -20,11 +20,13 @@ import eu.kanade.tachiyomi.data.library.LibraryServiceListener
|
|||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
|
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.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
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 } }
|
private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLogged } }
|
||||||
var tracks = emptyList<Track>()
|
var tracks = emptyList<Track>()
|
||||||
|
|
||||||
|
var trackList: List<TrackItem> = emptyList()
|
||||||
|
|
||||||
var chapters:List<ChapterItem> = emptyList()
|
var chapters:List<ChapterItem> = emptyList()
|
||||||
private set
|
private set
|
||||||
@ -73,6 +76,7 @@ class MangaDetailsPresenter(private val controller: MangaDetailsController,
|
|||||||
headerItem.isLocked = isLockedFromSearch
|
headerItem.isLocked = isLockedFromSearch
|
||||||
downloadManager.addListener(this)
|
downloadManager.addListener(this)
|
||||||
LibraryUpdateService.setListener(this)
|
LibraryUpdateService.setListener(this)
|
||||||
|
tracks = db.getTracks(manga).executeAsBlocking()
|
||||||
if (!manga.initialized) {
|
if (!manga.initialized) {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
controller.setRefresh(true)
|
controller.setRefresh(true)
|
||||||
@ -81,9 +85,9 @@ class MangaDetailsPresenter(private val controller: MangaDetailsController,
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
updateChapters()
|
updateChapters()
|
||||||
tracks = db.getTracks(manga).executeAsBlocking()
|
|
||||||
controller.updateChapters(this.chapters)
|
controller.updateChapters(this.chapters)
|
||||||
}
|
}
|
||||||
|
fetchTrackings()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onDestroy() {
|
fun onDestroy() {
|
||||||
@ -161,7 +165,7 @@ class MangaDetailsPresenter(private val controller: MangaDetailsController,
|
|||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Sets the active display mode.
|
* Sets the active display mode.
|
||||||
* @param mode the mode to set.
|
* @param hide set title to hidden
|
||||||
*/
|
*/
|
||||||
fun hideTitle(hide: Boolean) {
|
fun hideTitle(hide: Boolean) {
|
||||||
manga.displayMode = if (hide) Manga.DISPLAY_NUMBER else Manga.DISPLAY_NAME
|
manga.displayMode = if (hide) Manga.DISPLAY_NUMBER else Manga.DISPLAY_NAME
|
||||||
@ -658,7 +662,124 @@ class MangaDetailsPresenter(private val controller: MangaDetailsController,
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isTracked(): Boolean {
|
fun isTracked(): Boolean = loggedServices.any { service -> tracks.any { it.sync_id == service.id } }
|
||||||
return 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -54,7 +54,7 @@ class MangaHeaderHolder(
|
|||||||
filters_text.setOnClickListener { adapter.coverListener?.showChapterFilter() }
|
filters_text.setOnClickListener { adapter.coverListener?.showChapterFilter() }
|
||||||
chapters_title.setOnClickListener { adapter.coverListener?.showChapterFilter() }
|
chapters_title.setOnClickListener { adapter.coverListener?.showChapterFilter() }
|
||||||
webview_button.setOnClickListener { adapter.coverListener?.openInWebView() }
|
webview_button.setOnClickListener { adapter.coverListener?.openInWebView() }
|
||||||
share_button.setOnClickListener { adapter.coverListener?.prepareToShareManga() }
|
share_button.setOnClickListener { adapter.coverListener?.prepareToShareManga() }
|
||||||
favorite_button.setOnClickListener {
|
favorite_button.setOnClickListener {
|
||||||
adapter.coverListener?.favoriteManga(false)
|
adapter.coverListener?.favoriteManga(false)
|
||||||
}
|
}
|
||||||
@ -71,6 +71,7 @@ class MangaHeaderHolder(
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
manga_cover.setOnClickListener { adapter.coverListener?.zoomImageFromThumb(cover_card) }
|
manga_cover.setOnClickListener { adapter.coverListener?.zoomImageFromThumb(cover_card) }
|
||||||
|
track_button.setOnClickListener { adapter.coverListener?.showTrackingSheet() }
|
||||||
if (startExpanded)
|
if (startExpanded)
|
||||||
expandDesc()
|
expandDesc()
|
||||||
}
|
}
|
||||||
@ -144,6 +145,7 @@ class MangaHeaderHolder(
|
|||||||
val tracked = presenter.isTracked() && !item.isLocked
|
val tracked = presenter.isTracked() && !item.isLocked
|
||||||
|
|
||||||
with(track_button) {
|
with(track_button) {
|
||||||
|
visibleIf(presenter.hasTrackers())
|
||||||
text = itemView.context.getString(if (tracked) R.string.action_filter_tracked
|
text = itemView.context.getString(if (tracked) R.string.action_filter_tracked
|
||||||
else R.string.tracking)
|
else R.string.tracking)
|
||||||
|
|
||||||
@ -232,6 +234,19 @@ class MangaHeaderHolder(
|
|||||||
true_backdrop.setBackgroundColor(color)
|
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 {
|
override fun onLongClick(view: View?): Boolean {
|
||||||
super.onLongClick(view)
|
super.onLongClick(view)
|
||||||
return false
|
return false
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
@ -71,5 +71,6 @@ class ChaptersAdapter(
|
|||||||
fun favoriteManga(longPress: Boolean)
|
fun favoriteManga(longPress: Boolean)
|
||||||
fun copyToClipboard(content: String, label: Int)
|
fun copyToClipboard(content: String, label: Int)
|
||||||
fun zoomImageFromThumb(thumbView: View)
|
fun zoomImageFromThumb(thumbView: View)
|
||||||
|
fun showTrackingSheet()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import android.widget.NumberPicker
|
|||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.afollestad.materialdialogs.customview.customView
|
import com.afollestad.materialdialogs.customview.customView
|
||||||
import com.afollestad.materialdialogs.customview.getCustomView
|
import com.afollestad.materialdialogs.customview.getCustomView
|
||||||
import com.bluelinelabs.conductor.Controller
|
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Track
|
import eu.kanade.tachiyomi.data.database.models.Track
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
@ -15,14 +14,15 @@ import uy.kohesive.injekt.Injekt
|
|||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class SetTrackChaptersDialog<T> : DialogController
|
class SetTrackChaptersDialog<T> : DialogController
|
||||||
where T : Controller, T : SetTrackChaptersDialog.Listener {
|
where T : SetTrackChaptersDialog.Listener {
|
||||||
|
|
||||||
private val item: TrackItem
|
private val item: TrackItem
|
||||||
|
private lateinit var listener: Listener
|
||||||
|
|
||||||
constructor(target: T, item: TrackItem) : super(Bundle().apply {
|
constructor(target: T, item: TrackItem) : super(Bundle().apply {
|
||||||
putSerializable(KEY_ITEM_TRACK, item.track)
|
putSerializable(KEY_ITEM_TRACK, item.track)
|
||||||
}) {
|
}) {
|
||||||
targetController = target
|
listener = target
|
||||||
this.item = item
|
this.item = item
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ class SetTrackChaptersDialog<T> : DialogController
|
|||||||
// Remove focus to update selected number
|
// Remove focus to update selected number
|
||||||
val np: NumberPicker = view.findViewById(R.id.chapters_picker)
|
val np: NumberPicker = view.findViewById(R.id.chapters_picker)
|
||||||
np.clearFocus()
|
np.clearFocus()
|
||||||
(targetController as? Listener)?.setChaptersRead(item, np.value)
|
listener.setChaptersRead(item, np.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
val view = dialog.getCustomView()
|
val view = dialog.getCustomView()
|
||||||
|
@ -15,14 +15,15 @@ import uy.kohesive.injekt.Injekt
|
|||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class SetTrackScoreDialog<T> : DialogController
|
class SetTrackScoreDialog<T> : DialogController
|
||||||
where T : Controller, T : SetTrackScoreDialog.Listener {
|
where T : SetTrackScoreDialog.Listener {
|
||||||
|
|
||||||
private val item: TrackItem
|
private val item: TrackItem
|
||||||
|
private lateinit var listener: Listener
|
||||||
|
|
||||||
constructor(target: T, item: TrackItem) : super(Bundle().apply {
|
constructor(target: T, item: TrackItem) : super(Bundle().apply {
|
||||||
putSerializable(KEY_ITEM_TRACK, item.track)
|
putSerializable(KEY_ITEM_TRACK, item.track)
|
||||||
}) {
|
}) {
|
||||||
targetController = target
|
listener = target
|
||||||
this.item = item
|
this.item = item
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,8 +47,7 @@ class SetTrackScoreDialog<T> : DialogController
|
|||||||
val np: NumberPicker = view.findViewById(R.id.score_picker)
|
val np: NumberPicker = view.findViewById(R.id.score_picker)
|
||||||
np.clearFocus()
|
np.clearFocus()
|
||||||
|
|
||||||
(targetController as? Listener)?.setScore(item, np.value)
|
listener.setScore(item, np.value)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import android.app.Dialog
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.afollestad.materialdialogs.list.listItemsSingleChoice
|
import com.afollestad.materialdialogs.list.listItemsSingleChoice
|
||||||
import com.bluelinelabs.conductor.Controller
|
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Track
|
import eu.kanade.tachiyomi.data.database.models.Track
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
@ -13,14 +12,16 @@ import uy.kohesive.injekt.Injekt
|
|||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class SetTrackStatusDialog<T> : DialogController
|
class SetTrackStatusDialog<T> : DialogController
|
||||||
where T : Controller, T : SetTrackStatusDialog.Listener {
|
where T : SetTrackStatusDialog.Listener {
|
||||||
|
|
||||||
private val item: TrackItem
|
private val item: TrackItem
|
||||||
|
private lateinit var listener: Listener
|
||||||
|
|
||||||
constructor(target: T, item: TrackItem) : super(Bundle().apply {
|
constructor(target: T, item: TrackItem) : super(Bundle().apply {
|
||||||
putSerializable(KEY_ITEM_TRACK, item.track)
|
putSerializable(KEY_ITEM_TRACK, item.track)
|
||||||
}) {
|
}) {
|
||||||
targetController = target
|
listener = target
|
||||||
|
// targetController = target
|
||||||
this.item = item
|
this.item = item
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ class SetTrackStatusDialog<T> : DialogController
|
|||||||
.listItemsSingleChoice(items = statusString, initialSelection = selectedIndex,
|
.listItemsSingleChoice(items = statusString, initialSelection = selectedIndex,
|
||||||
waitForPositiveButton = false)
|
waitForPositiveButton = false)
|
||||||
{ dialog, position, _ ->
|
{ dialog, position, _ ->
|
||||||
(targetController as? Listener)?.setStatus(item, position)
|
listener.setStatus(item, position)
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package eu.kanade.tachiyomi.ui.manga.track
|
package eu.kanade.tachiyomi.ui.manga.track
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
import eu.kanade.tachiyomi.util.view.inflate
|
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>()
|
var items = emptyList<TrackItem>()
|
||||||
set(value) {
|
set(value) {
|
||||||
@ -34,9 +35,13 @@ class TrackAdapter(controller: TrackController) : RecyclerView.Adapter<TrackHold
|
|||||||
holder.bind(items[position])
|
holder.bind(items[position])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun indexOf(item: TrackService?):Int {
|
||||||
|
return items.indexOfFirst { item?.id == it.service.id }
|
||||||
|
}
|
||||||
|
|
||||||
interface OnClickListener {
|
interface OnClickListener {
|
||||||
fun onLogoClick(position: Int)
|
fun onLogoClick(position: Int)
|
||||||
fun onTitleClick(position: Int)
|
fun onSetClick(position: Int)
|
||||||
fun onStatusClick(position: Int)
|
fun onStatusClick(position: Int)
|
||||||
fun onChaptersClick(position: Int)
|
fun onChaptersClick(position: Int)
|
||||||
fun onScoreClick(position: Int)
|
fun onScoreClick(position: Int)
|
||||||
|
@ -119,7 +119,7 @@ class TrackController : NucleusController<TrackPresenter>(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTitleClick(position: Int) {
|
override fun onSetClick(position: Int) {
|
||||||
val item = adapter?.getItem(position) ?: return
|
val item = adapter?.getItem(position) ?: return
|
||||||
TrackSearchDialog(this, item.service, item.track != null).showDialog(router,
|
TrackSearchDialog(this, item.service, item.track != null).showDialog(router,
|
||||||
TAG_SEARCH_CONTROLLER)
|
TAG_SEARCH_CONTROLLER)
|
||||||
|
@ -2,8 +2,8 @@ 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.R
|
|
||||||
import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder
|
import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder
|
||||||
|
import eu.kanade.tachiyomi.util.view.visibleIf
|
||||||
import kotlinx.android.synthetic.main.track_item.*
|
import kotlinx.android.synthetic.main.track_item.*
|
||||||
|
|
||||||
class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
|
class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
|
||||||
@ -11,32 +11,28 @@ class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
|
|||||||
init {
|
init {
|
||||||
val listener = adapter.rowClickListener
|
val listener = adapter.rowClickListener
|
||||||
logo_container.setOnClickListener { listener.onLogoClick(adapterPosition) }
|
logo_container.setOnClickListener { listener.onLogoClick(adapterPosition) }
|
||||||
title_container.setOnClickListener { listener.onTitleClick(adapterPosition) }
|
track_set.setOnClickListener { listener.onSetClick(adapterPosition) }
|
||||||
status_container.setOnClickListener { listener.onStatusClick(adapterPosition) }
|
status_container.setOnClickListener { listener.onStatusClick(adapterPosition) }
|
||||||
chapters_container.setOnClickListener { listener.onChaptersClick(adapterPosition) }
|
chapters_container.setOnClickListener { listener.onChaptersClick(adapterPosition) }
|
||||||
score_container.setOnClickListener { listener.onScoreClick(adapterPosition) }
|
score_container.setOnClickListener { listener.onScoreClick(adapterPosition) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
fun bind(item: TrackItem) {
|
fun bind(item: TrackItem) {
|
||||||
val track = item.track
|
val track = item.track
|
||||||
track_logo.setImageResource(item.service.getLogo())
|
track_logo.setImageResource(item.service.getLogo())
|
||||||
logo_container.setBackgroundColor(item.service.getLogoColor())
|
logo_container.setBackgroundColor(item.service.getLogoColor())
|
||||||
|
track_group.visibleIf(track != null)
|
||||||
if (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}/" +
|
track_chapters.text = "${track.last_chapter_read}/" +
|
||||||
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)
|
||||||
} 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,10 @@ import eu.kanade.tachiyomi.data.track.TrackManager
|
|||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
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 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.*
|
||||||
import kotlinx.android.synthetic.main.track_search_dialog.view.track_search
|
|
||||||
import kotlinx.android.synthetic.main.track_search_dialog.view.track_search_list
|
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
import rx.subscriptions.CompositeSubscription
|
import rx.subscriptions.CompositeSubscription
|
||||||
@ -41,10 +40,14 @@ class TrackSearchDialog : DialogController {
|
|||||||
|
|
||||||
private var searchTextSubscription: Subscription? = null
|
private var searchTextSubscription: Subscription? = null
|
||||||
|
|
||||||
private val trackController
|
private lateinit var bottomSheet: TrackingBottomSheet
|
||||||
get() = targetController as TrackController
|
//private val trackController
|
||||||
|
// get() = targetController as TrackController
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private var wasPreviouslyTracked:Boolean = false
|
private var wasPreviouslyTracked:Boolean = false
|
||||||
|
private lateinit var presenter:MangaDetailsPresenter
|
||||||
|
|
||||||
constructor(target: TrackController, service: TrackService, wasTracked:Boolean) : super(Bundle()
|
constructor(target: TrackController, service: TrackService, wasTracked:Boolean) : super(Bundle()
|
||||||
.apply {
|
.apply {
|
||||||
@ -55,6 +58,16 @@ class TrackSearchDialog : DialogController {
|
|||||||
this.service = service
|
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")
|
@Suppress("unused")
|
||||||
constructor(bundle: Bundle) : super(bundle) {
|
constructor(bundle: Bundle) : super(bundle) {
|
||||||
service = Injekt.get<TrackManager>().getService(bundle.getInt(KEY_SERVICE))!!
|
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
|
// Do an initial search based on the manga's title
|
||||||
if (savedState == null) {
|
if (savedState == null) {
|
||||||
val title = trackController.presenter.manga.originalTitle()
|
val title = presenter.manga.originalTitle()
|
||||||
view.track_search.append(title)
|
view.track_search.append(title)
|
||||||
search(title)
|
search(title)
|
||||||
}
|
}
|
||||||
@ -129,7 +142,7 @@ class TrackSearchDialog : DialogController {
|
|||||||
val view = dialogView ?: return
|
val view = dialogView ?: return
|
||||||
view.progress.visibility = View.VISIBLE
|
view.progress.visibility = View.VISIBLE
|
||||||
view.track_search_list.visibility = View.INVISIBLE
|
view.track_search_list.visibility = View.INVISIBLE
|
||||||
trackController.presenter.search(query, service)
|
presenter.trackSearch(query, service)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSearchResults(results: List<TrackSearch>) {
|
fun onSearchResults(results: List<TrackSearch>) {
|
||||||
@ -153,8 +166,10 @@ class TrackSearchDialog : DialogController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onPositiveButtonClick() {
|
private fun onPositiveButtonClick() {
|
||||||
trackController.swipe_refresh.isRefreshing = true
|
// trackController.swipe_refresh.isRefreshing = true
|
||||||
trackController.presenter.registerTracking(selectedItem, service)
|
bottomSheet.refreshTrack(service)
|
||||||
|
presenter.registerTracking(selectedItem,
|
||||||
|
service)
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
@ -169,7 +169,7 @@ inline val View.marginLeft: Int
|
|||||||
|
|
||||||
object RecyclerWindowInsetsListener : View.OnApplyWindowInsetsListener {
|
object RecyclerWindowInsetsListener : View.OnApplyWindowInsetsListener {
|
||||||
override fun onApplyWindowInsets(v: View, insets: WindowInsets): WindowInsets {
|
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)
|
//v.updatePaddingRelative(bottom = v.paddingBottom + insets.systemWindowInsetBottom)
|
||||||
return insets
|
return insets
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,29 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/track"
|
||||||
style="@style/Theme.Widget.CardView.Item"
|
style="@style/Theme.Widget.CardView.Item"
|
||||||
android:padding="0dp">
|
android:padding="0dp">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
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
|
<FrameLayout
|
||||||
android:id="@+id/logo_container"
|
android:id="@+id/logo_container"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="0dp"
|
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_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/status_container"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
android:clickable="true"
|
|
||||||
tools:background="#2E51A2">
|
tools:background="#2E51A2">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@ -26,48 +33,21 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
tools:src="@drawable/tracker_mal" />
|
tools:src="@drawable/tracker_mal" />
|
||||||
|
|
||||||
</FrameLayout>
|
<ProgressBar
|
||||||
|
android:id="@+id/progress"
|
||||||
<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"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/title" />
|
android:layout_gravity="center"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<TextView
|
</FrameLayout>
|
||||||
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" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/track_group"
|
||||||
<View
|
android:layout_width="wrap_content"
|
||||||
android:id="@+id/divider1"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="0dp"
|
app:constraint_referenced_ids="chapters_container,status_container,score_container" />
|
||||||
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" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/status_container"
|
android:id="@+id/status_container"
|
||||||
@ -75,106 +55,114 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/selectable_list_drawable"
|
android:background="?attr/selectable_list_drawable"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:padding="16dp"
|
android:focusable="true"
|
||||||
app:layout_constraintStart_toEndOf="@+id/logo_container"
|
android:orientation="vertical"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintStart_toEndOf="@id/logo_container"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/divider1">
|
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
|
<TextView
|
||||||
style="@style/TextAppearance.Regular.Body1"
|
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/status" />
|
android:text="@string/status" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/track_status"
|
android:id="@+id/track_status"
|
||||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginTop="8dp"
|
||||||
android:gravity="end"
|
tools:text="Currently Reading" />
|
||||||
tools:text="Reading" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</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
|
<LinearLayout
|
||||||
android:id="@+id/chapters_container"
|
android:id="@+id/chapters_container"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
|
app:layout_constrainedHeight="true"
|
||||||
android:background="?attr/selectable_list_drawable"
|
android:background="?attr/selectable_list_drawable"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:padding="16dp"
|
android:focusable="true"
|
||||||
app:layout_constraintStart_toEndOf="@+id/logo_container"
|
android:orientation="vertical"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:paddingStart="8dp"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/divider2">
|
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
|
<TextView
|
||||||
style="@style/TextAppearance.Regular.Body1"
|
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/chapters" />
|
android:text="@string/chapters" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/track_chapters"
|
android:id="@+id/track_chapters"
|
||||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginTop="8dp"
|
||||||
android:gravity="end"
|
|
||||||
tools:text="12/24" />
|
tools:text="12/24" />
|
||||||
|
|
||||||
</LinearLayout>
|
</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
|
<LinearLayout
|
||||||
android:id="@+id/score_container"
|
android:id="@+id/score_container"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
android:background="?attr/selectable_list_drawable"
|
android:background="?attr/selectable_list_drawable"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:padding="16dp"
|
android:focusable="true"
|
||||||
app:layout_constraintStart_toEndOf="@+id/logo_container"
|
android:orientation="vertical"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/divider3">
|
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
|
<TextView
|
||||||
style="@style/TextAppearance.Regular.Body1"
|
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/score" />
|
android:text="@string/score" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/track_score"
|
android:id="@+id/track_score"
|
||||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginTop="8dp"
|
||||||
android:gravity="end"
|
|
||||||
tools:text="10" />
|
tools:text="10" />
|
||||||
|
|
||||||
</LinearLayout>
|
</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.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
</com.google.android.material.card.MaterialCardView>
|
14
app/src/main/res/layout/tracking_bottom_sheet.xml
Normal file
14
app/src/main/res/layout/tracking_bottom_sheet.xml
Normal 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>
|
@ -149,7 +149,7 @@
|
|||||||
<item name="layout_behavior">eu.kanade.tachiyomi.widget.FABMoveBehaviour</item>
|
<item name="layout_behavior">eu.kanade.tachiyomi.widget.FABMoveBehaviour</item>
|
||||||
</style>
|
</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_width">match_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:padding">@dimen/material_component_cards_top_and_bottom_padding</item>
|
<item name="android:padding">@dimen/material_component_cards_top_and_bottom_padding</item>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user