From 29134f6bb0765f9793405a73126880176d26b54d Mon Sep 17 00:00:00 2001 From: Jay Date: Mon, 9 Mar 2020 23:34:00 -0700 Subject: [PATCH] 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 --- .../data/track/anilist/AnilistModels.kt | 36 ++-- .../ui/manga/MangaDetailsController.kt | 35 ++++ .../ui/manga/MangaDetailsPresenter.kt | 129 +++++++++++- .../tachiyomi/ui/manga/MangaHeaderHolder.kt | 17 +- .../tachiyomi/ui/manga/TrackingBottomSheet.kt | 185 ++++++++++++++++++ .../ui/manga/chapter/ChaptersAdapter.kt | 1 + .../ui/manga/track/SetTrackChaptersDialog.kt | 8 +- .../ui/manga/track/SetTrackScoreDialog.kt | 8 +- .../ui/manga/track/SetTrackStatusDialog.kt | 9 +- .../tachiyomi/ui/manga/track/TrackAdapter.kt | 11 +- .../ui/manga/track/TrackController.kt | 2 +- .../tachiyomi/ui/manga/track/TrackHolder.kt | 20 +- .../ui/manga/track/TrackSearchDialog.kt | 35 +++- .../tachiyomi/util/view/ViewExtensions.kt | 2 +- app/src/main/res/layout/track_item.xml | 170 ++++++++-------- .../main/res/layout/tracking_bottom_sheet.xml | 14 ++ app/src/main/res/values/styles.xml | 2 +- 17 files changed, 526 insertions(+), 158 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/manga/TrackingBottomSheet.kt create mode 100644 app/src/main/res/layout/tracking_bottom_sheet.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt index 6877b6efb6..5eba6f373c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt @@ -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().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") } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt index 5c384b9bfd..42e293b689 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt @@ -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) { + trackingBottomSheet?.onNextTrackings(trackings) + } + + fun onTrackSearchResults(results: List) { + 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() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt index 1e4c66c524..ae4975432f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt @@ -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().services.filter { it.isLogged } } var tracks = emptyList() + var trackList: List = emptyList() var chapters:List = 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) } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt index ab714f43ab..7fa6f69282 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/TrackingBottomSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/TrackingBottomSheet.kt new file mode 100644 index 0000000000..63c7b35608 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/TrackingBottomSheet.kt @@ -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) { + onRefreshDone() + adapter?.items = trackings + controller.refreshTracker() + } + + fun onSearchResults(results: List) { + 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" + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersAdapter.kt index ff7375c5fa..906391f69e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersAdapter.kt @@ -71,5 +71,6 @@ class ChaptersAdapter( fun favoriteManga(longPress: Boolean) fun copyToClipboard(content: String, label: Int) fun zoomImageFromThumb(thumbView: View) + fun showTrackingSheet() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/SetTrackChaptersDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/SetTrackChaptersDialog.kt index dd3f65d98f..5f59acea52 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/SetTrackChaptersDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/SetTrackChaptersDialog.kt @@ -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 : 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 : 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() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/SetTrackScoreDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/SetTrackScoreDialog.kt index 6aac10037e..b85280289f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/SetTrackScoreDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/SetTrackScoreDialog.kt @@ -15,14 +15,15 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get class SetTrackScoreDialog : 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 : DialogController val np: NumberPicker = view.findViewById(R.id.score_picker) np.clearFocus() - (targetController as? Listener)?.setScore(item, np.value) - + listener.setScore(item, np.value) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/SetTrackStatusDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/SetTrackStatusDialog.kt index 42ccd06a9f..7047b8f39c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/SetTrackStatusDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/SetTrackStatusDialog.kt @@ -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 : 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 : DialogController .listItemsSingleChoice(items = statusString, initialSelection = selectedIndex, waitForPositiveButton = false) { dialog, position, _ -> - (targetController as? Listener)?.setStatus(item, position) + listener.setStatus(item, position) dialog.dismiss() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt index 4f4daf86c5..9d8599e516 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt @@ -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() { +class TrackAdapter(controller: OnClickListener) : RecyclerView.Adapter() { var items = emptyList() set(value) { @@ -34,9 +35,13 @@ class TrackAdapter(controller: TrackController) : RecyclerView.Adapter(), } } - 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) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackHolder.kt index c8c9da6887..d56d97eea6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackHolder.kt @@ -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) + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchDialog.kt index e6f6337f10..d5c5ab3c41 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchDialog.kt @@ -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().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) { @@ -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 { diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt index 855ec9c7ec..1fb821f6a3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt @@ -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 } diff --git a/app/src/main/res/layout/track_item.xml b/app/src/main/res/layout/track_item.xml index ba771d41c4..f0a71d0f1e 100644 --- a/app/src/main/res/layout/track_item.xml +++ b/app/src/main/res/layout/track_item.xml @@ -1,22 +1,29 @@ - + android:layout_height="wrap_content" + android:background="?android:attr/colorBackground" + android:minHeight="?attr/actionBarSize"> - - - - - + android:layout_gravity="center" + android:padding="4dp" + android:visibility="gone"/> - + - - - + + 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"> + android:layout_marginTop="8dp" + tools:text="Currently Reading" /> - - + 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"> - - + 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"> + + - + \ No newline at end of file diff --git a/app/src/main/res/layout/tracking_bottom_sheet.xml b/app/src/main/res/layout/tracking_bottom_sheet.xml new file mode 100644 index 0000000000..2bb015b59c --- /dev/null +++ b/app/src/main/res/layout/tracking_bottom_sheet.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 3cd736afed..e943e0b88d 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -149,7 +149,7 @@ eu.kanade.tachiyomi.widget.FABMoveBehaviour -