mirror of
https://github.com/mihonapp/mihon.git
synced 2025-07-02 05:57:50 +02:00
Tracking sheet and search adjustments (#5427)
* Tracking sheet and search visual adjustments * Remove track item divider * Add start margin to "add tracking" button * Fix track search dialog crash when no item chosen * Show "remove" action only when track item is previously set * Remove placeholder for total chapters * Cleanups * Add track search error/empty result message * Make track search dialog fullscreen * Use AutofitRecyclerView for track search dialog * Fix text input overlapping * Run track search from IME action instead * Remove deprecated method * Reformat * Set track search error message on the placeholder * Use payload to notify track search item change * Fix track search action icon tint color
This commit is contained in:
@ -1118,8 +1118,7 @@ class MangaController :
|
||||
|
||||
fun onTrackingSearchResultsError(error: Throwable) {
|
||||
Timber.e(error)
|
||||
activity?.toast(error.message)
|
||||
getTrackingSearchDialog()?.onSearchResultsError()
|
||||
getTrackingSearchDialog()?.onSearchResultsError(error.message)
|
||||
}
|
||||
|
||||
private fun getTrackingSearchDialog(): TrackSearchDialog? {
|
||||
|
@ -4,6 +4,8 @@ import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.tachiyomi.databinding.TrackItemBinding
|
||||
import eu.kanade.tachiyomi.util.view.applyElevationOverlay
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class TrackAdapter(listener: OnClickListener) : RecyclerView.Adapter<TrackHolder>() {
|
||||
|
||||
@ -29,6 +31,7 @@ class TrackAdapter(listener: OnClickListener) : RecyclerView.Adapter<TrackHolder
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackHolder {
|
||||
binding = TrackItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
binding.card.applyElevationOverlay()
|
||||
return TrackHolder(binding, this)
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.manga.track
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.databinding.TrackItemBinding
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
@ -37,38 +38,64 @@ class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter)
|
||||
fun bind(item: TrackItem) {
|
||||
val track = item.track
|
||||
binding.trackLogo.setImageResource(item.service.getLogo())
|
||||
binding.logoContainer.setBackgroundColor(item.service.getLogoColor())
|
||||
binding.logoContainer.setCardBackgroundColor(item.service.getLogoColor())
|
||||
|
||||
binding.trackSet.isVisible = track == null
|
||||
binding.trackTitle.isVisible = track != null
|
||||
|
||||
binding.topDivider.isVisible = track != null
|
||||
binding.middleRow.isVisible = track != null
|
||||
binding.bottomDivider.isVisible = track != null
|
||||
binding.bottomRow.isVisible = track != null
|
||||
|
||||
binding.card.isVisible = track != null
|
||||
|
||||
if (track != null) {
|
||||
val ctx = binding.trackTitle.context
|
||||
binding.trackTitle.text = track.title
|
||||
binding.trackChapters.text = "${track.last_chapter_read}/" +
|
||||
if (track.total_chapters > 0) track.total_chapters else "-"
|
||||
binding.trackChapters.text = track.last_chapter_read.toString()
|
||||
if (track.total_chapters > 0) {
|
||||
binding.trackChapters.text = "${binding.trackChapters.text} / ${track.total_chapters}"
|
||||
}
|
||||
binding.trackStatus.text = item.service.getStatus(track.status)
|
||||
|
||||
if (item.service.getScoreList().isEmpty()) {
|
||||
binding.trackScore.isVisible = false
|
||||
binding.vertDivider2.isVisible = false
|
||||
} else {
|
||||
binding.trackScore.text = if (track.score == 0f) "-" else item.service.displayScore(track)
|
||||
val supportsScoring = item.service.getScoreList().isNotEmpty()
|
||||
if (supportsScoring) {
|
||||
if (track.score != 0F) {
|
||||
item.service.getScoreList()
|
||||
binding.trackScore.text = item.service.displayScore(track)
|
||||
binding.trackScore.alpha = SET_STATUS_TEXT_ALPHA
|
||||
} else {
|
||||
binding.trackScore.text = ctx.getString(R.string.score)
|
||||
binding.trackScore.alpha = UNSET_STATUS_TEXT_ALPHA
|
||||
}
|
||||
}
|
||||
binding.trackScore.isVisible = supportsScoring
|
||||
binding.vertDivider2.isVisible = supportsScoring
|
||||
|
||||
if (item.service.supportsReadingDates) {
|
||||
binding.trackStartDate.text =
|
||||
if (track.started_reading_date != 0L) dateFormat.format(track.started_reading_date) else "-"
|
||||
binding.trackFinishDate.text =
|
||||
if (track.finished_reading_date != 0L) dateFormat.format(track.finished_reading_date) else "-"
|
||||
} else {
|
||||
binding.bottomDivider.isVisible = false
|
||||
binding.bottomRow.isVisible = false
|
||||
val supportsReadingDates = item.service.supportsReadingDates
|
||||
if (supportsReadingDates) {
|
||||
if (track.started_reading_date != 0L) {
|
||||
binding.trackStartDate.text = dateFormat.format(track.started_reading_date)
|
||||
binding.trackStartDate.alpha = SET_STATUS_TEXT_ALPHA
|
||||
} else {
|
||||
binding.trackStartDate.text = ctx.getString(R.string.track_started_reading_date)
|
||||
binding.trackStartDate.alpha = UNSET_STATUS_TEXT_ALPHA
|
||||
}
|
||||
if (track.finished_reading_date != 0L) {
|
||||
binding.trackFinishDate.text = dateFormat.format(track.finished_reading_date)
|
||||
binding.trackFinishDate.alpha = SET_STATUS_TEXT_ALPHA
|
||||
} else {
|
||||
binding.trackFinishDate.text = ctx.getString(R.string.track_finished_reading_date)
|
||||
binding.trackFinishDate.alpha = UNSET_STATUS_TEXT_ALPHA
|
||||
}
|
||||
}
|
||||
binding.bottomDivider.isVisible = supportsReadingDates
|
||||
binding.bottomRow.isVisible = supportsReadingDates
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SET_STATUS_TEXT_ALPHA = 1F
|
||||
private const val UNSET_STATUS_TEXT_ALPHA = 0.5F
|
||||
}
|
||||
}
|
||||
|
@ -1,76 +1,57 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.track
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.core.view.isVisible
|
||||
import coil.clear
|
||||
import coil.load
|
||||
import eu.kanade.tachiyomi.R
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.databinding.TrackSearchItemBinding
|
||||
import eu.kanade.tachiyomi.util.view.inflate
|
||||
import eu.kanade.tachiyomi.util.view.applyElevationOverlay
|
||||
|
||||
class TrackSearchAdapter(context: Context) :
|
||||
ArrayAdapter<TrackSearch>(context, R.layout.track_search_item, mutableListOf<TrackSearch>()) {
|
||||
class TrackSearchAdapter(
|
||||
private val currentTrackUrl: String?,
|
||||
private val onSelectionChanged: (TrackSearch?) -> Unit
|
||||
) : RecyclerView.Adapter<TrackSearchHolder>() {
|
||||
var selectedItemPosition = -1
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
val previousPosition = field
|
||||
field = value
|
||||
// Just notify the now-unselected item
|
||||
notifyItemChanged(previousPosition, UncheckPayload)
|
||||
onSelectionChanged(items.getOrNull(value))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getView(position: Int, view: View?, parent: ViewGroup): View {
|
||||
var v = view
|
||||
// Get the data item for this position
|
||||
val track = getItem(position)!!
|
||||
// Check if an existing view is being reused, otherwise inflate the view
|
||||
val holder: TrackSearchHolder // view lookup cache stored in tag
|
||||
if (v == null) {
|
||||
v = parent.inflate(R.layout.track_search_item)
|
||||
holder = TrackSearchHolder(v)
|
||||
v.tag = holder
|
||||
var items = emptyList<TrackSearch>()
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
selectedItemPosition = value.indexOfFirst { it.tracking_url == currentTrackUrl }
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackSearchHolder {
|
||||
val binding = TrackSearchItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
binding.container.applyElevationOverlay()
|
||||
return TrackSearchHolder(binding, this)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TrackSearchHolder, position: Int) {
|
||||
holder.bind(items[position], position)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TrackSearchHolder, position: Int, payloads: MutableList<Any>) {
|
||||
if (payloads.getOrNull(0) == UncheckPayload) {
|
||||
holder.setUnchecked()
|
||||
} else {
|
||||
holder = v.tag as TrackSearchHolder
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
}
|
||||
holder.onSetValues(track)
|
||||
return v
|
||||
}
|
||||
|
||||
fun setItems(syncs: List<TrackSearch>) {
|
||||
setNotifyOnChange(false)
|
||||
clear()
|
||||
addAll(syncs)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
class TrackSearchHolder(private val view: View) {
|
||||
|
||||
private val binding = TrackSearchItemBinding.bind(view)
|
||||
|
||||
fun onSetValues(track: TrackSearch) {
|
||||
binding.trackSearchTitle.text = track.title
|
||||
binding.trackSearchSummary.text = track.summary
|
||||
binding.trackSearchCover.clear()
|
||||
if (track.cover_url.isNotEmpty()) {
|
||||
binding.trackSearchCover.load(track.cover_url)
|
||||
}
|
||||
|
||||
val hasStatus = track.publishing_status.isNotBlank()
|
||||
binding.trackSearchStatus.isVisible = hasStatus
|
||||
binding.trackSearchStatusResult.isVisible = hasStatus
|
||||
if (hasStatus) {
|
||||
binding.trackSearchStatusResult.text = track.publishing_status.capitalize()
|
||||
}
|
||||
|
||||
val hasType = track.publishing_type.isNotBlank()
|
||||
binding.trackSearchType.isVisible = hasType
|
||||
binding.trackSearchTypeResult.isVisible = hasType
|
||||
if (hasType) {
|
||||
binding.trackSearchTypeResult.text = track.publishing_type.capitalize()
|
||||
}
|
||||
|
||||
val hasStartDate = track.start_date.isNotBlank()
|
||||
binding.trackSearchStart.isVisible = hasStartDate
|
||||
binding.trackSearchStartResult.isVisible = hasStartDate
|
||||
if (hasStartDate) {
|
||||
binding.trackSearchStartResult.text = track.start_date
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
private object UncheckPayload
|
||||
}
|
||||
}
|
||||
|
@ -2,29 +2,31 @@ package eu.kanade.tachiyomi.ui.manga.track
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.appcompat.app.AppCompatDialog
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.customview.customView
|
||||
import dev.chrisbanes.insetter.applyInsetter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
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.databinding.TrackSearchDialogBinding
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import eu.kanade.tachiyomi.util.view.setNavigationBarTransparentCompat
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import reactivecircus.flowbinding.android.widget.itemClicks
|
||||
import reactivecircus.flowbinding.android.widget.textChanges
|
||||
import reactivecircus.flowbinding.android.widget.editorActionEvents
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class TrackSearchDialog : DialogController {
|
||||
|
||||
@ -32,59 +34,130 @@ class TrackSearchDialog : DialogController {
|
||||
|
||||
private var adapter: TrackSearchAdapter? = null
|
||||
|
||||
private var selectedItem: Track? = null
|
||||
|
||||
private val service: TrackService
|
||||
private val currentTrackUrl: String?
|
||||
|
||||
private val trackController
|
||||
get() = targetController as MangaController
|
||||
|
||||
constructor(target: MangaController, service: TrackService) : super(
|
||||
bundleOf(KEY_SERVICE to service.id)
|
||||
) {
|
||||
private lateinit var currentlySearched: String
|
||||
|
||||
constructor(
|
||||
target: MangaController,
|
||||
_service: TrackService,
|
||||
_currentTrackUrl: String?
|
||||
) : super(bundleOf(KEY_SERVICE to _service.id, KEY_CURRENT_URL to _currentTrackUrl)) {
|
||||
targetController = target
|
||||
this.service = service
|
||||
service = _service
|
||||
currentTrackUrl = _currentTrackUrl
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
constructor(bundle: Bundle) : super(bundle) {
|
||||
service = Injekt.get<TrackManager>().getService(bundle.getInt(KEY_SERVICE))!!
|
||||
currentTrackUrl = bundle.getString(KEY_CURRENT_URL)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
binding = TrackSearchDialogBinding.inflate(LayoutInflater.from(activity!!))
|
||||
val dialog = MaterialDialog(activity!!)
|
||||
.customView(view = binding!!.root)
|
||||
.positiveButton(android.R.string.ok) { onPositiveButtonClick() }
|
||||
.negativeButton(android.R.string.cancel)
|
||||
.neutralButton(R.string.action_remove) { onRemoveButtonClick() }
|
||||
|
||||
onViewCreated(dialog.view, savedViewState)
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
fun onViewCreated(view: View, savedState: Bundle?) {
|
||||
// Create adapter
|
||||
val adapter = TrackSearchAdapter(view.context)
|
||||
this.adapter = adapter
|
||||
binding!!.trackSearchList.adapter = adapter
|
||||
|
||||
// Set listeners
|
||||
selectedItem = null
|
||||
|
||||
binding!!.trackSearchList.itemClicks()
|
||||
.onEach { position ->
|
||||
selectedItem = adapter.getItem(position)
|
||||
// Toolbar stuff
|
||||
binding!!.toolbar.setNavigationOnClickListener { dialog?.dismiss() }
|
||||
binding!!.toolbar.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.done -> {
|
||||
val adapter = adapter ?: return@setOnMenuItemClickListener true
|
||||
val item = adapter.items.getOrNull(adapter.selectedItemPosition)
|
||||
if (item != null) {
|
||||
trackController.presenter.registerTracking(item, service)
|
||||
dialog?.dismiss()
|
||||
}
|
||||
}
|
||||
R.id.remove -> {
|
||||
trackController.presenter.unregisterTracking(service)
|
||||
dialog?.dismiss()
|
||||
}
|
||||
}
|
||||
.launchIn(trackController.viewScope)
|
||||
true
|
||||
}
|
||||
binding!!.toolbar.menu.findItem(R.id.remove).isVisible = currentTrackUrl != null
|
||||
|
||||
// Create adapter
|
||||
adapter = TrackSearchAdapter(currentTrackUrl) { which ->
|
||||
binding!!.toolbar.menu.findItem(R.id.done).isEnabled = which != null
|
||||
}
|
||||
binding!!.trackSearchRecyclerview.adapter = adapter
|
||||
|
||||
// Do an initial search based on the manga's title
|
||||
if (savedState == null) {
|
||||
val title = trackController.presenter.manga.title
|
||||
binding!!.trackSearch.append(title)
|
||||
search(title)
|
||||
if (savedViewState == null) {
|
||||
currentlySearched = trackController.presenter.manga.title
|
||||
binding!!.titleInput.editText?.append(currentlySearched)
|
||||
}
|
||||
search(currentlySearched)
|
||||
|
||||
// Input listener
|
||||
binding?.titleInput?.editText
|
||||
?.editorActionEvents {
|
||||
when (it.actionId) {
|
||||
EditorInfo.IME_ACTION_SEARCH -> {
|
||||
true
|
||||
}
|
||||
else -> {
|
||||
it.keyEvent?.action == KeyEvent.ACTION_DOWN && it.keyEvent?.keyCode == KeyEvent.KEYCODE_ENTER
|
||||
}
|
||||
}
|
||||
}
|
||||
?.filter { it.view.text.isNotBlank() }
|
||||
?.onEach {
|
||||
val query = it.view.text.toString()
|
||||
if (query != currentlySearched) {
|
||||
currentlySearched = query
|
||||
search(it.view.text.toString())
|
||||
it.view.context.getSystemService<InputMethodManager>()?.hideSoftInputFromWindow(it.view.windowToken, 0)
|
||||
it.view.clearFocus()
|
||||
}
|
||||
}
|
||||
?.launchIn(trackController.viewScope)
|
||||
|
||||
// Edge to edge
|
||||
binding!!.appbar.applyInsetter {
|
||||
type(navigationBars = true, statusBars = true) {
|
||||
padding(left = true, top = true, right = true)
|
||||
}
|
||||
}
|
||||
binding!!.titleInput.applyInsetter {
|
||||
type(navigationBars = true) {
|
||||
margin(horizontal = true)
|
||||
}
|
||||
}
|
||||
binding!!.progress.applyInsetter {
|
||||
type(navigationBars = true) {
|
||||
margin()
|
||||
}
|
||||
}
|
||||
binding!!.message.applyInsetter {
|
||||
type(navigationBars = true) {
|
||||
margin()
|
||||
}
|
||||
}
|
||||
binding!!.trackSearchRecyclerview.applyInsetter {
|
||||
type(navigationBars = true) {
|
||||
padding(vertical = true)
|
||||
margin(horizontal = true)
|
||||
}
|
||||
}
|
||||
|
||||
return AppCompatDialog(activity!!, R.style.ThemeOverlay_Tachiyomi_Dialog_Fullscreen).apply {
|
||||
setContentView(binding!!.root)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
super.onAttach(view)
|
||||
dialog?.window?.let { window ->
|
||||
window.setNavigationBarTransparentCompat(window.context)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,46 +167,39 @@ class TrackSearchDialog : DialogController {
|
||||
adapter = null
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
super.onAttach(view)
|
||||
binding!!.trackSearch.textChanges()
|
||||
.debounce(TimeUnit.SECONDS.toMillis(1))
|
||||
.filter { it.isNotBlank() }
|
||||
.onEach { search(it.toString()) }
|
||||
.launchIn(trackController.viewScope)
|
||||
}
|
||||
|
||||
private fun search(query: String) {
|
||||
val binding = binding ?: return
|
||||
binding.progress.isVisible = true
|
||||
binding.trackSearchList.isVisible = false
|
||||
binding.trackSearchRecyclerview.isVisible = false
|
||||
binding.message.isVisible = false
|
||||
trackController.presenter.trackingSearch(query, service)
|
||||
}
|
||||
|
||||
fun onSearchResults(results: List<TrackSearch>) {
|
||||
selectedItem = null
|
||||
val binding = binding ?: return
|
||||
binding.progress.isVisible = false
|
||||
binding.trackSearchList.isVisible = true
|
||||
adapter?.setItems(results)
|
||||
|
||||
val emptyResult = results.isEmpty()
|
||||
adapter?.items = results
|
||||
binding.trackSearchRecyclerview.isVisible = !emptyResult
|
||||
binding.trackSearchRecyclerview.scrollToPosition(0)
|
||||
binding.message.isVisible = emptyResult
|
||||
if (emptyResult) {
|
||||
binding.message.text = binding.message.context.getString(R.string.no_results_found)
|
||||
}
|
||||
}
|
||||
|
||||
fun onSearchResultsError() {
|
||||
fun onSearchResultsError(message: String?) {
|
||||
val binding = binding ?: return
|
||||
binding.progress.isVisible = false
|
||||
binding.trackSearchList.isVisible = false
|
||||
adapter?.setItems(emptyList())
|
||||
}
|
||||
|
||||
private fun onPositiveButtonClick() {
|
||||
trackController.presenter.registerTracking(selectedItem, service)
|
||||
}
|
||||
|
||||
private fun onRemoveButtonClick() {
|
||||
trackController.presenter.unregisterTracking(service)
|
||||
binding.trackSearchRecyclerview.isVisible = false
|
||||
binding.message.isVisible = true
|
||||
binding.message.text = message ?: binding.message.context.getString(R.string.unknown_error)
|
||||
adapter?.items = emptyList()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val KEY_SERVICE = "service_id"
|
||||
const val KEY_CURRENT_URL = "current_url"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.track
|
||||
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.clear
|
||||
import coil.load
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.databinding.TrackSearchItemBinding
|
||||
import eu.kanade.tachiyomi.util.view.setMaxLinesAndEllipsize
|
||||
import java.util.Locale
|
||||
|
||||
class TrackSearchHolder(
|
||||
private val binding: TrackSearchItemBinding,
|
||||
private val adapter: TrackSearchAdapter
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(track: TrackSearch, position: Int) {
|
||||
binding.container.isChecked = position == adapter.selectedItemPosition
|
||||
binding.container.setOnClickListener {
|
||||
adapter.selectedItemPosition = position
|
||||
binding.container.isChecked = true
|
||||
}
|
||||
|
||||
binding.trackSearchTitle.text = track.title
|
||||
binding.trackSearchCover.clear()
|
||||
if (track.cover_url.isNotEmpty()) {
|
||||
binding.trackSearchCover.load(track.cover_url)
|
||||
}
|
||||
|
||||
val hasStatus = track.publishing_status.isNotBlank()
|
||||
binding.trackSearchStatus.isVisible = hasStatus
|
||||
binding.trackSearchStatusResult.isVisible = hasStatus
|
||||
if (hasStatus) {
|
||||
binding.trackSearchStatusResult.text = track.publishing_status.lowercase().replaceFirstChar {
|
||||
it.titlecase(Locale.getDefault())
|
||||
}
|
||||
}
|
||||
|
||||
val hasType = track.publishing_type.isNotBlank()
|
||||
binding.trackSearchType.isVisible = hasType
|
||||
binding.trackSearchTypeResult.isVisible = hasType
|
||||
if (hasType) {
|
||||
binding.trackSearchTypeResult.text = track.publishing_type.lowercase().replaceFirstChar {
|
||||
it.titlecase(Locale.getDefault())
|
||||
}
|
||||
}
|
||||
|
||||
val hasStartDate = track.start_date.isNotBlank()
|
||||
binding.trackSearchStart.isVisible = hasStartDate
|
||||
binding.trackSearchStartResult.isVisible = hasStartDate
|
||||
if (hasStartDate) {
|
||||
binding.trackSearchStartResult.text = track.start_date
|
||||
}
|
||||
|
||||
binding.trackSearchSummary.setMaxLinesAndEllipsize()
|
||||
binding.trackSearchSummary.text = track.summary
|
||||
}
|
||||
|
||||
fun setUnchecked() {
|
||||
binding.container.isChecked = false
|
||||
}
|
||||
}
|
@ -96,7 +96,8 @@ class TrackSheet(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TrackSearchDialog(controller, item.service).showDialog(controller.router, TAG_SEARCH_CONTROLLER)
|
||||
TrackSearchDialog(controller, item.service, item.track?.tracking_url)
|
||||
.showDialog(controller.router, TAG_SEARCH_CONTROLLER)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.lang.truncateCenter
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
@ -4,10 +4,12 @@ package eu.kanade.tachiyomi.util.view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Point
|
||||
import android.text.TextUtils
|
||||
import android.view.Gravity
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.MenuRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.view.menu.MenuBuilder
|
||||
@ -16,12 +18,17 @@ import androidx.appcompat.widget.TooltipCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.forEach
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.chip.ChipGroup
|
||||
import com.google.android.material.elevation.ElevationOverlayProvider
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
/**
|
||||
* Returns coordinates of view.
|
||||
@ -174,3 +181,21 @@ inline fun ChipGroup.setChips(
|
||||
addView(chip)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies elevation overlay to a MaterialCardView
|
||||
*/
|
||||
inline fun MaterialCardView.applyElevationOverlay() {
|
||||
if (Injekt.get<PreferencesHelper>().isDarkMode()) {
|
||||
val provider = ElevationOverlayProvider(context)
|
||||
setCardBackgroundColor(provider.compositeOverlay(cardBackgroundColor.defaultColor, cardElevation))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets TextView max lines dynamically. Can only be called when the view is already laid out.
|
||||
*/
|
||||
inline fun TextView.setMaxLinesAndEllipsize(_ellipsize: TextUtils.TruncateAt = TextUtils.TruncateAt.END) = post {
|
||||
maxLines = (measuredHeight - paddingTop - paddingBottom) / lineHeight
|
||||
ellipsize = _ellipsize
|
||||
}
|
||||
|
Reference in New Issue
Block a user