Support multiple scrobblers
This commit is contained in:
@@ -30,7 +30,8 @@ import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.databinding.FragmentDetailsBinding
|
||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
|
||||
import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingInfoBottomSheet
|
||||
import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingItemDecoration
|
||||
import org.koitharu.kotatsu.details.ui.scrobbling.ScrollingInfoAdapter
|
||||
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.image.ui.ImageActivity
|
||||
import org.koitharu.kotatsu.main.ui.owners.NoModalBottomSheetOwner
|
||||
@@ -69,7 +70,6 @@ class DetailsFragment :
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.textViewAuthor.setOnClickListener(this)
|
||||
binding.imageViewCover.setOnClickListener(this)
|
||||
binding.scrobblingLayout.root.setOnClickListener(this)
|
||||
binding.textViewDescription.movementMethod = LinkMovementMethod.getInstance()
|
||||
binding.chipsTags.onChipClickListener = this
|
||||
viewModel.manga.observe(viewLifecycleOwner, ::onMangaUpdated)
|
||||
@@ -203,35 +203,22 @@ class DetailsFragment :
|
||||
}
|
||||
}
|
||||
|
||||
private fun onScrobblingInfoChanged(scrobbling: ScrobblingInfo?) {
|
||||
with(binding.scrobblingLayout) {
|
||||
root.isVisible = scrobbling != null
|
||||
if (scrobbling == null) {
|
||||
CoilUtils.dispose(imageViewCover)
|
||||
return
|
||||
}
|
||||
imageViewCover.newImageRequest(scrobbling.coverUrl)?.run {
|
||||
placeholder(R.drawable.ic_placeholder)
|
||||
fallback(R.drawable.ic_placeholder)
|
||||
error(R.drawable.ic_error_placeholder)
|
||||
lifecycle(viewLifecycleOwner)
|
||||
enqueueWith(coil)
|
||||
}
|
||||
textViewTitle.text = scrobbling.title
|
||||
textViewTitle.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, scrobbling.scrobbler.iconResId, 0)
|
||||
ratingBar.rating = scrobbling.rating * ratingBar.numStars
|
||||
textViewStatus.text = scrobbling.status?.let {
|
||||
resources.getStringArray(R.array.scrobbling_statuses).getOrNull(it.ordinal)
|
||||
}
|
||||
private fun onScrobblingInfoChanged(scrobblings: List<ScrobblingInfo>) {
|
||||
var adapter = binding.recyclerViewScrobbling.adapter as? ScrollingInfoAdapter
|
||||
binding.recyclerViewScrobbling.isGone = scrobblings.isEmpty()
|
||||
if (adapter != null) {
|
||||
adapter.items = scrobblings
|
||||
} else {
|
||||
adapter = ScrollingInfoAdapter(viewLifecycleOwner, coil, childFragmentManager)
|
||||
adapter.items = scrobblings
|
||||
binding.recyclerViewScrobbling.adapter = adapter
|
||||
binding.recyclerViewScrobbling.addItemDecoration(ScrobblingItemDecoration())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
val manga = viewModel.manga.value ?: return
|
||||
when (v.id) {
|
||||
R.id.scrobbling_layout -> {
|
||||
ScrobblingInfoBottomSheet.show(childFragmentManager)
|
||||
}
|
||||
R.id.textView_author -> {
|
||||
startActivity(
|
||||
SearchActivity.newIntent(
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||
import org.koitharu.kotatsu.scrobbling.domain.Scrobbler
|
||||
import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingInfo
|
||||
import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingStatus
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||
@@ -54,13 +55,11 @@ class DetailsViewModel @AssistedInject constructor(
|
||||
mangaDataRepository: MangaDataRepository,
|
||||
private val bookmarksRepository: BookmarksRepository,
|
||||
private val settings: AppSettings,
|
||||
scrobblers: Set<@JvmSuppressWildcards Scrobbler>,
|
||||
private val scrobblers: Set<@JvmSuppressWildcards Scrobbler>,
|
||||
private val imageGetter: Html.ImageGetter,
|
||||
mangaRepositoryFactory: MangaRepository.Factory,
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val scrobbler = scrobblers.first() // TODO support multiple scrobblers
|
||||
|
||||
private val delegate = MangaDetailsDelegate(
|
||||
intent = intent,
|
||||
settings = settings,
|
||||
@@ -121,10 +120,13 @@ class DetailsViewModel @AssistedInject constructor(
|
||||
|
||||
val onMangaRemoved = SingleLiveEvent<Manga>()
|
||||
val isScrobblingAvailable: Boolean
|
||||
get() = scrobbler.isAvailable
|
||||
get() = scrobblers.any { it.isAvailable }
|
||||
|
||||
val scrobblingInfo = scrobbler.observeScrobblingInfo(delegate.mangaId)
|
||||
.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, null)
|
||||
val scrobblingInfo: LiveData<List<ScrobblingInfo>> = combine(
|
||||
scrobblers.map { it.observeScrobblingInfo(delegate.mangaId) },
|
||||
) { scrobblingInfo ->
|
||||
scrobblingInfo.filterNotNull()
|
||||
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, emptyList())
|
||||
|
||||
val branches: LiveData<List<String?>> = delegate.manga.map {
|
||||
val chapters = it?.chapters ?: return@map emptyList()
|
||||
@@ -238,21 +240,27 @@ class DetailsViewModel @AssistedInject constructor(
|
||||
}
|
||||
|
||||
fun updateScrobbling(rating: Float, status: ScrobblingStatus?) {
|
||||
launchJob(Dispatchers.Default) {
|
||||
scrobbler.updateScrobblingInfo(
|
||||
mangaId = delegate.mangaId,
|
||||
rating = rating,
|
||||
status = status,
|
||||
comment = null,
|
||||
)
|
||||
for (scrobbler in scrobblers) {
|
||||
if (!scrobbler.isAvailable) continue
|
||||
launchJob(Dispatchers.Default) {
|
||||
scrobbler.updateScrobblingInfo(
|
||||
mangaId = delegate.mangaId,
|
||||
rating = rating,
|
||||
status = status,
|
||||
comment = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun unregisterScrobbling() {
|
||||
launchJob(Dispatchers.Default) {
|
||||
scrobbler.unregisterScrobbling(
|
||||
mangaId = delegate.mangaId,
|
||||
)
|
||||
for (scrobbler in scrobblers) {
|
||||
if (!scrobbler.isAvailable) continue
|
||||
launchJob(Dispatchers.Default) {
|
||||
scrobbler.unregisterScrobbling(
|
||||
mangaId = delegate.mangaId,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.koitharu.kotatsu.details.ui.scrobbling
|
||||
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.databinding.ItemScrobblingInfoBinding
|
||||
import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingInfo
|
||||
import org.koitharu.kotatsu.utils.ext.disposeImageRequest
|
||||
import org.koitharu.kotatsu.utils.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.utils.ext.newImageRequest
|
||||
|
||||
fun scrobblingInfoAD(
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
coil: ImageLoader,
|
||||
fragmentManager: FragmentManager,
|
||||
) = adapterDelegateViewBinding<ScrobblingInfo, ScrobblingInfo, ItemScrobblingInfoBinding>(
|
||||
{ layoutInflater, parent -> ItemScrobblingInfoBinding.inflate(layoutInflater, parent, false) },
|
||||
) {
|
||||
binding.root.setOnClickListener {
|
||||
ScrobblingInfoBottomSheet.show(fragmentManager, bindingAdapterPosition)
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.imageViewCover.newImageRequest(item.coverUrl)?.run {
|
||||
placeholder(R.drawable.ic_placeholder)
|
||||
fallback(R.drawable.ic_placeholder)
|
||||
error(R.drawable.ic_error_placeholder)
|
||||
lifecycle(lifecycleOwner)
|
||||
enqueueWith(coil)
|
||||
}
|
||||
binding.textViewTitle.text = item.title
|
||||
binding.textViewTitle.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, item.scrobbler.iconResId, 0)
|
||||
binding.ratingBar.rating = item.rating * binding.ratingBar.numStars
|
||||
binding.textViewStatus.text = item.status?.let {
|
||||
context.resources.getStringArray(R.array.scrobbling_statuses).getOrNull(it.ordinal)
|
||||
}
|
||||
}
|
||||
|
||||
onViewRecycled {
|
||||
binding.imageViewCover.disposeImageRequest()
|
||||
}
|
||||
}
|
||||
@@ -26,10 +26,7 @@ import org.koitharu.kotatsu.image.ui.ImageActivity
|
||||
import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingInfo
|
||||
import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingStatus
|
||||
import org.koitharu.kotatsu.scrobbling.ui.selector.ScrobblingSelectorBottomSheet
|
||||
import org.koitharu.kotatsu.utils.ext.crossfade
|
||||
import org.koitharu.kotatsu.utils.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.scaleUpActivityOptionsOf
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ScrobblingInfoBottomSheet :
|
||||
@@ -40,11 +37,17 @@ class ScrobblingInfoBottomSheet :
|
||||
PopupMenu.OnMenuItemClickListener {
|
||||
|
||||
private val viewModel by activityViewModels<DetailsViewModel>()
|
||||
private var scrobblerIndex: Int = -1
|
||||
|
||||
@Inject
|
||||
lateinit var coil: ImageLoader
|
||||
private var menu: PopupMenu? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
scrobblerIndex = requireArguments().getInt(ARG_INDEX, scrobblerIndex)
|
||||
}
|
||||
|
||||
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetScrobblingBinding {
|
||||
return SheetScrobblingBinding.inflate(inflater, container, false)
|
||||
}
|
||||
@@ -95,14 +98,15 @@ class ScrobblingInfoBottomSheet :
|
||||
when (v.id) {
|
||||
R.id.button_menu -> menu?.show()
|
||||
R.id.imageView_cover -> {
|
||||
val coverUrl = viewModel.scrobblingInfo.value?.coverUrl ?: return
|
||||
val coverUrl = viewModel.scrobblingInfo.value?.getOrNull(scrobblerIndex)?.coverUrl ?: return
|
||||
val options = scaleUpActivityOptionsOf(v)
|
||||
startActivity(ImageActivity.newIntent(v.context, coverUrl), options.toBundle())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onScrobblingInfoChanged(scrobbling: ScrobblingInfo?) {
|
||||
private fun onScrobblingInfoChanged(scrobblings: List<ScrobblingInfo>) {
|
||||
val scrobbling = scrobblings.getOrNull(scrobblerIndex)
|
||||
if (scrobbling == null) {
|
||||
dismissAllowingStateLoss()
|
||||
return
|
||||
@@ -122,17 +126,10 @@ class ScrobblingInfoBottomSheet :
|
||||
.enqueueWith(coil)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = "ScrobblingInfoBottomSheet"
|
||||
|
||||
fun show(fm: FragmentManager) = ScrobblingInfoBottomSheet().show(fm, TAG)
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_browser -> {
|
||||
val url = viewModel.scrobblingInfo.value?.externalUrl ?: return false
|
||||
val url = viewModel.scrobblingInfo.value?.getOrNull(scrobblerIndex)?.externalUrl ?: return false
|
||||
val intent = Intent(Intent.ACTION_VIEW, url.toUri())
|
||||
startActivity(
|
||||
Intent.createChooser(intent, getString(R.string.open_in_browser)),
|
||||
@@ -150,4 +147,14 @@ class ScrobblingInfoBottomSheet :
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = "ScrobblingInfoBottomSheet"
|
||||
private const val ARG_INDEX = "index"
|
||||
|
||||
fun show(fm: FragmentManager, index: Int) = ScrobblingInfoBottomSheet().withArgs(1) {
|
||||
putInt(ARG_INDEX, index)
|
||||
}.show(fm, TAG)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.koitharu.kotatsu.details.ui.scrobbling
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
class ScrobblingItemDecoration() : RecyclerView.ItemDecoration() {
|
||||
|
||||
private var spacing: Int = -1
|
||||
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
if (spacing == -1) {
|
||||
spacing = parent.context.resources.getDimensionPixelOffset(R.dimen.scrobbling_list_spacing)
|
||||
}
|
||||
outRect.set(0, spacing, 0, 0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.koitharu.kotatsu.details.ui.scrobbling
|
||||
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||
import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingInfo
|
||||
|
||||
class ScrollingInfoAdapter(
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
coil: ImageLoader,
|
||||
fragmentManager: FragmentManager,
|
||||
) : AsyncListDifferDelegationAdapter<ScrobblingInfo>(DiffCallback()) {
|
||||
|
||||
init {
|
||||
delegatesManager.addDelegate(scrobblingInfoAD(lifecycleOwner, coil, fragmentManager))
|
||||
}
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<ScrobblingInfo>() {
|
||||
|
||||
override fun areItemsTheSame(oldItem: ScrobblingInfo, newItem: ScrobblingInfo): Boolean {
|
||||
return oldItem.scrobbler == newItem.scrobbler
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: ScrobblingInfo, newItem: ScrobblingInfo): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun getChangePayload(oldItem: ScrobblingInfo, newItem: ScrobblingInfo): Any? {
|
||||
return Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,11 @@ import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import coil.ImageLoader
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@@ -33,7 +36,8 @@ class ScrobblingSelectorBottomSheet :
|
||||
View.OnClickListener,
|
||||
MenuItem.OnActionExpandListener,
|
||||
SearchView.OnQueryTextListener,
|
||||
DialogInterface.OnKeyListener {
|
||||
DialogInterface.OnKeyListener,
|
||||
AdapterView.OnItemSelectedListener {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ScrobblingSelectorViewModel.Factory
|
||||
@@ -68,6 +72,7 @@ class ScrobblingSelectorBottomSheet :
|
||||
}
|
||||
binding.buttonDone.setOnClickListener(this)
|
||||
initOptionsMenu()
|
||||
initSpinner()
|
||||
|
||||
viewModel.content.observe(viewLifecycleOwner) { listAdapter.items = it }
|
||||
viewModel.selectedItemId.observe(viewLifecycleOwner) {
|
||||
@@ -133,6 +138,12 @@ class ScrobblingSelectorBottomSheet :
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
viewModel.setScrobblerIndex(position)
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) = Unit
|
||||
|
||||
private fun onError(e: Throwable) {
|
||||
Toast.makeText(requireContext(), e.getDisplayMessage(resources), Toast.LENGTH_LONG).show()
|
||||
if (viewModel.isEmpty) {
|
||||
@@ -150,6 +161,21 @@ class ScrobblingSelectorBottomSheet :
|
||||
searchView.queryHint = searchMenuItem.title
|
||||
}
|
||||
|
||||
private fun initSpinner() {
|
||||
val entries = viewModel.availableScrobblers
|
||||
if (entries.size <= 1) {
|
||||
binding.spinnerScrobblers.isVisible = false
|
||||
return
|
||||
}
|
||||
val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, entries)
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
binding.spinnerScrobblers.adapter = adapter
|
||||
viewModel.selectedScrobblerIndex.observe(viewLifecycleOwner) {
|
||||
binding.spinnerScrobblers.setSelection(it)
|
||||
}
|
||||
binding.spinnerScrobblers.onItemSelectedListener = this
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = "ScrobblingSelectorBottomSheet"
|
||||
|
||||
@@ -21,21 +21,28 @@ import org.koitharu.kotatsu.scrobbling.domain.Scrobbler
|
||||
import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga
|
||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||
import org.koitharu.kotatsu.utils.ext.requireValue
|
||||
|
||||
class ScrobblingSelectorViewModel @AssistedInject constructor(
|
||||
@Assisted val manga: Manga,
|
||||
scrobblers: Set<@JvmSuppressWildcards Scrobbler>,
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val scrobbler = scrobblers.first() // TODO support multiple scrobblers
|
||||
val availableScrobblers = scrobblers.filter { it.isAvailable }
|
||||
|
||||
private val shikiMangaList = MutableStateFlow<List<ScrobblerManga>?>(null)
|
||||
val selectedScrobblerIndex = MutableLiveData(0)
|
||||
|
||||
private val scrobblerMangaList = MutableStateFlow<List<ScrobblerManga>?>(null)
|
||||
private val hasNextPage = MutableStateFlow(false)
|
||||
private var loadingJob: Job? = null
|
||||
private var doneJob: Job? = null
|
||||
private var initJob: Job? = null
|
||||
|
||||
private val currentScrobbler: Scrobbler
|
||||
get() = availableScrobblers[selectedScrobblerIndex.requireValue()]
|
||||
|
||||
val content: LiveData<List<ListModel>> = combine(
|
||||
shikiMangaList.filterNotNull(),
|
||||
scrobblerMangaList.filterNotNull(),
|
||||
hasNextPage,
|
||||
) { list, isHasNextPage ->
|
||||
when {
|
||||
@@ -50,19 +57,10 @@ class ScrobblingSelectorViewModel @AssistedInject constructor(
|
||||
val onClose = SingleLiveEvent<Unit>()
|
||||
|
||||
val isEmpty: Boolean
|
||||
get() = shikiMangaList.value.isNullOrEmpty()
|
||||
get() = scrobblerMangaList.value.isNullOrEmpty()
|
||||
|
||||
init {
|
||||
launchJob(Dispatchers.Default) {
|
||||
try {
|
||||
val info = scrobbler.getScrobblingInfoOrNull(manga.id)
|
||||
if (info != null) {
|
||||
selectedItemId.postValue(info.targetId)
|
||||
}
|
||||
} finally {
|
||||
loadList(append = false)
|
||||
}
|
||||
}
|
||||
initialize()
|
||||
}
|
||||
|
||||
fun search(query: String) {
|
||||
@@ -79,12 +77,12 @@ class ScrobblingSelectorViewModel @AssistedInject constructor(
|
||||
return
|
||||
}
|
||||
loadingJob = launchLoadingJob(Dispatchers.Default) {
|
||||
val offset = if (append) shikiMangaList.value?.size ?: 0 else 0
|
||||
val list = scrobbler.findManga(checkNotNull(searchQuery.value), offset)
|
||||
val offset = if (append) scrobblerMangaList.value?.size ?: 0 else 0
|
||||
val list = currentScrobbler.findManga(checkNotNull(searchQuery.value), offset)
|
||||
if (!append) {
|
||||
shikiMangaList.value = list
|
||||
scrobblerMangaList.value = list
|
||||
} else if (list.isNotEmpty()) {
|
||||
shikiMangaList.value = shikiMangaList.value?.plus(list) ?: list
|
||||
scrobblerMangaList.value = scrobblerMangaList.value?.plus(list) ?: list
|
||||
}
|
||||
hasNextPage.value = list.isNotEmpty()
|
||||
}
|
||||
@@ -99,11 +97,34 @@ class ScrobblingSelectorViewModel @AssistedInject constructor(
|
||||
onClose.call(Unit)
|
||||
}
|
||||
doneJob = launchJob(Dispatchers.Default) {
|
||||
scrobbler.linkManga(manga.id, targetId)
|
||||
currentScrobbler.linkManga(manga.id, targetId)
|
||||
onClose.postCall(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun setScrobblerIndex(index: Int) {
|
||||
if (index == selectedScrobblerIndex.value || index !in availableScrobblers.indices) return
|
||||
selectedScrobblerIndex.value = index
|
||||
initialize()
|
||||
}
|
||||
|
||||
private fun initialize() {
|
||||
initJob?.cancel()
|
||||
loadingJob?.cancel()
|
||||
hasNextPage.value = false
|
||||
scrobblerMangaList.value = null
|
||||
initJob = launchJob(Dispatchers.Default) {
|
||||
try {
|
||||
val info = currentScrobbler.getScrobblingInfoOrNull(manga.id)
|
||||
if (info != null) {
|
||||
selectedItemId.postValue(info.targetId)
|
||||
}
|
||||
} finally {
|
||||
loadList(append = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
|
||||
|
||||
@@ -157,19 +157,24 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/textView_bookmarks"
|
||||
tools:listitem="@layout/item_bookmark" />
|
||||
|
||||
<include
|
||||
android:id="@+id/scrobbling_layout"
|
||||
layout="@layout/layout_scrobbling_info"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView_scrobbling"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:orientation="vertical"
|
||||
android:overScrollMode="never"
|
||||
android:scrollbars="none"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/recyclerView_bookmarks"
|
||||
tools:itemCount="2"
|
||||
tools:listitem="@layout/item_scrobbling_info"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.SelectableTextView
|
||||
@@ -185,7 +190,7 @@
|
||||
android:textIsSelectable="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/scrobbling_layout"
|
||||
app:layout_constraintTop_toBottomOf="@id/recyclerView_scrobbling"
|
||||
tools:ignore="UnusedAttribute"
|
||||
tools:text="@tools:sample/lorem/random[250]" />
|
||||
|
||||
|
||||
@@ -169,19 +169,24 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/textView_bookmarks"
|
||||
tools:listitem="@layout/item_bookmark" />
|
||||
|
||||
<include
|
||||
android:id="@+id/scrobbling_layout"
|
||||
layout="@layout/layout_scrobbling_info"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView_scrobbling"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:orientation="vertical"
|
||||
android:overScrollMode="never"
|
||||
android:scrollbars="none"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/recyclerView_bookmarks"
|
||||
tools:itemCount="2"
|
||||
tools:listitem="@layout/item_scrobbling_info"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.SelectableTextView
|
||||
@@ -197,7 +202,7 @@
|
||||
android:textIsSelectable="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/scrobbling_layout"
|
||||
app:layout_constraintTop_toBottomOf="@id/recyclerView_scrobbling"
|
||||
tools:ignore="UnusedAttribute"
|
||||
tools:text="@tools:sample/lorem/random[250]" />
|
||||
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/scrobbling_layout"
|
||||
style="@style/Widget.Material3.CardView.Filled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:contentPadding="8dp">
|
||||
|
||||
<RelativeLayout
|
||||
@@ -24,6 +24,12 @@
|
||||
|
||||
</org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_scrobblers"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:listitem="@android:layout/simple_spinner_item" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<dimen name="widget_cover_width">84dp</dimen>
|
||||
<dimen name="reading_progress_stroke">4dp</dimen>
|
||||
<dimen name="reading_progress_text_size">10dp</dimen>
|
||||
<dimen name="scrobbling_list_spacing">12dp</dimen>
|
||||
|
||||
<dimen name="search_suggestions_manga_height">124dp</dimen>
|
||||
<dimen name="search_suggestions_manga_spacing">4dp</dimen>
|
||||
|
||||
Reference in New Issue
Block a user