Cleanup
parent
c98d7561b8
commit
59dd53c025
@ -1,170 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.bookmarks.ui.sheet
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.FragmentManager
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
|
||||||
import coil.ImageLoader
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import org.koitharu.kotatsu.R
|
|
||||||
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
|
||||||
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
|
|
||||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
|
||||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
|
||||||
import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetBehavior
|
|
||||||
import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetCallback
|
|
||||||
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
|
|
||||||
import org.koitharu.kotatsu.core.util.RecyclerViewScrollCallback
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.observe
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.plus
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.showDistinct
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.withArgs
|
|
||||||
import org.koitharu.kotatsu.databinding.SheetPagesBinding
|
|
||||||
import org.koitharu.kotatsu.list.ui.GridSpanResolver
|
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
|
||||||
import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
|
|
||||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
|
||||||
import org.koitharu.kotatsu.reader.ui.thumbnails.OnPageSelectListener
|
|
||||||
import org.koitharu.kotatsu.reader.ui.thumbnails.PageThumbnail
|
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
@Deprecated("")
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class BookmarksSheet :
|
|
||||||
BaseAdaptiveSheet<SheetPagesBinding>(),
|
|
||||||
AdaptiveSheetCallback,
|
|
||||||
OnListItemClickListener<Bookmark> {
|
|
||||||
|
|
||||||
private val viewModel by viewModels<BookmarksSheetViewModel>()
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var coil: ImageLoader
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var settings: AppSettings
|
|
||||||
|
|
||||||
private var bookmarksAdapter: BookmarksAdapter? = null
|
|
||||||
private var spanResolver: GridSpanResolver? = null
|
|
||||||
|
|
||||||
private val spanSizeLookup = SpanSizeLookup()
|
|
||||||
private val listCommitCallback = Runnable {
|
|
||||||
spanSizeLookup.invalidateCache()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): SheetPagesBinding {
|
|
||||||
return SheetPagesBinding.inflate(inflater, container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewBindingCreated(binding: SheetPagesBinding, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewBindingCreated(binding, savedInstanceState)
|
|
||||||
addSheetCallback(this)
|
|
||||||
spanResolver = GridSpanResolver(binding.root.resources)
|
|
||||||
bookmarksAdapter = BookmarksAdapter(
|
|
||||||
coil = coil,
|
|
||||||
lifecycleOwner = viewLifecycleOwner,
|
|
||||||
clickListener = this@BookmarksSheet,
|
|
||||||
headerClickListener = null,
|
|
||||||
)
|
|
||||||
viewBinding?.headerBar?.setTitle(R.string.bookmarks)
|
|
||||||
with(binding.recyclerView) {
|
|
||||||
addItemDecoration(TypedListSpacingDecoration(context, false))
|
|
||||||
adapter = bookmarksAdapter
|
|
||||||
addOnLayoutChangeListener(spanResolver)
|
|
||||||
spanResolver?.setGridSize(settings.gridSize / 100f, this)
|
|
||||||
(layoutManager as GridLayoutManager).spanSizeLookup = spanSizeLookup
|
|
||||||
}
|
|
||||||
viewModel.content.observe(viewLifecycleOwner, ::onThumbnailsChanged)
|
|
||||||
viewModel.onError.observeEvent(viewLifecycleOwner, SnackbarErrorObserver(binding.recyclerView, this))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
spanResolver = null
|
|
||||||
bookmarksAdapter = null
|
|
||||||
spanSizeLookup.invalidateCache()
|
|
||||||
super.onDestroyView()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemClick(item: Bookmark, view: View) {
|
|
||||||
val listener = (parentFragment as? OnPageSelectListener) ?: (activity as? OnPageSelectListener)
|
|
||||||
if (listener != null) {
|
|
||||||
listener.onPageSelected(ReaderPage(item.toMangaPage(), item.page, item.chapterId))
|
|
||||||
} else {
|
|
||||||
val intent = IntentBuilder(view.context)
|
|
||||||
.manga(viewModel.manga)
|
|
||||||
.bookmark(item)
|
|
||||||
.incognito(true)
|
|
||||||
.build()
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStateChanged(sheet: View, newState: Int) {
|
|
||||||
viewBinding?.recyclerView?.isFastScrollerEnabled = newState == AdaptiveSheetBehavior.STATE_EXPANDED
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onThumbnailsChanged(list: List<ListModel>) {
|
|
||||||
val adapter = bookmarksAdapter ?: return
|
|
||||||
if (adapter.itemCount == 0) {
|
|
||||||
var position = list.indexOfFirst { it is PageThumbnail && it.isCurrent }
|
|
||||||
if (position > 0) {
|
|
||||||
val spanCount = spanResolver?.spanCount ?: 0
|
|
||||||
val offset = if (position > spanCount + 1) {
|
|
||||||
(resources.getDimensionPixelSize(R.dimen.manga_list_details_item_height) * 0.6).roundToInt()
|
|
||||||
} else {
|
|
||||||
position = 0
|
|
||||||
0
|
|
||||||
}
|
|
||||||
val scrollCallback = RecyclerViewScrollCallback(requireViewBinding().recyclerView, position, offset)
|
|
||||||
adapter.setItems(list, listCommitCallback + scrollCallback)
|
|
||||||
} else {
|
|
||||||
adapter.setItems(list, listCommitCallback)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
adapter.setItems(list, listCommitCallback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class SpanSizeLookup : GridLayoutManager.SpanSizeLookup() {
|
|
||||||
|
|
||||||
init {
|
|
||||||
isSpanIndexCacheEnabled = true
|
|
||||||
isSpanGroupIndexCacheEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getSpanSize(position: Int): Int {
|
|
||||||
val total = (viewBinding?.recyclerView?.layoutManager as? GridLayoutManager)?.spanCount ?: return 1
|
|
||||||
return when (bookmarksAdapter?.getItemViewType(position)) {
|
|
||||||
ListItemType.PAGE_THUMB.ordinal -> 1
|
|
||||||
else -> total
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun invalidateCache() {
|
|
||||||
invalidateSpanGroupIndexCache()
|
|
||||||
invalidateSpanIndexCache()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
const val ARG_MANGA = "manga"
|
|
||||||
|
|
||||||
private const val TAG = "BookmarksSheet"
|
|
||||||
|
|
||||||
fun show(fm: FragmentManager, manga: Manga) {
|
|
||||||
BookmarksSheet().withArgs(1) {
|
|
||||||
putParcelable(ARG_MANGA, ParcelableManga(manga))
|
|
||||||
}.showDistinct(fm, TAG)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.bookmarks.ui.sheet
|
|
||||||
|
|
||||||
import androidx.lifecycle.SavedStateHandle
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.flow.stateIn
|
|
||||||
import kotlinx.coroutines.plus
|
|
||||||
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
|
||||||
import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository
|
|
||||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
|
||||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
|
||||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.require
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
|
|
||||||
import org.koitharu.kotatsu.parsers.util.SuspendLazy
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@Deprecated("")
|
|
||||||
@HiltViewModel
|
|
||||||
class BookmarksSheetViewModel @Inject constructor(
|
|
||||||
savedStateHandle: SavedStateHandle,
|
|
||||||
mangaRepositoryFactory: MangaRepository.Factory,
|
|
||||||
bookmarksRepository: BookmarksRepository,
|
|
||||||
) : BaseViewModel() {
|
|
||||||
|
|
||||||
val manga = savedStateHandle.require<ParcelableManga>(BookmarksSheet.ARG_MANGA).manga
|
|
||||||
private val chaptersLazy = SuspendLazy {
|
|
||||||
requireNotNull(manga.chapters ?: mangaRepositoryFactory.create(manga.source).getDetails(manga).chapters)
|
|
||||||
}
|
|
||||||
|
|
||||||
val content: StateFlow<List<ListModel>> = bookmarksRepository.observeBookmarks(manga)
|
|
||||||
.map { mapList(it) }
|
|
||||||
.withErrorHandling()
|
|
||||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, listOf(LoadingFooter()))
|
|
||||||
|
|
||||||
private suspend fun mapList(bookmarks: List<Bookmark>): List<ListModel> {
|
|
||||||
val chapters = chaptersLazy.get()
|
|
||||||
val bookmarksMap = bookmarks.groupBy { it.chapterId }
|
|
||||||
val result = ArrayList<ListModel>(bookmarks.size + bookmarksMap.size)
|
|
||||||
for (chapter in chapters) {
|
|
||||||
val b = bookmarksMap[chapter.id]
|
|
||||||
if (b.isNullOrEmpty()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result += ListHeader(chapter.name)
|
|
||||||
result.addAll(b)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.details.ui
|
|
||||||
|
|
||||||
import android.transition.TransitionManager
|
|
||||||
import android.view.Gravity
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
import androidx.constraintlayout.widget.ConstraintSet
|
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
import androidx.core.graphics.Insets
|
|
||||||
import androidx.core.view.setMargins
|
|
||||||
import androidx.core.view.updateLayoutParams
|
|
||||||
import org.koitharu.kotatsu.R
|
|
||||||
import org.koitharu.kotatsu.core.ui.util.WindowInsetsDelegate
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.getThemeDimensionPixelSize
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
|
|
||||||
import org.koitharu.kotatsu.databinding.ItemTipBinding
|
|
||||||
import com.google.android.material.R as materialR
|
|
||||||
|
|
||||||
class ButtonTip(
|
|
||||||
private val root: ViewGroup,
|
|
||||||
private val insetsDelegate: WindowInsetsDelegate,
|
|
||||||
private val viewModel: DetailsViewModel,
|
|
||||||
) : View.OnClickListener, WindowInsetsDelegate.WindowInsetsListener {
|
|
||||||
|
|
||||||
private var selfBinding = ItemTipBinding.inflate(LayoutInflater.from(root.context), root, false)
|
|
||||||
private val actionBarSize = root.context.getThemeDimensionPixelSize(materialR.attr.actionBarSize)
|
|
||||||
|
|
||||||
init {
|
|
||||||
selfBinding.textView.setText(R.string.details_button_tip)
|
|
||||||
selfBinding.imageViewIcon.setImageResource(R.drawable.ic_tap)
|
|
||||||
selfBinding.root.id = R.id.layout_tip
|
|
||||||
selfBinding.buttonClose.setOnClickListener(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onClick(v: View?) {
|
|
||||||
remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onWindowInsetsChanged(insets: Insets) {
|
|
||||||
if (root is CoordinatorLayout) {
|
|
||||||
selfBinding.root.updateLayoutParams<CoordinatorLayout.LayoutParams> {
|
|
||||||
bottomMargin = topMargin + insets.bottom + insets.top + actionBarSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addToRoot() {
|
|
||||||
val lp: ViewGroup.LayoutParams = when (root) {
|
|
||||||
is CoordinatorLayout -> CoordinatorLayout.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
|
||||||
).apply {
|
|
||||||
// anchorId = R.id.layout_bottom
|
|
||||||
// anchorGravity = Gravity.TOP
|
|
||||||
gravity = Gravity.BOTTOM
|
|
||||||
setMargins(root.resources.getDimensionPixelOffset(R.dimen.margin_normal))
|
|
||||||
bottomMargin += actionBarSize
|
|
||||||
}
|
|
||||||
|
|
||||||
is ConstraintLayout -> ConstraintLayout.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
|
||||||
).apply {
|
|
||||||
width = root.resources.getDimensionPixelSize(R.dimen.m3_side_sheet_width)
|
|
||||||
setMargins(root.resources.getDimensionPixelOffset(R.dimen.margin_normal))
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
|
||||||
}
|
|
||||||
root.addView(selfBinding.root, lp)
|
|
||||||
if (root is ConstraintLayout) {
|
|
||||||
val cs = ConstraintSet()
|
|
||||||
cs.clone(root)
|
|
||||||
cs.connect(R.id.layout_tip, ConstraintSet.TOP, R.id.appbar, ConstraintSet.BOTTOM)
|
|
||||||
cs.connect(R.id.layout_tip, ConstraintSet.START, R.id.card_chapters, ConstraintSet.START)
|
|
||||||
cs.connect(R.id.layout_tip, ConstraintSet.END, R.id.card_chapters, ConstraintSet.END)
|
|
||||||
cs.applyTo(root)
|
|
||||||
}
|
|
||||||
insetsDelegate.addInsetsListener(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove() {
|
|
||||||
if (root.context.isAnimationsEnabled) {
|
|
||||||
TransitionManager.beginDelayedTransition(root)
|
|
||||||
}
|
|
||||||
insetsDelegate.removeInsetsListener(this)
|
|
||||||
root.removeView(selfBinding.root)
|
|
||||||
viewModel.onButtonTipClosed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.details.ui
|
|
||||||
|
|
||||||
import android.view.InputDevice
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
|
||||||
import android.view.View.OnLayoutChangeListener
|
|
||||||
import androidx.activity.OnBackPressedCallback
|
|
||||||
import androidx.appcompat.view.ActionMode
|
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
||||||
import com.google.android.material.tabs.TabLayout
|
|
||||||
import org.koitharu.kotatsu.core.ui.util.ActionModeListener
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.doOnExpansionsChanged
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.setTabsEnabled
|
|
||||||
|
|
||||||
class ChaptersBottomSheetMediator(
|
|
||||||
private val behavior: BottomSheetBehavior<*>,
|
|
||||||
private val pager: ViewPager2,
|
|
||||||
private val tabLayout: TabLayout,
|
|
||||||
) : OnBackPressedCallback(false),
|
|
||||||
ActionModeListener,
|
|
||||||
OnLayoutChangeListener, View.OnGenericMotionListener {
|
|
||||||
|
|
||||||
private var lockCounter = 0
|
|
||||||
|
|
||||||
init {
|
|
||||||
behavior.doOnExpansionsChanged { isExpanded ->
|
|
||||||
isEnabled = isExpanded
|
|
||||||
if (!isExpanded) {
|
|
||||||
unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleOnBackPressed() {
|
|
||||||
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActionModeStarted(mode: ActionMode) {
|
|
||||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
|
||||||
lock()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActionModeFinished(mode: ActionMode) {
|
|
||||||
unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLayoutChange(
|
|
||||||
v: View?,
|
|
||||||
left: Int,
|
|
||||||
top: Int,
|
|
||||||
right: Int,
|
|
||||||
bottom: Int,
|
|
||||||
oldLeft: Int,
|
|
||||||
oldTop: Int,
|
|
||||||
oldRight: Int,
|
|
||||||
oldBottom: Int,
|
|
||||||
) {
|
|
||||||
val height = bottom - top
|
|
||||||
if (height != behavior.peekHeight) {
|
|
||||||
behavior.peekHeight = height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onGenericMotion(v: View?, event: MotionEvent): Boolean {
|
|
||||||
if (event.source and InputDevice.SOURCE_CLASS_POINTER != 0) {
|
|
||||||
if (event.actionMasked == MotionEvent.ACTION_SCROLL) {
|
|
||||||
if (event.getAxisValue(MotionEvent.AXIS_VSCROLL) < 0f) {
|
|
||||||
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
|
||||||
} else {
|
|
||||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun lock() {
|
|
||||||
lockCounter++
|
|
||||||
updateLock()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unlock() {
|
|
||||||
lockCounter--
|
|
||||||
if (lockCounter < 0) {
|
|
||||||
lockCounter = 0
|
|
||||||
}
|
|
||||||
updateLock()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateLock() {
|
|
||||||
behavior.isDraggable = lockCounter <= 0
|
|
||||||
pager.isUserInputEnabled = lockCounter <= 0
|
|
||||||
tabLayout.setTabsEnabled(lockCounter <= 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.details.ui
|
|
||||||
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuInflater
|
|
||||||
import android.view.MenuItem
|
|
||||||
import androidx.activity.OnBackPressedCallback
|
|
||||||
import androidx.appcompat.widget.SearchView
|
|
||||||
import androidx.core.view.MenuProvider
|
|
||||||
import org.koitharu.kotatsu.R
|
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
|
|
||||||
class ChaptersMenuProvider(
|
|
||||||
private val viewModel: DetailsViewModel,
|
|
||||||
private val bottomSheetMediator: ChaptersBottomSheetMediator?,
|
|
||||||
) : OnBackPressedCallback(false), MenuProvider, SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener {
|
|
||||||
|
|
||||||
private var searchItemRef: WeakReference<MenuItem>? = null
|
|
||||||
|
|
||||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
|
||||||
menuInflater.inflate(R.menu.opt_chapters, menu)
|
|
||||||
val searchMenuItem = menu.findItem(R.id.action_search)
|
|
||||||
searchMenuItem.setOnActionExpandListener(this)
|
|
||||||
val searchView = searchMenuItem.actionView as SearchView
|
|
||||||
searchView.setOnQueryTextListener(this)
|
|
||||||
searchView.setIconifiedByDefault(false)
|
|
||||||
searchView.queryHint = searchMenuItem.title
|
|
||||||
searchItemRef = WeakReference(searchMenuItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPrepareMenu(menu: Menu) {
|
|
||||||
menu.findItem(R.id.action_search)?.isVisible = viewModel.isChaptersEmpty.value == false
|
|
||||||
menu.findItem(R.id.action_reversed)?.isChecked = viewModel.isChaptersReversed.value == true
|
|
||||||
menu.findItem(R.id.action_grid_view)?.isChecked = viewModel.isChaptersInGridView.value == true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
|
|
||||||
R.id.action_reversed -> {
|
|
||||||
viewModel.setChaptersReversed(!menuItem.isChecked)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
R.id.action_grid_view-> {
|
|
||||||
viewModel.setChaptersInGridView(!menuItem.isChecked)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleOnBackPressed() {
|
|
||||||
searchItemRef?.get()?.collapseActionView()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
|
||||||
bottomSheetMediator?.lock()
|
|
||||||
isEnabled = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
|
||||||
isEnabled = false
|
|
||||||
(item.actionView as? SearchView)?.setQuery("", false)
|
|
||||||
viewModel.performChapterSearch(null)
|
|
||||||
bottomSheetMediator?.unlock()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onQueryTextSubmit(query: String?): Boolean = false
|
|
||||||
|
|
||||||
override fun onQueryTextChange(newText: String?): Boolean {
|
|
||||||
viewModel.performChapterSearch(newText)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,712 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.details.ui
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.text.style.DynamicDrawableSpan
|
|
||||||
import android.text.style.ForegroundColorSpan
|
|
||||||
import android.text.style.ImageSpan
|
|
||||||
import android.text.style.RelativeSizeSpan
|
|
||||||
import android.transition.TransitionManager
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup.MarginLayoutParams
|
|
||||||
import android.view.ViewTreeObserver
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.viewModels
|
|
||||||
import androidx.appcompat.widget.PopupMenu
|
|
||||||
import androidx.core.graphics.Insets
|
|
||||||
import androidx.core.text.buildSpannedString
|
|
||||||
import androidx.core.text.inSpans
|
|
||||||
import androidx.core.text.method.LinkMovementMethodCompat
|
|
||||||
import androidx.core.view.isGone
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.core.view.updateLayoutParams
|
|
||||||
import androidx.core.view.updatePadding
|
|
||||||
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
|
||||||
import coil.ImageLoader
|
|
||||||
import coil.request.ImageRequest
|
|
||||||
import coil.request.SuccessResult
|
|
||||||
import coil.transform.CircleCropTransformation
|
|
||||||
import coil.util.CoilUtils
|
|
||||||
import com.google.android.material.chip.Chip
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import kotlinx.coroutines.flow.FlowCollector
|
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
|
||||||
import org.koitharu.kotatsu.R
|
|
||||||
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
|
||||||
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
|
|
||||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
|
||||||
import org.koitharu.kotatsu.core.model.iconResId
|
|
||||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
|
||||||
import org.koitharu.kotatsu.core.model.titleResId
|
|
||||||
import org.koitharu.kotatsu.core.os.AppShortcutManager
|
|
||||||
import org.koitharu.kotatsu.core.parser.MangaIntent
|
|
||||||
import org.koitharu.kotatsu.core.parser.favicon.faviconUri
|
|
||||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
|
||||||
import org.koitharu.kotatsu.core.ui.BaseListAdapter
|
|
||||||
import org.koitharu.kotatsu.core.ui.image.ChipIconTarget
|
|
||||||
import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver
|
|
||||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
|
||||||
import org.koitharu.kotatsu.core.ui.list.decor.SpacingItemDecoration
|
|
||||||
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
|
|
||||||
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
|
||||||
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
|
||||||
import org.koitharu.kotatsu.core.util.FileSize
|
|
||||||
import org.koitharu.kotatsu.core.util.ViewBadge
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.crossfade
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.enqueueWith
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.isTextTruncated
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.observe
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.parentView
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.setOnContextClickListenerCompat
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.source
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.textAndVisible
|
|
||||||
import org.koitharu.kotatsu.databinding.ActivityDetailsNewBinding
|
|
||||||
import org.koitharu.kotatsu.details.data.MangaDetails
|
|
||||||
import org.koitharu.kotatsu.details.data.ReadingTime
|
|
||||||
import org.koitharu.kotatsu.details.service.MangaPrefetchService
|
|
||||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
|
|
||||||
import org.koitharu.kotatsu.details.ui.model.HistoryInfo
|
|
||||||
import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet
|
|
||||||
import org.koitharu.kotatsu.details.ui.related.RelatedMangaActivity
|
|
||||||
import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingItemDecoration
|
|
||||||
import org.koitharu.kotatsu.details.ui.scrobbling.ScrollingInfoAdapter
|
|
||||||
import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver
|
|
||||||
import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteSheet
|
|
||||||
import org.koitharu.kotatsu.image.ui.ImageActivity
|
|
||||||
import org.koitharu.kotatsu.list.domain.ListExtraProvider
|
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.mangaGridItemAD
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.MangaItemModel
|
|
||||||
import org.koitharu.kotatsu.list.ui.size.StaticItemSizeResolver
|
|
||||||
import org.koitharu.kotatsu.local.ui.info.LocalInfoDialog
|
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
|
||||||
import org.koitharu.kotatsu.parsers.util.ellipsize
|
|
||||||
import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
|
|
||||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo
|
|
||||||
import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet
|
|
||||||
import org.koitharu.kotatsu.search.ui.MangaListActivity
|
|
||||||
import org.koitharu.kotatsu.search.ui.SearchActivity
|
|
||||||
import org.koitharu.kotatsu.stats.ui.sheet.MangaStatsSheet
|
|
||||||
import javax.inject.Inject
|
|
||||||
import com.google.android.material.R as materialR
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class DetailsActivity2 :
|
|
||||||
BaseActivity<ActivityDetailsNewBinding>(),
|
|
||||||
View.OnClickListener,
|
|
||||||
View.OnLongClickListener, PopupMenu.OnMenuItemClickListener, View.OnLayoutChangeListener,
|
|
||||||
ViewTreeObserver.OnDrawListener, ChipsView.OnChipClickListener, OnListItemClickListener<Bookmark> {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var shortcutManager: AppShortcutManager
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var coil: ImageLoader
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var tagHighlighter: ListExtraProvider
|
|
||||||
|
|
||||||
private val viewModel: DetailsViewModel by viewModels()
|
|
||||||
|
|
||||||
var bottomSheetMediator: ChaptersBottomSheetMediator? = null
|
|
||||||
private set
|
|
||||||
|
|
||||||
private lateinit var chaptersBadge: ViewBadge
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setContentView(ActivityDetailsNewBinding.inflate(layoutInflater))
|
|
||||||
supportActionBar?.run {
|
|
||||||
setDisplayHomeAsUpEnabled(true)
|
|
||||||
setDisplayShowTitleEnabled(false)
|
|
||||||
}
|
|
||||||
viewBinding.buttonRead.setOnClickListener(this)
|
|
||||||
viewBinding.buttonRead.setOnLongClickListener(this)
|
|
||||||
viewBinding.buttonRead.setOnContextClickListenerCompat(this)
|
|
||||||
viewBinding.buttonChapters?.setOnClickListener(this)
|
|
||||||
viewBinding.infoLayout.chipBranch.setOnClickListener(this)
|
|
||||||
viewBinding.infoLayout.chipSize.setOnClickListener(this)
|
|
||||||
viewBinding.infoLayout.chipSource.setOnClickListener(this)
|
|
||||||
viewBinding.infoLayout.chipFavorite.setOnClickListener(this)
|
|
||||||
viewBinding.infoLayout.chipAuthor.setOnClickListener(this)
|
|
||||||
viewBinding.infoLayout.chipTime.setOnClickListener(this)
|
|
||||||
viewBinding.imageViewCover.setOnClickListener(this)
|
|
||||||
viewBinding.buttonDescriptionMore.setOnClickListener(this)
|
|
||||||
viewBinding.buttonScrobblingMore.setOnClickListener(this)
|
|
||||||
viewBinding.buttonRelatedMore.setOnClickListener(this)
|
|
||||||
viewBinding.infoLayout.chipSource.setOnClickListener(this)
|
|
||||||
viewBinding.infoLayout.chipSize.setOnClickListener(this)
|
|
||||||
viewBinding.textViewDescription.addOnLayoutChangeListener(this)
|
|
||||||
viewBinding.textViewDescription.viewTreeObserver.addOnDrawListener(this)
|
|
||||||
viewBinding.textViewDescription.movementMethod = LinkMovementMethodCompat.getInstance()
|
|
||||||
viewBinding.chipsTags.onChipClickListener = this
|
|
||||||
viewBinding.recyclerViewRelated.addItemDecoration(
|
|
||||||
SpacingItemDecoration(resources.getDimensionPixelOffset(R.dimen.grid_spacing)),
|
|
||||||
)
|
|
||||||
TitleScrollCoordinator(viewBinding.textViewTitle).attach(viewBinding.scrollView)
|
|
||||||
|
|
||||||
chaptersBadge = ViewBadge(viewBinding.buttonChapters ?: viewBinding.buttonRead, this)
|
|
||||||
|
|
||||||
viewModel.details.filterNotNull().observe(this, ::onMangaUpdated)
|
|
||||||
viewModel.onMangaRemoved.observeEvent(this, ::onMangaRemoved)
|
|
||||||
viewModel.newChaptersCount.observe(this, ::onNewChaptersChanged)
|
|
||||||
viewModel.onError.observeEvent(
|
|
||||||
this,
|
|
||||||
SnackbarErrorObserver(viewBinding.scrollView, null, exceptionResolver) {
|
|
||||||
if (it) viewModel.reload()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
viewModel.onActionDone.observeEvent(this, ReversibleActionObserver(viewBinding.scrollView, null))
|
|
||||||
viewModel.historyInfo.observe(this, ::onHistoryChanged)
|
|
||||||
viewModel.isLoading.observe(this, ::onLoadingStateChanged)
|
|
||||||
viewModel.scrobblingInfo.observe(this, ::onScrobblingInfoChanged)
|
|
||||||
viewModel.localSize.observe(this, ::onLocalSizeChanged)
|
|
||||||
viewModel.relatedManga.observe(this, ::onRelatedMangaChanged)
|
|
||||||
// viewModel.chapters.observe(this, ::onChaptersChanged)
|
|
||||||
viewModel.readingTime.observe(this, ::onReadingTimeChanged)
|
|
||||||
viewModel.selectedBranch.observe(this) {
|
|
||||||
viewBinding.infoLayout.chipBranch.text = it.ifNullOrEmpty { getString(R.string.system_default) }
|
|
||||||
}
|
|
||||||
viewModel.favouriteCategories.observe(this, ::onFavoritesChanged)
|
|
||||||
val menuInvalidator = MenuInvalidator(this)
|
|
||||||
viewModel.isStatsAvailable.observe(this, menuInvalidator)
|
|
||||||
viewModel.remoteManga.observe(this, menuInvalidator)
|
|
||||||
viewModel.branches.observe(this) {
|
|
||||||
viewBinding.infoLayout.chipBranch.isVisible = it.size > 1
|
|
||||||
}
|
|
||||||
viewModel.chapters.observe(this, PrefetchObserver(this))
|
|
||||||
viewModel.onDownloadStarted.observeEvent(
|
|
||||||
this,
|
|
||||||
DownloadStartedObserver(viewBinding.scrollView),
|
|
||||||
)
|
|
||||||
|
|
||||||
addMenuProvider(
|
|
||||||
DetailsMenuProvider(
|
|
||||||
activity = this,
|
|
||||||
viewModel = viewModel,
|
|
||||||
snackbarHost = viewBinding.scrollView,
|
|
||||||
appShortcutManager = shortcutManager,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onClick(v: View) {
|
|
||||||
when (v.id) {
|
|
||||||
R.id.button_read -> openReader(isIncognitoMode = false)
|
|
||||||
R.id.chip_branch -> showBranchPopupMenu(v)
|
|
||||||
R.id.button_chapters -> {
|
|
||||||
ChaptersPagesSheet.show(supportFragmentManager)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.chip_author -> {
|
|
||||||
val manga = viewModel.manga.value ?: return
|
|
||||||
startActivity(
|
|
||||||
SearchActivity.newIntent(
|
|
||||||
context = v.context,
|
|
||||||
source = manga.source,
|
|
||||||
query = manga.author ?: return,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.chip_source -> {
|
|
||||||
val manga = viewModel.manga.value ?: return
|
|
||||||
startActivity(
|
|
||||||
MangaListActivity.newIntent(
|
|
||||||
context = v.context,
|
|
||||||
source = manga.source,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.chip_size -> {
|
|
||||||
val manga = viewModel.manga.value ?: return
|
|
||||||
LocalInfoDialog.show(supportFragmentManager, manga)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.chip_favorite -> {
|
|
||||||
val manga = viewModel.manga.value ?: return
|
|
||||||
FavoriteSheet.show(supportFragmentManager, manga)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.chip_time -> {
|
|
||||||
if (viewModel.isStatsAvailable.value) {
|
|
||||||
val manga = viewModel.manga.value ?: return
|
|
||||||
MangaStatsSheet.show(supportFragmentManager, manga)
|
|
||||||
} else {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.imageView_cover -> {
|
|
||||||
val manga = viewModel.manga.value ?: return
|
|
||||||
startActivity(
|
|
||||||
ImageActivity.newIntent(
|
|
||||||
v.context,
|
|
||||||
manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl },
|
|
||||||
manga.source,
|
|
||||||
),
|
|
||||||
scaleUpActivityOptionsOf(v),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.button_description_more -> {
|
|
||||||
val tv = viewBinding.textViewDescription
|
|
||||||
TransitionManager.beginDelayedTransition(tv.parentView)
|
|
||||||
if (tv.maxLines in 1 until Integer.MAX_VALUE) {
|
|
||||||
tv.maxLines = Integer.MAX_VALUE
|
|
||||||
} else {
|
|
||||||
tv.maxLines = resources.getInteger(R.integer.details_description_lines)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.button_scrobbling_more -> {
|
|
||||||
val manga = viewModel.manga.value ?: return
|
|
||||||
ScrobblingSelectorSheet.show(supportFragmentManager, manga, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.button_related_more -> {
|
|
||||||
val manga = viewModel.manga.value ?: return
|
|
||||||
startActivity(RelatedMangaActivity.newIntent(v.context, manga))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onChipClick(chip: Chip, data: Any?) {
|
|
||||||
val tag = data as? MangaTag ?: return
|
|
||||||
startActivity(MangaListActivity.newIntent(this, setOf(tag)))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLongClick(v: View): Boolean = when (v.id) {
|
|
||||||
R.id.button_read -> {
|
|
||||||
val menu = PopupMenu(v.context, v)
|
|
||||||
menu.inflate(R.menu.popup_read)
|
|
||||||
menu.menu.findItem(R.id.action_forget)?.isVisible = viewModel.historyInfo.value.run {
|
|
||||||
!isIncognitoMode && history != null
|
|
||||||
}
|
|
||||||
menu.setOnMenuItemClickListener(this)
|
|
||||||
menu.setForceShowIcon(true)
|
|
||||||
menu.show()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
|
||||||
return when (item.itemId) {
|
|
||||||
R.id.action_incognito -> {
|
|
||||||
openReader(isIncognitoMode = true)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.action_forget -> {
|
|
||||||
viewModel.removeFromHistory()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemClick(item: Bookmark, view: View) {
|
|
||||||
startActivity(
|
|
||||||
IntentBuilder(view.context).bookmark(item).incognito(true).build(),
|
|
||||||
)
|
|
||||||
Toast.makeText(view.context, R.string.incognito_mode, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDraw() {
|
|
||||||
viewBinding.run {
|
|
||||||
buttonDescriptionMore.isVisible = textViewDescription.maxLines == Int.MAX_VALUE ||
|
|
||||||
textViewDescription.isTextTruncated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLayoutChange(
|
|
||||||
v: View?,
|
|
||||||
left: Int,
|
|
||||||
top: Int,
|
|
||||||
right: Int,
|
|
||||||
bottom: Int,
|
|
||||||
oldLeft: Int,
|
|
||||||
oldTop: Int,
|
|
||||||
oldRight: Int,
|
|
||||||
oldBottom: Int
|
|
||||||
) {
|
|
||||||
with(viewBinding) {
|
|
||||||
buttonDescriptionMore.isVisible = textViewDescription.isTextTruncated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onChaptersChanged(chapters: List<ChapterListItem>?) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onFavoritesChanged(categories: Set<FavouriteCategory>) {
|
|
||||||
val chip = viewBinding.infoLayout.chipFavorite
|
|
||||||
chip.setChipIconResource(if (categories.isEmpty()) R.drawable.ic_heart_outline else R.drawable.ic_heart)
|
|
||||||
chip.text = if (categories.isEmpty()) {
|
|
||||||
getString(R.string.add_to_favourites)
|
|
||||||
} else {
|
|
||||||
if (categories.size == 1) {
|
|
||||||
categories.first().title.ellipsize(FAV_LABEL_LIMIT)
|
|
||||||
}
|
|
||||||
buildString(FAV_LABEL_LIMIT + 6) {
|
|
||||||
for ((i, cat) in categories.withIndex()) {
|
|
||||||
if (i == 0) {
|
|
||||||
append(cat.title.ellipsize(FAV_LABEL_LIMIT - 4))
|
|
||||||
} else if (length + cat.title.length > FAV_LABEL_LIMIT) {
|
|
||||||
append(", ")
|
|
||||||
append(getString(R.string.list_ellipsize_pattern, categories.size - i))
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
append(", ")
|
|
||||||
append(cat.title)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onReadingTimeChanged(time: ReadingTime?) {
|
|
||||||
val chip = viewBinding.infoLayout.chipTime
|
|
||||||
chip.textAndVisible = time?.formatShort(chip.resources)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onDescriptionChanged(description: CharSequence?) {
|
|
||||||
val tv = viewBinding.textViewDescription
|
|
||||||
if (description.isNullOrBlank()) {
|
|
||||||
tv.setText(R.string.no_description)
|
|
||||||
} else {
|
|
||||||
tv.text = description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onLocalSizeChanged(size: Long) {
|
|
||||||
val chip = viewBinding.infoLayout.chipSize
|
|
||||||
if (size == 0L) {
|
|
||||||
chip.isVisible = false
|
|
||||||
} else {
|
|
||||||
chip.text = FileSize.BYTES.format(chip.context, size)
|
|
||||||
chip.isVisible = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onRelatedMangaChanged(related: List<MangaItemModel>) {
|
|
||||||
if (related.isEmpty()) {
|
|
||||||
viewBinding.groupRelated.isVisible = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val rv = viewBinding.recyclerViewRelated
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
val adapter = (rv.adapter as? BaseListAdapter<ListModel>) ?: BaseListAdapter<ListModel>()
|
|
||||||
.addDelegate(
|
|
||||||
ListItemType.MANGA_GRID,
|
|
||||||
mangaGridItemAD(
|
|
||||||
coil, this,
|
|
||||||
StaticItemSizeResolver(resources.getDimensionPixelSize(R.dimen.smaller_grid_width)),
|
|
||||||
) { item, view ->
|
|
||||||
startActivity(DetailsActivity.newIntent(view.context, item))
|
|
||||||
},
|
|
||||||
).also { rv.adapter = it }
|
|
||||||
adapter.items = related
|
|
||||||
viewBinding.groupRelated.isVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onLoadingStateChanged(isLoading: Boolean) {
|
|
||||||
val button = viewBinding.buttonChapters ?: return
|
|
||||||
if (isLoading) {
|
|
||||||
button.setImageDrawable(
|
|
||||||
CircularProgressDrawable(this).also {
|
|
||||||
it.setStyle(CircularProgressDrawable.LARGE)
|
|
||||||
it.setColorSchemeColors(getThemeColor(materialR.attr.colorControlNormal))
|
|
||||||
it.start()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
button.setImageResource(R.drawable.ic_list_sheet)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onScrobblingInfoChanged(scrobblings: List<ScrobblingInfo>) {
|
|
||||||
var adapter = viewBinding.recyclerViewScrobbling.adapter as? ScrollingInfoAdapter
|
|
||||||
viewBinding.groupScrobbling.isGone = scrobblings.isEmpty()
|
|
||||||
if (adapter != null) {
|
|
||||||
adapter.items = scrobblings
|
|
||||||
} else {
|
|
||||||
adapter = ScrollingInfoAdapter(this, coil, supportFragmentManager)
|
|
||||||
adapter.items = scrobblings
|
|
||||||
viewBinding.recyclerViewScrobbling.adapter = adapter
|
|
||||||
viewBinding.recyclerViewScrobbling.addItemDecoration(ScrobblingItemDecoration())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onMangaUpdated(details: MangaDetails) {
|
|
||||||
with(viewBinding) {
|
|
||||||
val manga = details.toManga()
|
|
||||||
val hasChapters = !manga.chapters.isNullOrEmpty()
|
|
||||||
// Main
|
|
||||||
loadCover(manga)
|
|
||||||
textViewTitle.text = manga.title
|
|
||||||
textViewSubtitle.textAndVisible = manga.altTitle
|
|
||||||
infoLayout.chipAuthor.textAndVisible = manga.author
|
|
||||||
if (manga.hasRating) {
|
|
||||||
ratingBar.rating = manga.rating * ratingBar.numStars
|
|
||||||
ratingBar.isVisible = true
|
|
||||||
} else {
|
|
||||||
ratingBar.isVisible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
manga.state?.let { state ->
|
|
||||||
textViewState.textAndVisible = resources.getString(state.titleResId)
|
|
||||||
imageViewState.setImageResource(state.iconResId)
|
|
||||||
} ?: run {
|
|
||||||
textViewState.isVisible = false
|
|
||||||
imageViewState.isVisible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (manga.source == MangaSource.LOCAL || manga.source == MangaSource.DUMMY) {
|
|
||||||
infoLayout.chipSource.isVisible = false
|
|
||||||
} else {
|
|
||||||
infoLayout.chipSource.text = manga.source.title
|
|
||||||
infoLayout.chipSource.isVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
textViewNsfw.isVisible = manga.isNsfw
|
|
||||||
|
|
||||||
// Chips
|
|
||||||
bindTags(manga)
|
|
||||||
|
|
||||||
textViewDescription.text = details.description.ifNullOrEmpty { getString(R.string.no_description) }
|
|
||||||
|
|
||||||
viewBinding.infoLayout.chipSource.also { chip ->
|
|
||||||
ImageRequest.Builder(this@DetailsActivity2)
|
|
||||||
.data(manga.source.faviconUri())
|
|
||||||
.lifecycle(this@DetailsActivity2)
|
|
||||||
.crossfade(false)
|
|
||||||
.size(resources.getDimensionPixelSize(materialR.dimen.m3_chip_icon_size))
|
|
||||||
.target(ChipIconTarget(chip))
|
|
||||||
.placeholder(R.drawable.ic_web)
|
|
||||||
.fallback(R.drawable.ic_web)
|
|
||||||
.error(R.drawable.ic_web)
|
|
||||||
.source(manga.source)
|
|
||||||
.transformations(CircleCropTransformation())
|
|
||||||
.allowRgb565(true)
|
|
||||||
.enqueueWith(coil)
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonChapters?.isEnabled = hasChapters
|
|
||||||
title = manga.title
|
|
||||||
buttonRead.isEnabled = hasChapters
|
|
||||||
invalidateOptionsMenu()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onMangaRemoved(manga: Manga) {
|
|
||||||
Toast.makeText(
|
|
||||||
this,
|
|
||||||
getString(R.string._s_deleted_from_local_storage, manga.title),
|
|
||||||
Toast.LENGTH_SHORT,
|
|
||||||
).show()
|
|
||||||
finishAfterTransition()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onWindowInsetsChanged(insets: Insets) {
|
|
||||||
viewBinding.root.updatePadding(
|
|
||||||
left = insets.left,
|
|
||||||
right = insets.right,
|
|
||||||
)
|
|
||||||
viewBinding.cardChapters?.updateLayoutParams<MarginLayoutParams> {
|
|
||||||
val baseOffset = leftMargin
|
|
||||||
bottomMargin = insets.bottom + baseOffset
|
|
||||||
topMargin = insets.bottom + baseOffset
|
|
||||||
}
|
|
||||||
viewBinding.scrollView.updatePadding(
|
|
||||||
bottom = insets.bottom,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onHistoryChanged(info: HistoryInfo) {
|
|
||||||
with(viewBinding.buttonRead) {
|
|
||||||
if (info.history != null) {
|
|
||||||
setTitle(R.string._continue)
|
|
||||||
} else {
|
|
||||||
setTitle(R.string.read)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
viewBinding.buttonRead.subtitle = when {
|
|
||||||
!info.isValid -> getString(R.string.loading_)
|
|
||||||
info.currentChapter >= 0 -> getString(
|
|
||||||
R.string.chapter_d_of_d,
|
|
||||||
info.currentChapter + 1,
|
|
||||||
info.totalChapters,
|
|
||||||
)
|
|
||||||
|
|
||||||
info.totalChapters == 0 -> getString(R.string.no_chapters)
|
|
||||||
else -> resources.getQuantityString(
|
|
||||||
R.plurals.chapters,
|
|
||||||
info.totalChapters,
|
|
||||||
info.totalChapters,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
viewBinding.buttonRead.setProgress(info.history?.percent?.coerceIn(0f, 1f) ?: 0f, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onNewChaptersChanged(count: Int) {
|
|
||||||
chaptersBadge.counter = count
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showBranchPopupMenu(v: View) {
|
|
||||||
val menu = PopupMenu(v.context, v)
|
|
||||||
val branches = viewModel.branches.value
|
|
||||||
for ((i, branch) in branches.withIndex()) {
|
|
||||||
val title = buildSpannedString {
|
|
||||||
if (branch.isCurrent) {
|
|
||||||
inSpans(
|
|
||||||
ImageSpan(
|
|
||||||
this@DetailsActivity2,
|
|
||||||
R.drawable.ic_current_chapter,
|
|
||||||
DynamicDrawableSpan.ALIGN_BASELINE,
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
append(' ')
|
|
||||||
}
|
|
||||||
append(' ')
|
|
||||||
}
|
|
||||||
append(branch.name ?: getString(R.string.system_default))
|
|
||||||
append(' ')
|
|
||||||
append(' ')
|
|
||||||
inSpans(
|
|
||||||
ForegroundColorSpan(
|
|
||||||
v.context.getThemeColor(
|
|
||||||
android.R.attr.textColorSecondary,
|
|
||||||
Color.LTGRAY,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
RelativeSizeSpan(0.74f),
|
|
||||||
) {
|
|
||||||
append(branch.count.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val item = menu.menu.add(R.id.group_branches, Menu.NONE, i, title)
|
|
||||||
item.isCheckable = true
|
|
||||||
item.isChecked = branch.isSelected
|
|
||||||
}
|
|
||||||
menu.menu.setGroupCheckable(R.id.group_branches, true, true)
|
|
||||||
menu.setOnMenuItemClickListener {
|
|
||||||
viewModel.setSelectedBranch(branches.getOrNull(it.order)?.name)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
menu.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun openReader(isIncognitoMode: Boolean) {
|
|
||||||
val manga = viewModel.manga.value ?: return
|
|
||||||
val chapterId = viewModel.historyInfo.value.history?.chapterId
|
|
||||||
if (chapterId != null && manga.chapters?.none { x -> x.id == chapterId } == true) {
|
|
||||||
Snackbar.make(viewBinding.scrollView, R.string.chapter_is_missing, Snackbar.LENGTH_SHORT)
|
|
||||||
.show()
|
|
||||||
} else {
|
|
||||||
startActivity(
|
|
||||||
IntentBuilder(this)
|
|
||||||
.manga(manga)
|
|
||||||
.branch(viewModel.selectedBranchValue)
|
|
||||||
.incognito(isIncognitoMode)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
if (isIncognitoMode) {
|
|
||||||
Toast.makeText(this, R.string.incognito_mode, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindTags(manga: Manga) {
|
|
||||||
viewBinding.chipsTags.isVisible = manga.tags.isNotEmpty()
|
|
||||||
viewBinding.chipsTags.setChips(
|
|
||||||
manga.tags.map { tag ->
|
|
||||||
ChipsView.ChipModel(
|
|
||||||
title = tag.title,
|
|
||||||
tint = tagHighlighter.getTagTint(tag),
|
|
||||||
icon = 0,
|
|
||||||
data = tag,
|
|
||||||
isCheckable = false,
|
|
||||||
isChecked = false,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadCover(manga: Manga) {
|
|
||||||
val imageUrl = manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }
|
|
||||||
val lastResult = CoilUtils.result(viewBinding.imageViewCover)
|
|
||||||
if (lastResult is SuccessResult && lastResult.request.data == imageUrl) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val request = ImageRequest.Builder(this)
|
|
||||||
.target(viewBinding.imageViewCover)
|
|
||||||
.size(CoverSizeResolver(viewBinding.imageViewCover))
|
|
||||||
.data(imageUrl)
|
|
||||||
.tag(manga.source)
|
|
||||||
.crossfade(this)
|
|
||||||
.lifecycle(this)
|
|
||||||
.placeholderMemoryCacheKey(manga.coverUrl)
|
|
||||||
val previousDrawable = lastResult?.drawable
|
|
||||||
if (previousDrawable != null) {
|
|
||||||
request.fallback(previousDrawable)
|
|
||||||
.placeholder(previousDrawable)
|
|
||||||
.error(previousDrawable)
|
|
||||||
} else {
|
|
||||||
request.fallback(R.drawable.ic_placeholder)
|
|
||||||
.placeholder(R.drawable.ic_placeholder)
|
|
||||||
.error(R.drawable.ic_error_placeholder)
|
|
||||||
}
|
|
||||||
request.enqueueWith(coil)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PrefetchObserver(
|
|
||||||
private val context: Context,
|
|
||||||
) : FlowCollector<List<ChapterListItem>?> {
|
|
||||||
|
|
||||||
private var isCalled = false
|
|
||||||
|
|
||||||
override suspend fun emit(value: List<ChapterListItem>?) {
|
|
||||||
if (value.isNullOrEmpty()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!isCalled) {
|
|
||||||
isCalled = true
|
|
||||||
val item = value.find { it.isCurrent } ?: value.first()
|
|
||||||
MangaPrefetchService.prefetchPages(context, item.chapter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private const val FAV_LABEL_LIMIT = 10
|
|
||||||
|
|
||||||
fun newIntent(context: Context, manga: Manga): Intent {
|
|
||||||
return Intent(context, DetailsActivity2::class.java)
|
|
||||||
.putExtra(MangaIntent.KEY_MANGA, ParcelableManga(manga))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun newIntent(context: Context, mangaId: Long): Intent {
|
|
||||||
return Intent(context, DetailsActivity2::class.java)
|
|
||||||
.putExtra(MangaIntent.KEY_ID, mangaId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,424 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.details.ui
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.transition.TransitionManager
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.view.ViewTreeObserver
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.widget.PopupMenu
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.graphics.Insets
|
|
||||||
import androidx.core.text.method.LinkMovementMethodCompat
|
|
||||||
import androidx.core.view.isGone
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.core.view.updatePadding
|
|
||||||
import androidx.fragment.app.activityViewModels
|
|
||||||
import coil.ImageLoader
|
|
||||||
import coil.request.ImageRequest
|
|
||||||
import coil.request.SuccessResult
|
|
||||||
import coil.util.CoilUtils
|
|
||||||
import com.google.android.material.chip.Chip
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
|
||||||
import org.koitharu.kotatsu.R
|
|
||||||
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
|
||||||
import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter
|
|
||||||
import org.koitharu.kotatsu.bookmarks.ui.sheet.BookmarksSheet
|
|
||||||
import org.koitharu.kotatsu.core.model.countChaptersByBranch
|
|
||||||
import org.koitharu.kotatsu.core.model.iconResId
|
|
||||||
import org.koitharu.kotatsu.core.model.titleResId
|
|
||||||
import org.koitharu.kotatsu.core.ui.BaseFragment
|
|
||||||
import org.koitharu.kotatsu.core.ui.BaseListAdapter
|
|
||||||
import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver
|
|
||||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
|
||||||
import org.koitharu.kotatsu.core.ui.list.decor.SpacingItemDecoration
|
|
||||||
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
|
||||||
import org.koitharu.kotatsu.core.util.FileSize
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.crossfade
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.drawableTop
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.enqueueWith
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.isTextTruncated
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.observe
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.parentView
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.resolveDp
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.showOrHide
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.textAndVisible
|
|
||||||
import org.koitharu.kotatsu.databinding.FragmentDetailsBinding
|
|
||||||
import org.koitharu.kotatsu.details.data.ReadingTime
|
|
||||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
|
|
||||||
import org.koitharu.kotatsu.details.ui.model.HistoryInfo
|
|
||||||
import org.koitharu.kotatsu.details.ui.related.RelatedMangaActivity
|
|
||||||
import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingItemDecoration
|
|
||||||
import org.koitharu.kotatsu.details.ui.scrobbling.ScrollingInfoAdapter
|
|
||||||
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
|
|
||||||
import org.koitharu.kotatsu.image.ui.ImageActivity
|
|
||||||
import org.koitharu.kotatsu.list.domain.ListExtraProvider
|
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.mangaGridItemAD
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.MangaItemModel
|
|
||||||
import org.koitharu.kotatsu.list.ui.size.StaticItemSizeResolver
|
|
||||||
import org.koitharu.kotatsu.local.ui.info.LocalInfoDialog
|
|
||||||
import org.koitharu.kotatsu.main.ui.owners.NoModalBottomSheetOwner
|
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
|
||||||
import org.koitharu.kotatsu.reader.ui.ReaderActivity
|
|
||||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo
|
|
||||||
import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet
|
|
||||||
import org.koitharu.kotatsu.search.ui.MangaListActivity
|
|
||||||
import org.koitharu.kotatsu.search.ui.SearchActivity
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class DetailsFragment :
|
|
||||||
BaseFragment<FragmentDetailsBinding>(),
|
|
||||||
View.OnClickListener,
|
|
||||||
ChipsView.OnChipClickListener,
|
|
||||||
OnListItemClickListener<Bookmark>, ViewTreeObserver.OnDrawListener, View.OnLayoutChangeListener {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var coil: ImageLoader
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var tagHighlighter: ListExtraProvider
|
|
||||||
|
|
||||||
private val viewModel by activityViewModels<DetailsViewModel>()
|
|
||||||
|
|
||||||
override fun onCreateViewBinding(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
) = FragmentDetailsBinding.inflate(inflater, container, false)
|
|
||||||
|
|
||||||
override fun onViewBindingCreated(binding: FragmentDetailsBinding, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewBindingCreated(binding, savedInstanceState)
|
|
||||||
binding.textViewAuthor.setOnClickListener(this)
|
|
||||||
binding.imageViewCover.setOnClickListener(this)
|
|
||||||
binding.buttonDescriptionMore.setOnClickListener(this)
|
|
||||||
binding.buttonBookmarksMore.setOnClickListener(this)
|
|
||||||
binding.buttonScrobblingMore.setOnClickListener(this)
|
|
||||||
binding.buttonRelatedMore.setOnClickListener(this)
|
|
||||||
binding.infoLayout.textViewSource.setOnClickListener(this)
|
|
||||||
binding.infoLayout.textViewSize.setOnClickListener(this)
|
|
||||||
binding.textViewDescription.addOnLayoutChangeListener(this)
|
|
||||||
binding.textViewDescription.viewTreeObserver.addOnDrawListener(this)
|
|
||||||
binding.textViewDescription.movementMethod = LinkMovementMethodCompat.getInstance()
|
|
||||||
binding.chipsTags.onChipClickListener = this
|
|
||||||
binding.recyclerViewRelated.addItemDecoration(
|
|
||||||
SpacingItemDecoration(resources.getDimensionPixelOffset(R.dimen.grid_spacing)),
|
|
||||||
)
|
|
||||||
TitleScrollCoordinator(binding.textViewTitle).attach(binding.scrollView)
|
|
||||||
viewModel.manga.filterNotNull().observe(viewLifecycleOwner, ::onMangaUpdated)
|
|
||||||
viewModel.isLoading.observe(viewLifecycleOwner, ::onLoadingStateChanged)
|
|
||||||
viewModel.historyInfo.observe(viewLifecycleOwner, ::onHistoryChanged)
|
|
||||||
viewModel.bookmarks.observe(viewLifecycleOwner, ::onBookmarksChanged)
|
|
||||||
viewModel.scrobblingInfo.observe(viewLifecycleOwner, ::onScrobblingInfoChanged)
|
|
||||||
viewModel.description.observe(viewLifecycleOwner, ::onDescriptionChanged)
|
|
||||||
viewModel.localSize.observe(viewLifecycleOwner, ::onLocalSizeChanged)
|
|
||||||
viewModel.relatedManga.observe(viewLifecycleOwner, ::onRelatedMangaChanged)
|
|
||||||
viewModel.chapters.observe(viewLifecycleOwner, ::onChaptersChanged)
|
|
||||||
viewModel.readingTime.observe(viewLifecycleOwner, ::onReadingTimeChanged)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemClick(item: Bookmark, view: View) {
|
|
||||||
startActivity(
|
|
||||||
ReaderActivity.IntentBuilder(view.context).bookmark(item).incognito(true).build(),
|
|
||||||
)
|
|
||||||
Toast.makeText(view.context, R.string.incognito_mode, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemLongClick(item: Bookmark, view: View): Boolean {
|
|
||||||
val menu = PopupMenu(view.context, view)
|
|
||||||
menu.inflate(R.menu.popup_bookmark)
|
|
||||||
menu.setOnMenuItemClickListener { menuItem ->
|
|
||||||
when (menuItem.itemId) {
|
|
||||||
R.id.action_remove -> viewModel.removeBookmark(item)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
menu.show()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDraw() {
|
|
||||||
viewBinding?.run {
|
|
||||||
buttonDescriptionMore.isVisible = textViewDescription.maxLines == Int.MAX_VALUE ||
|
|
||||||
textViewDescription.isTextTruncated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLayoutChange(
|
|
||||||
v: View?,
|
|
||||||
left: Int,
|
|
||||||
top: Int,
|
|
||||||
right: Int,
|
|
||||||
bottom: Int,
|
|
||||||
oldLeft: Int,
|
|
||||||
oldTop: Int,
|
|
||||||
oldRight: Int,
|
|
||||||
oldBottom: Int
|
|
||||||
) {
|
|
||||||
with(viewBinding ?: return) {
|
|
||||||
buttonDescriptionMore.isVisible = textViewDescription.isTextTruncated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onMangaUpdated(manga: Manga) {
|
|
||||||
with(requireViewBinding()) {
|
|
||||||
// Main
|
|
||||||
loadCover(manga)
|
|
||||||
textViewTitle.text = manga.title
|
|
||||||
textViewSubtitle.textAndVisible = manga.altTitle
|
|
||||||
textViewAuthor.textAndVisible = manga.author
|
|
||||||
if (manga.hasRating) {
|
|
||||||
ratingBar.rating = manga.rating * ratingBar.numStars
|
|
||||||
ratingBar.isVisible = true
|
|
||||||
} else {
|
|
||||||
ratingBar.isVisible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
infoLayout.textViewState.apply {
|
|
||||||
manga.state?.let { state ->
|
|
||||||
textAndVisible = resources.getString(state.titleResId)
|
|
||||||
drawableTop = ContextCompat.getDrawable(context, state.iconResId)
|
|
||||||
} ?: run {
|
|
||||||
isVisible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (manga.source == MangaSource.LOCAL || manga.source == MangaSource.DUMMY) {
|
|
||||||
infoLayout.textViewSource.isVisible = false
|
|
||||||
} else {
|
|
||||||
infoLayout.textViewSource.text = manga.source.title
|
|
||||||
infoLayout.textViewSource.isVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
infoLayout.textViewNsfw.isVisible = manga.isNsfw
|
|
||||||
|
|
||||||
// Chips
|
|
||||||
bindTags(manga)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onChaptersChanged(chapters: List<ChapterListItem>?) {
|
|
||||||
val infoLayout = requireViewBinding().infoLayout
|
|
||||||
if (chapters.isNullOrEmpty()) {
|
|
||||||
infoLayout.textViewChapters.isVisible = false
|
|
||||||
} else {
|
|
||||||
val count = chapters.countChaptersByBranch()
|
|
||||||
infoLayout.textViewChapters.isVisible = true
|
|
||||||
val chaptersText = resources.getQuantityString(R.plurals.chapters, count, count)
|
|
||||||
infoLayout.textViewChapters.text = chaptersText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onReadingTimeChanged(time: ReadingTime?) {
|
|
||||||
val binding = viewBinding ?: return
|
|
||||||
if (time == null) {
|
|
||||||
binding.approximateReadTimeLayout.isVisible = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
binding.approximateReadTime.text = time.format(resources)
|
|
||||||
binding.approximateReadTimeTitle.setText(
|
|
||||||
if (time.isContinue) R.string.approximate_remaining_time else R.string.approximate_reading_time,
|
|
||||||
)
|
|
||||||
binding.approximateReadTimeLayout.isVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onDescriptionChanged(description: CharSequence?) {
|
|
||||||
val tv = requireViewBinding().textViewDescription
|
|
||||||
if (description.isNullOrBlank()) {
|
|
||||||
tv.setText(R.string.no_description)
|
|
||||||
} else {
|
|
||||||
tv.text = description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onLocalSizeChanged(size: Long) {
|
|
||||||
val textView = requireViewBinding().infoLayout.textViewSize
|
|
||||||
if (size == 0L) {
|
|
||||||
textView.isVisible = false
|
|
||||||
} else {
|
|
||||||
textView.text = FileSize.BYTES.format(textView.context, size)
|
|
||||||
textView.isVisible = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onRelatedMangaChanged(related: List<MangaItemModel>) {
|
|
||||||
if (related.isEmpty()) {
|
|
||||||
requireViewBinding().groupRelated.isVisible = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val rv = viewBinding?.recyclerViewRelated ?: return
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
val adapter = (rv.adapter as? BaseListAdapter<ListModel>) ?: BaseListAdapter<ListModel>()
|
|
||||||
.addDelegate(
|
|
||||||
ListItemType.MANGA_GRID,
|
|
||||||
mangaGridItemAD(
|
|
||||||
coil, viewLifecycleOwner,
|
|
||||||
StaticItemSizeResolver(resources.getDimensionPixelSize(R.dimen.smaller_grid_width)),
|
|
||||||
) { item, view ->
|
|
||||||
startActivity(DetailsActivity.newIntent(view.context, item))
|
|
||||||
},
|
|
||||||
).also { rv.adapter = it }
|
|
||||||
adapter.items = related
|
|
||||||
requireViewBinding().groupRelated.isVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onHistoryChanged(history: HistoryInfo) {
|
|
||||||
requireViewBinding().progressView.setPercent(history.history?.percent ?: PROGRESS_NONE, animate = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onLoadingStateChanged(isLoading: Boolean) {
|
|
||||||
requireViewBinding().progressBar.showOrHide(isLoading)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onBookmarksChanged(bookmarks: List<Bookmark>) {
|
|
||||||
var adapter = requireViewBinding().recyclerViewBookmarks.adapter as? BookmarksAdapter
|
|
||||||
requireViewBinding().groupBookmarks.isGone = bookmarks.isEmpty()
|
|
||||||
if (adapter != null) {
|
|
||||||
adapter.items = bookmarks
|
|
||||||
} else {
|
|
||||||
adapter = BookmarksAdapter(coil, viewLifecycleOwner, this)
|
|
||||||
adapter.items = bookmarks
|
|
||||||
requireViewBinding().recyclerViewBookmarks.adapter = adapter
|
|
||||||
val spacing = resources.getDimensionPixelOffset(R.dimen.bookmark_list_spacing)
|
|
||||||
requireViewBinding().recyclerViewBookmarks.addItemDecoration(SpacingItemDecoration(spacing))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onScrobblingInfoChanged(scrobblings: List<ScrobblingInfo>) {
|
|
||||||
var adapter = requireViewBinding().recyclerViewScrobbling.adapter as? ScrollingInfoAdapter
|
|
||||||
requireViewBinding().groupScrobbling.isGone = scrobblings.isEmpty()
|
|
||||||
if (adapter != null) {
|
|
||||||
adapter.items = scrobblings
|
|
||||||
} else {
|
|
||||||
adapter = ScrollingInfoAdapter(viewLifecycleOwner, coil, childFragmentManager)
|
|
||||||
adapter.items = scrobblings
|
|
||||||
requireViewBinding().recyclerViewScrobbling.adapter = adapter
|
|
||||||
requireViewBinding().recyclerViewScrobbling.addItemDecoration(ScrobblingItemDecoration())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onClick(v: View) {
|
|
||||||
val manga = viewModel.manga.value ?: return
|
|
||||||
when (v.id) {
|
|
||||||
R.id.textView_author -> {
|
|
||||||
startActivity(
|
|
||||||
SearchActivity.newIntent(
|
|
||||||
context = v.context,
|
|
||||||
source = manga.source,
|
|
||||||
query = manga.author ?: return,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.textView_source -> {
|
|
||||||
startActivity(
|
|
||||||
MangaListActivity.newIntent(
|
|
||||||
context = v.context,
|
|
||||||
source = manga.source,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.textView_size -> {
|
|
||||||
LocalInfoDialog.show(parentFragmentManager, manga)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.imageView_cover -> {
|
|
||||||
startActivity(
|
|
||||||
ImageActivity.newIntent(
|
|
||||||
v.context,
|
|
||||||
manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl },
|
|
||||||
manga.source,
|
|
||||||
),
|
|
||||||
scaleUpActivityOptionsOf(v),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.button_description_more -> {
|
|
||||||
val tv = requireViewBinding().textViewDescription
|
|
||||||
TransitionManager.beginDelayedTransition(tv.parentView)
|
|
||||||
if (tv.maxLines in 1 until Integer.MAX_VALUE) {
|
|
||||||
tv.maxLines = Integer.MAX_VALUE
|
|
||||||
} else {
|
|
||||||
tv.maxLines = resources.getInteger(R.integer.details_description_lines)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.button_scrobbling_more -> {
|
|
||||||
ScrobblingSelectorSheet.show(parentFragmentManager, manga, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.button_bookmarks_more -> {
|
|
||||||
BookmarksSheet.show(parentFragmentManager, manga)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.button_related_more -> {
|
|
||||||
startActivity(RelatedMangaActivity.newIntent(v.context, manga))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onChipClick(chip: Chip, data: Any?) {
|
|
||||||
val tag = data as? MangaTag ?: return
|
|
||||||
startActivity(MangaListActivity.newIntent(requireContext(), setOf(tag)))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onWindowInsetsChanged(insets: Insets) {
|
|
||||||
requireViewBinding().root.updatePadding(
|
|
||||||
bottom = (
|
|
||||||
(activity as? NoModalBottomSheetOwner)?.getBottomSheetCollapsedHeight()
|
|
||||||
?.plus(insets.bottom)?.plus(resources.resolveDp(16))
|
|
||||||
)
|
|
||||||
?: insets.bottom,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindTags(manga: Manga) {
|
|
||||||
requireViewBinding().chipsTags.setChips(
|
|
||||||
manga.tags.map { tag ->
|
|
||||||
ChipsView.ChipModel(
|
|
||||||
title = tag.title,
|
|
||||||
tint = tagHighlighter.getTagTint(tag),
|
|
||||||
icon = 0,
|
|
||||||
data = tag,
|
|
||||||
isCheckable = false,
|
|
||||||
isChecked = false,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadCover(manga: Manga) {
|
|
||||||
val imageUrl = manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }
|
|
||||||
val lastResult = CoilUtils.result(requireViewBinding().imageViewCover)
|
|
||||||
if (lastResult is SuccessResult && lastResult.request.data == imageUrl) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val request = ImageRequest.Builder(context ?: return)
|
|
||||||
.target(requireViewBinding().imageViewCover)
|
|
||||||
.size(CoverSizeResolver(requireViewBinding().imageViewCover))
|
|
||||||
.data(imageUrl)
|
|
||||||
.tag(manga.source)
|
|
||||||
.crossfade(requireContext())
|
|
||||||
.lifecycle(viewLifecycleOwner)
|
|
||||||
.placeholderMemoryCacheKey(manga.coverUrl)
|
|
||||||
val previousDrawable = lastResult?.drawable
|
|
||||||
if (previousDrawable != null) {
|
|
||||||
request.fallback(previousDrawable)
|
|
||||||
.placeholder(previousDrawable)
|
|
||||||
.error(previousDrawable)
|
|
||||||
} else {
|
|
||||||
request.fallback(R.drawable.ic_placeholder)
|
|
||||||
.placeholder(R.drawable.ic_placeholder)
|
|
||||||
.error(R.drawable.ic_error_placeholder)
|
|
||||||
}
|
|
||||||
request.enqueueWith(coil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.details.ui.pager
|
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
|
||||||
import com.google.android.material.tabs.TabLayout
|
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
|
||||||
import org.koitharu.kotatsu.R
|
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
|
||||||
import org.koitharu.kotatsu.details.ui.pager.chapters.ChaptersFragment
|
|
||||||
import org.koitharu.kotatsu.details.ui.pager.pages.PagesFragment
|
|
||||||
|
|
||||||
class DetailsPagerAdapter(
|
|
||||||
activity: FragmentActivity,
|
|
||||||
settings: AppSettings,
|
|
||||||
) : FragmentStateAdapter(activity),
|
|
||||||
TabLayoutMediator.TabConfigurationStrategy {
|
|
||||||
|
|
||||||
val isPagesTabEnabled = settings.isPagesTabEnabled
|
|
||||||
|
|
||||||
override fun getItemCount(): Int = if (isPagesTabEnabled) 2 else 1
|
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment = when (position) {
|
|
||||||
0 -> ChaptersFragment()
|
|
||||||
1 -> PagesFragment()
|
|
||||||
else -> throw IllegalArgumentException("Invalid position $position")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
|
|
||||||
tab.setText(
|
|
||||||
when (position) {
|
|
||||||
0 -> R.string.chapters
|
|
||||||
1 -> R.string.pages
|
|
||||||
else -> 0
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package org.koitharu.kotatsu.reader.ui.thumbnails
|
package org.koitharu.kotatsu.details.ui.pager.pages
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package org.koitharu.kotatsu.reader.ui.thumbnails
|
package org.koitharu.kotatsu.details.ui.pager.pages
|
||||||
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||||
@ -1,124 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.reader.ui
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.FragmentManager
|
|
||||||
import androidx.fragment.app.activityViewModels
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import org.koitharu.kotatsu.R
|
|
||||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
|
||||||
import org.koitharu.kotatsu.core.model.findById
|
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
|
||||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
|
||||||
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
|
|
||||||
import org.koitharu.kotatsu.core.util.RecyclerViewScrollCallback
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.showDistinct
|
|
||||||
import org.koitharu.kotatsu.databinding.SheetChaptersBinding
|
|
||||||
import org.koitharu.kotatsu.details.ui.adapter.ChaptersAdapter
|
|
||||||
import org.koitharu.kotatsu.details.ui.mapChapters
|
|
||||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
|
|
||||||
import org.koitharu.kotatsu.details.ui.pager.chapters.ChapterGridSpanHelper
|
|
||||||
import org.koitharu.kotatsu.details.ui.withVolumeHeaders
|
|
||||||
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
|
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration
|
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
|
||||||
import java.time.Instant
|
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
@Deprecated("Use ChaptersPagesSheet instead")
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class ChaptersSheet : BaseAdaptiveSheet<SheetChaptersBinding>(),
|
|
||||||
OnListItemClickListener<ChapterListItem> {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var settings: AppSettings
|
|
||||||
|
|
||||||
private val viewModel: ReaderViewModel by activityViewModels()
|
|
||||||
|
|
||||||
override fun onCreateViewBinding(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
) = SheetChaptersBinding.inflate(inflater, container, false)
|
|
||||||
|
|
||||||
override fun onViewBindingCreated(binding: SheetChaptersBinding, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewBindingCreated(binding, savedInstanceState)
|
|
||||||
val manga = viewModel.manga
|
|
||||||
if (manga == null) {
|
|
||||||
dismissAllowingStateLoss()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val state = viewModel.getCurrentState()
|
|
||||||
val currentChapter = state?.let { manga.allChapters.findById(it.chapterId) }
|
|
||||||
val chapters = manga.mapChapters(
|
|
||||||
history = state?.let {
|
|
||||||
MangaHistory(
|
|
||||||
createdAt = Instant.now(),
|
|
||||||
updatedAt = Instant.now(),
|
|
||||||
chapterId = it.chapterId,
|
|
||||||
page = it.page,
|
|
||||||
scroll = it.scroll,
|
|
||||||
percent = PROGRESS_NONE,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
newCount = 0,
|
|
||||||
branch = currentChapter?.branch,
|
|
||||||
bookmarks = listOf(),
|
|
||||||
isGrid = settings.isChaptersGridView,
|
|
||||||
).withVolumeHeaders(binding.root.context)
|
|
||||||
if (chapters.isEmpty()) {
|
|
||||||
dismissAllowingStateLoss()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val currentPosition = if (currentChapter != null) {
|
|
||||||
chapters.indexOfFirst { it is ChapterListItem && it.chapter.id == currentChapter.id }
|
|
||||||
} else {
|
|
||||||
-1
|
|
||||||
}
|
|
||||||
binding.recyclerView.addItemDecoration(TypedListSpacingDecoration(binding.recyclerView.context, true))
|
|
||||||
binding.recyclerView.adapter = ChaptersAdapter(this).also { adapter ->
|
|
||||||
if (currentPosition >= 0) {
|
|
||||||
val targetPosition = (currentPosition - 1).coerceAtLeast(0)
|
|
||||||
val offset =
|
|
||||||
(resources.getDimensionPixelSize(R.dimen.chapter_list_item_height) * 0.6).roundToInt()
|
|
||||||
adapter.setItems(
|
|
||||||
chapters, RecyclerViewScrollCallback(binding.recyclerView, targetPosition, offset),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
adapter.items = chapters
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ChapterGridSpanHelper.attach(binding.recyclerView)
|
|
||||||
binding.recyclerView.layoutManager = if (settings.isChaptersGridView) {
|
|
||||||
GridLayoutManager(context, ChapterGridSpanHelper.getSpanCount(binding.recyclerView)).apply {
|
|
||||||
spanSizeLookup = ChapterGridSpanHelper.SpanSizeLookup(binding.recyclerView)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LinearLayoutManager(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemClick(item: ChapterListItem, view: View) {
|
|
||||||
((parentFragment as? OnChapterChangeListener)
|
|
||||||
?: (activity as? OnChapterChangeListener))?.let {
|
|
||||||
dismiss()
|
|
||||||
it.onChapterSelected(item.chapter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun interface OnChapterChangeListener {
|
|
||||||
|
|
||||||
fun onChapterSelected(chapter: MangaChapter): Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private const val TAG = "ChaptersBottomSheet"
|
|
||||||
|
|
||||||
fun show(fm: FragmentManager) = ChaptersSheet().showDistinct(fm, TAG)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.reader.ui.thumbnails
|
|
||||||
|
|
||||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
|
||||||
|
|
||||||
@Deprecated("")
|
|
||||||
fun interface OnPageSelectListener {
|
|
||||||
|
|
||||||
fun onPageSelected(page: ReaderPage): Boolean
|
|
||||||
}
|
|
||||||
@ -1,195 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.reader.ui.thumbnails
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.FragmentManager
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import coil.ImageLoader
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import org.koitharu.kotatsu.R
|
|
||||||
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
|
|
||||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
|
||||||
import org.koitharu.kotatsu.core.ui.list.BoundsScrollListener
|
|
||||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
|
||||||
import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetBehavior
|
|
||||||
import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetCallback
|
|
||||||
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
|
|
||||||
import org.koitharu.kotatsu.core.util.RecyclerViewScrollCallback
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.observe
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.plus
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.showDistinct
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.showOrHide
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.withArgs
|
|
||||||
import org.koitharu.kotatsu.databinding.SheetPagesBinding
|
|
||||||
import org.koitharu.kotatsu.list.ui.GridSpanResolver
|
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
|
||||||
import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
|
|
||||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
|
||||||
import org.koitharu.kotatsu.reader.ui.thumbnails.adapter.PageThumbnailAdapter
|
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
@Deprecated("Use ChaptersPagesSheet instead")
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class PagesThumbnailsSheet :
|
|
||||||
BaseAdaptiveSheet<SheetPagesBinding>(),
|
|
||||||
AdaptiveSheetCallback,
|
|
||||||
OnListItemClickListener<PageThumbnail> {
|
|
||||||
|
|
||||||
private val viewModel by viewModels<PagesThumbnailsViewModel>()
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var coil: ImageLoader
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var settings: AppSettings
|
|
||||||
|
|
||||||
private var thumbnailsAdapter: PageThumbnailAdapter? = null
|
|
||||||
private var spanResolver: GridSpanResolver? = null
|
|
||||||
private var scrollListener: ScrollListener? = null
|
|
||||||
|
|
||||||
private val spanSizeLookup = SpanSizeLookup()
|
|
||||||
private val listCommitCallback = Runnable {
|
|
||||||
spanSizeLookup.invalidateCache()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): SheetPagesBinding {
|
|
||||||
return SheetPagesBinding.inflate(inflater, container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewBindingCreated(binding: SheetPagesBinding, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewBindingCreated(binding, savedInstanceState)
|
|
||||||
addSheetCallback(this)
|
|
||||||
spanResolver = GridSpanResolver(binding.root.resources)
|
|
||||||
thumbnailsAdapter = PageThumbnailAdapter(
|
|
||||||
coil = coil,
|
|
||||||
lifecycleOwner = viewLifecycleOwner,
|
|
||||||
clickListener = this@PagesThumbnailsSheet,
|
|
||||||
)
|
|
||||||
with(binding.recyclerView) {
|
|
||||||
addItemDecoration(TypedListSpacingDecoration(context, false))
|
|
||||||
adapter = thumbnailsAdapter
|
|
||||||
addOnLayoutChangeListener(spanResolver)
|
|
||||||
spanResolver?.setGridSize(settings.gridSize / 100f, this)
|
|
||||||
addOnScrollListener(ScrollListener().also { scrollListener = it })
|
|
||||||
(layoutManager as GridLayoutManager).spanSizeLookup = spanSizeLookup
|
|
||||||
}
|
|
||||||
viewModel.thumbnails.observe(viewLifecycleOwner, ::onThumbnailsChanged)
|
|
||||||
viewModel.branch.observe(viewLifecycleOwner, ::updateTitle)
|
|
||||||
viewModel.onError.observeEvent(viewLifecycleOwner, SnackbarErrorObserver(binding.recyclerView, this))
|
|
||||||
viewModel.isLoading.observe(viewLifecycleOwner) { binding.progressBar.showOrHide(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
spanResolver = null
|
|
||||||
scrollListener = null
|
|
||||||
thumbnailsAdapter = null
|
|
||||||
spanSizeLookup.invalidateCache()
|
|
||||||
super.onDestroyView()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemClick(item: PageThumbnail, view: View) {
|
|
||||||
val listener = (parentFragment as? OnPageSelectListener) ?: (activity as? OnPageSelectListener)
|
|
||||||
if (listener != null) {
|
|
||||||
listener.onPageSelected(item.page)
|
|
||||||
} else {
|
|
||||||
val state = ReaderState(item.page.chapterId, item.page.index, 0)
|
|
||||||
val intent = IntentBuilder(view.context).manga(viewModel.manga).state(state).build()
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStateChanged(sheet: View, newState: Int) {
|
|
||||||
viewBinding?.recyclerView?.isFastScrollerEnabled = newState == AdaptiveSheetBehavior.STATE_EXPANDED
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateTitle(branch: String?) {
|
|
||||||
val mangaName = viewModel.manga.title
|
|
||||||
viewBinding?.headerBar?.title = if (branch != null) {
|
|
||||||
getString(R.string.manga_branch_title_template, mangaName, branch)
|
|
||||||
} else {
|
|
||||||
mangaName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onThumbnailsChanged(list: List<ListModel>) {
|
|
||||||
val adapter = thumbnailsAdapter ?: return
|
|
||||||
if (adapter.itemCount == 0) {
|
|
||||||
var position = list.indexOfFirst { it is PageThumbnail && it.isCurrent }
|
|
||||||
if (position > 0) {
|
|
||||||
val spanCount = spanResolver?.spanCount ?: 0
|
|
||||||
val offset = if (position > spanCount + 1) {
|
|
||||||
(resources.getDimensionPixelSize(R.dimen.manga_list_details_item_height) * 0.6).roundToInt()
|
|
||||||
} else {
|
|
||||||
position = 0
|
|
||||||
0
|
|
||||||
}
|
|
||||||
val scrollCallback = RecyclerViewScrollCallback(requireViewBinding().recyclerView, position, offset)
|
|
||||||
adapter.setItems(list, listCommitCallback + scrollCallback)
|
|
||||||
} else {
|
|
||||||
adapter.setItems(list, listCommitCallback)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
adapter.setItems(list, listCommitCallback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class ScrollListener : BoundsScrollListener(3, 3) {
|
|
||||||
|
|
||||||
override fun onScrolledToStart(recyclerView: RecyclerView) {
|
|
||||||
viewModel.loadPrevChapter()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onScrolledToEnd(recyclerView: RecyclerView) {
|
|
||||||
viewModel.loadNextChapter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class SpanSizeLookup : GridLayoutManager.SpanSizeLookup() {
|
|
||||||
|
|
||||||
init {
|
|
||||||
isSpanIndexCacheEnabled = true
|
|
||||||
isSpanGroupIndexCacheEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getSpanSize(position: Int): Int {
|
|
||||||
val total = (viewBinding?.recyclerView?.layoutManager as? GridLayoutManager)?.spanCount ?: return 1
|
|
||||||
return when (thumbnailsAdapter?.getItemViewType(position)) {
|
|
||||||
ListItemType.PAGE_THUMB.ordinal -> 1
|
|
||||||
else -> total
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun invalidateCache() {
|
|
||||||
invalidateSpanGroupIndexCache()
|
|
||||||
invalidateSpanIndexCache()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
const val ARG_MANGA = "manga"
|
|
||||||
const val ARG_CURRENT_PAGE = "current"
|
|
||||||
const val ARG_CHAPTER_ID = "chapter_id"
|
|
||||||
|
|
||||||
private const val TAG = "PagesThumbnailsSheet"
|
|
||||||
|
|
||||||
fun show(fm: FragmentManager, manga: Manga, chapterId: Long, currentPage: Int = -1) {
|
|
||||||
PagesThumbnailsSheet().withArgs(3) {
|
|
||||||
putParcelable(ARG_MANGA, ParcelableManga(manga))
|
|
||||||
putLong(ARG_CHAPTER_ID, chapterId)
|
|
||||||
putInt(ARG_CURRENT_PAGE, currentPage)
|
|
||||||
}.showDistinct(fm, TAG)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.reader.ui.thumbnails
|
|
||||||
|
|
||||||
import androidx.lifecycle.SavedStateHandle
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.flow.stateIn
|
|
||||||
import org.koitharu.kotatsu.core.model.findById
|
|
||||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
|
||||||
import org.koitharu.kotatsu.core.parser.MangaIntent
|
|
||||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.firstNotNull
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.require
|
|
||||||
import org.koitharu.kotatsu.details.domain.DetailsLoadUseCase
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
|
||||||
import org.koitharu.kotatsu.reader.domain.ChaptersLoader
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
|
||||||
class PagesThumbnailsViewModel @Inject constructor(
|
|
||||||
savedStateHandle: SavedStateHandle,
|
|
||||||
private val chaptersLoader: ChaptersLoader,
|
|
||||||
detailsLoadUseCase: DetailsLoadUseCase,
|
|
||||||
) : BaseViewModel() {
|
|
||||||
|
|
||||||
private val currentPageIndex: Int =
|
|
||||||
savedStateHandle[PagesThumbnailsSheet.ARG_CURRENT_PAGE] ?: -1
|
|
||||||
private val initialChapterId: Long = savedStateHandle[PagesThumbnailsSheet.ARG_CHAPTER_ID] ?: 0L
|
|
||||||
val manga = savedStateHandle.require<ParcelableManga>(PagesThumbnailsSheet.ARG_MANGA).manga
|
|
||||||
|
|
||||||
private val mangaDetails = detailsLoadUseCase(MangaIntent.of(manga)).map {
|
|
||||||
val b = manga.chapters?.findById(initialChapterId)?.branch
|
|
||||||
branch.value = b
|
|
||||||
it.filterChapters(b)
|
|
||||||
}.withErrorHandling()
|
|
||||||
.stateIn(viewModelScope, SharingStarted.Lazily, null)
|
|
||||||
private var loadingJob: Job
|
|
||||||
private var loadingPrevJob: Job? = null
|
|
||||||
private var loadingNextJob: Job? = null
|
|
||||||
|
|
||||||
val thumbnails = MutableStateFlow<List<ListModel>>(emptyList())
|
|
||||||
val branch = MutableStateFlow<String?>(null)
|
|
||||||
|
|
||||||
init {
|
|
||||||
loadingJob = launchLoadingJob(Dispatchers.Default) {
|
|
||||||
chaptersLoader.init(checkNotNull(mangaDetails.first { x -> x?.isLoaded == true }))
|
|
||||||
chaptersLoader.loadSingleChapter(initialChapterId)
|
|
||||||
updateList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadPrevChapter() {
|
|
||||||
if (loadingJob.isActive || loadingPrevJob?.isActive == true) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
loadingPrevJob = loadPrevNextChapter(isNext = false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadNextChapter() {
|
|
||||||
if (loadingJob.isActive || loadingNextJob?.isActive == true) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
loadingNextJob = loadPrevNextChapter(isNext = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadPrevNextChapter(isNext: Boolean): Job = launchLoadingJob(Dispatchers.Default) {
|
|
||||||
val currentId = (if (isNext) chaptersLoader.last() else chaptersLoader.first()).chapterId
|
|
||||||
chaptersLoader.loadPrevNextChapter(mangaDetails.firstNotNull(), currentId, isNext)
|
|
||||||
updateList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateList() {
|
|
||||||
val snapshot = chaptersLoader.snapshot()
|
|
||||||
val pages = buildList(snapshot.size + chaptersLoader.size + 2) {
|
|
||||||
var previousChapterId = 0L
|
|
||||||
for (page in snapshot) {
|
|
||||||
if (page.chapterId != previousChapterId) {
|
|
||||||
chaptersLoader.peekChapter(page.chapterId)?.let {
|
|
||||||
add(ListHeader(it.name))
|
|
||||||
}
|
|
||||||
previousChapterId = page.chapterId
|
|
||||||
}
|
|
||||||
this += PageThumbnail(
|
|
||||||
isCurrent = page.chapterId == initialChapterId && page.index == currentPageIndex,
|
|
||||||
page = page,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
thumbnails.value = pages
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,410 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
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/layout_root"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".details.ui.DetailsActivity2"
|
|
||||||
tools:viewBindingType="android.view.ViewGroup">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:id="@+id/appbar"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:elevation="0dp"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
app:elevation="0dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintWidth_max="420dp"
|
|
||||||
app:layout_constraintWidth_percent="0.5"
|
|
||||||
app:liftOnScroll="false">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
|
||||||
android:id="@+id/scrollView"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:scrollIndicators="top"
|
|
||||||
android:scrollbars="vertical"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="@id/appbar"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/appbar">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
|
||||||
android:id="@+id/imageView_cover"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="?colorSecondaryContainer"
|
|
||||||
android:foreground="?selectableItemBackground"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:layout_constraintDimensionRatio="H,13:18"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintWidth_percent="0.3"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
|
|
||||||
tools:background="@tools:sample/backgrounds/scenic[5]"
|
|
||||||
tools:ignore="ContentDescription,UnusedAttribute" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_nsfw"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="@dimen/card_indicator_offset"
|
|
||||||
android:background="@drawable/bg_chip"
|
|
||||||
android:backgroundTint="@color/warning"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingHorizontal="4dp"
|
|
||||||
android:paddingVertical="2dp"
|
|
||||||
android:text="@string/nsfw"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textAppearance="?textAppearanceLabelMedium"
|
|
||||||
android:textColor="?colorOnError"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintEnd_toEndOf="@id/imageView_cover" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
|
||||||
android:id="@+id/textView_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="5"
|
|
||||||
android:textAppearance="?attr/textAppearanceHeadlineSmall"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="@tools:sample/lorem" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
|
||||||
android:id="@+id/textView_subtitle"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="3"
|
|
||||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_title"
|
|
||||||
tools:text="@tools:sample/lorem[12]" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/imageView_state"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginVertical="0.5dp"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/textView_state"
|
|
||||||
app:layout_constraintDimensionRatio="1"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/textView_state"
|
|
||||||
tools:src="@drawable/ic_state_ongoing" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_state"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:drawablePadding="4dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:labelFor="@id/imageView_state"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textColor="?colorTertiary"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:drawableTint="?colorTertiary"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_state"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_subtitle"
|
|
||||||
app:layout_constraintWidth_default="wrap"
|
|
||||||
tools:text="@string/state_ongoing" />
|
|
||||||
|
|
||||||
<RatingBar
|
|
||||||
android:id="@+id/rating_bar"
|
|
||||||
style="?ratingBarStyleSmall"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:isIndicator="true"
|
|
||||||
android:max="1"
|
|
||||||
android:numStars="5"
|
|
||||||
android:stepSize="0.5"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_state"
|
|
||||||
tools:rating="4" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Barrier
|
|
||||||
android:id="@+id/barrier_header"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:barrierDirection="bottom"
|
|
||||||
app:barrierMargin="@dimen/margin_normal"
|
|
||||||
app:constraint_referenced_ids="imageView_cover,rating_bar" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/info_layout"
|
|
||||||
layout="@layout/layout_details_chips"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/barrier_header" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.ProgressButton
|
|
||||||
android:id="@+id/button_read"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="end|center_vertical"
|
|
||||||
android:layout_marginHorizontal="@dimen/screen_padding"
|
|
||||||
android:layout_marginTop="@dimen/margin_normal"
|
|
||||||
android:foreground="?selectableItemBackground"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingHorizontal="6dp"
|
|
||||||
android:paddingVertical="8dp"
|
|
||||||
android:textColor="?colorOnPrimaryContainer"
|
|
||||||
app:baseColor="?colorSecondaryContainer"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/info_layout"
|
|
||||||
app:progressColor="?colorPrimary"
|
|
||||||
app:subtitleTextAppearance="?textAppearanceBodySmall"
|
|
||||||
app:titleTextAppearance="?textAppearanceButton"
|
|
||||||
tools:max="100"
|
|
||||||
tools:progress="40"
|
|
||||||
tools:subtitle="12 chapters"
|
|
||||||
tools:title="@string/read" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_description_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
|
||||||
android:layout_marginTop="@dimen/margin_normal"
|
|
||||||
android:gravity="center_vertical|start"
|
|
||||||
android:padding="@dimen/grid_spacing"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="@string/description"
|
|
||||||
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/button_description_more"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/button_read" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_description_more"
|
|
||||||
style="@style/Widget.Kotatsu.Button.More"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:text="@string/more"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/textView_description_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
|
||||||
android:id="@+id/textView_description"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_normal"
|
|
||||||
android:layout_marginTop="@dimen/margin_small"
|
|
||||||
android:layout_marginEnd="@dimen/margin_normal"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:maxLines="@integer/details_description_lines"
|
|
||||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_description_title"
|
|
||||||
tools:ignore="UnusedAttribute"
|
|
||||||
tools:text="@tools:sample/lorem/random" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
|
||||||
android:id="@+id/chips_tags"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/margin_small"
|
|
||||||
android:paddingStart="@dimen/screen_padding"
|
|
||||||
android:paddingEnd="@dimen/screen_padding"
|
|
||||||
app:chipSpacingHorizontal="6dp"
|
|
||||||
app:chipSpacingVertical="6dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/textView_description" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_scrobbling_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
|
||||||
android:layout_marginTop="@dimen/margin_normal"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical|start"
|
|
||||||
android:padding="@dimen/grid_spacing"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="@string/tracking"
|
|
||||||
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/button_scrobbling_more"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/chips_tags" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_scrobbling_more"
|
|
||||||
style="@style/Widget.Kotatsu.Button.More"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:text="@string/manage"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/textView_scrobbling_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/recyclerView_scrobbling"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
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/textView_scrobbling_title"
|
|
||||||
tools:itemCount="2"
|
|
||||||
tools:listitem="@layout/item_scrobbling_info"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
|
||||||
android:id="@+id/progressBar"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:indeterminate="true"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:hideAnimationBehavior="outward"
|
|
||||||
app:layout_constraintBottom_toTopOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:showAnimationBehavior="inward"
|
|
||||||
app:trackCornerRadius="0dp"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/group_scrobbling"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:constraint_referenced_ids="recyclerView_scrobbling,textView_scrobbling_title,button_scrobbling_more"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_related_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
|
||||||
android:layout_marginTop="@dimen/margin_small"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical|start"
|
|
||||||
android:padding="@dimen/grid_spacing"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="@string/related_manga"
|
|
||||||
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/button_related_more"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/recyclerView_scrobbling" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_related_more"
|
|
||||||
style="@style/Widget.Kotatsu.Button.More"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:text="@string/show_all"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/textView_related_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/recyclerView_related"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="6dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:nestedScrollingEnabled="false"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingStart="12dp"
|
|
||||||
android:paddingEnd="12dp"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_related_title"
|
|
||||||
tools:listitem="@layout/item_manga_grid" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/group_related"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:constraint_referenced_ids="recyclerView_related,textView_related_title,button_related_more"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:id="@+id/card_chapters"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="@dimen/side_card_offset"
|
|
||||||
android:layout_marginTop="2dp"
|
|
||||||
android:layout_marginEnd="@dimen/side_card_offset"
|
|
||||||
android:layout_marginBottom="@dimen/side_card_offset"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/appbar"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
|
||||||
android:id="@+id/container_side"
|
|
||||||
android:name="org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@ -1,115 +1,393 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
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/layout_root"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".details.ui.DetailsActivity">
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:viewBindingType="android.view.ViewGroup">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/appbar"
|
android:id="@+id/appbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:elevation="0dp"
|
android:fitsSystemWindows="true">
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
android:stateListAnimator="@null"
|
|
||||||
app:elevation="0dp"
|
|
||||||
app:liftOnScroll="false">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
android:id="@id/toolbar"
|
android:id="@id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="wrap_content" />
|
||||||
android:theme="?attr/actionBarTheme"
|
|
||||||
app:layout_scrollFlags="noScroll"
|
|
||||||
tools:ignore="PrivateResource" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:id="@+id/container_details"
|
android:id="@+id/scrollView"
|
||||||
android:name="org.koitharu.kotatsu.details.ui.DetailsFragment"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
android:clipToPadding="false"
|
||||||
tools:layout="@layout/fragment_details" />
|
android:scrollIndicators="top"
|
||||||
|
android:scrollbars="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/layout_bottom"
|
|
||||||
style="@style/Widget.Material3.BottomSheet"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:behavior_hideable="false"
|
|
||||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.BottomSheet"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<FrameLayout
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
android:id="@+id/layout_bs_header"
|
android:id="@+id/imageView_cover"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="?colorSecondaryContainer"
|
||||||
|
android:foreground="?selectableItemBackground"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintDimensionRatio="H,13:18"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintWidth_percent="0.3"
|
||||||
|
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
|
||||||
|
tools:background="@tools:sample/backgrounds/scenic[5]"
|
||||||
|
tools:ignore="ContentDescription,UnusedAttribute" />
|
||||||
|
|
||||||
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
|
<TextView
|
||||||
android:id="@+id/dragHandle"
|
android:id="@+id/textView_nsfw"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="?actionBarSize"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="top"
|
android:layout_margin="@dimen/card_indicator_offset"
|
||||||
android:paddingTop="0dp"
|
android:background="@drawable/bg_chip"
|
||||||
android:paddingBottom="32dp"
|
android:backgroundTint="@color/warning"
|
||||||
tools:layout_marginBottom="14dp" />
|
android:gravity="center"
|
||||||
|
android:paddingHorizontal="4dp"
|
||||||
|
android:paddingVertical="2dp"
|
||||||
|
android:text="@string/nsfw"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textAppearance="?textAppearanceLabelMedium"
|
||||||
|
android:textColor="?colorOnError"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/imageView_cover" />
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
||||||
android:id="@+id/toolbar_chapters"
|
android:id="@+id/textView_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="?actionBarSize"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom"
|
android:layout_marginStart="16dp"
|
||||||
android:theme="@style/ThemeOverlay.Kotatsu.MainToolbar"
|
android:layout_marginTop="16dp"
|
||||||
tools:menu="@menu/opt_chapters">
|
android:layout_marginEnd="16dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="5"
|
||||||
|
android:textAppearance="?attr/textAppearanceHeadlineSmall"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="@tools:sample/lorem" />
|
||||||
|
|
||||||
|
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
||||||
|
android:id="@+id/textView_subtitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="3"
|
||||||
|
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/textView_title"
|
||||||
|
tools:text="@tools:sample/lorem[12]" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/button_dropdown"
|
android:id="@+id/imageView_state"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginVertical="0.5dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/textView_state"
|
||||||
|
app:layout_constraintDimensionRatio="1"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/textView_state"
|
||||||
|
tools:src="@drawable/ic_state_ongoing" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_state"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:drawablePadding="4dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:labelFor="@id/imageView_state"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="?colorTertiary"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:drawableTint="?colorTertiary"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/imageView_state"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/textView_subtitle"
|
||||||
|
app:layout_constraintWidth_default="wrap"
|
||||||
|
tools:text="@string/state_ongoing" />
|
||||||
|
|
||||||
|
<RatingBar
|
||||||
|
android:id="@+id/rating_bar"
|
||||||
|
style="?ratingBarStyleSmall"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="start|center_vertical"
|
android:layout_marginStart="16dp"
|
||||||
android:background="?selectableItemBackgroundBorderless"
|
android:layout_marginTop="4dp"
|
||||||
android:contentDescription="@string/translations"
|
android:layout_marginEnd="16dp"
|
||||||
android:padding="@dimen/margin_small"
|
android:isIndicator="true"
|
||||||
android:src="@drawable/ic_expand_more" />
|
android:max="1"
|
||||||
|
android:numStars="5"
|
||||||
|
android:stepSize="0.5"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/textView_state"
|
||||||
|
tools:rating="4" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Barrier
|
||||||
|
android:id="@+id/barrier_header"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:barrierDirection="bottom"
|
||||||
|
app:barrierMargin="@dimen/margin_normal"
|
||||||
|
app:constraint_referenced_ids="imageView_cover,rating_bar" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<include
|
||||||
|
android:id="@+id/info_layout"
|
||||||
|
layout="@layout/layout_details_chips"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/barrier_header" />
|
||||||
|
|
||||||
|
<org.koitharu.kotatsu.core.ui.widgets.ProgressButton
|
||||||
android:id="@+id/button_read"
|
android:id="@+id/button_read"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="@dimen/margin_normal"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:foreground="?selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingHorizontal="6dp"
|
||||||
|
android:paddingVertical="8dp"
|
||||||
|
android:textColor="?colorOnPrimaryContainer"
|
||||||
|
app:baseColor="?colorSecondaryContainer"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/button_chapters"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/info_layout"
|
||||||
|
app:progressColor="?colorPrimary"
|
||||||
|
app:subtitleTextAppearance="?textAppearanceBodySmall"
|
||||||
|
app:titleTextAppearance="?textAppearanceButton"
|
||||||
|
tools:max="100"
|
||||||
|
tools:progress="40"
|
||||||
|
tools:subtitle="12 chapters"
|
||||||
|
tools:title="@string/read" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/button_chapters"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:background="@drawable/bg_circle_button"
|
||||||
|
android:backgroundTint="?colorSecondaryContainer"
|
||||||
|
android:contentDescription="@string/chapters"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/button_read"
|
||||||
|
app:layout_constraintDimensionRatio="1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/button_read"
|
||||||
|
app:srcCompat="@drawable/ic_list_sheet" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_description_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/margin_small"
|
||||||
|
android:layout_marginTop="@dimen/margin_normal"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center_vertical|start"
|
||||||
|
android:padding="@dimen/grid_spacing"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/description"
|
||||||
|
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/button_description_more"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/button_read" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_description_more"
|
||||||
|
style="@style/Widget.Kotatsu.Button.More"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="end|center_vertical"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginHorizontal="@dimen/toolbar_button_margin"
|
android:text="@string/more"
|
||||||
android:enabled="false"
|
app:layout_constraintBaseline_toBaselineOf="@id/textView_description_title"
|
||||||
android:text="@string/read"
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
android:textAllCaps="false"
|
|
||||||
app:iconGravity="textStart"
|
|
||||||
app:iconPadding="8dp"
|
|
||||||
tools:enabled="true"
|
|
||||||
tools:icon="@drawable/ic_read" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.MaterialToolbar>
|
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
||||||
|
android:id="@+id/textView_description"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/margin_normal"
|
||||||
|
android:layout_marginTop="@dimen/margin_small"
|
||||||
|
android:layout_marginEnd="@dimen/margin_normal"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:lineSpacingMultiplier="1.2"
|
||||||
|
android:maxLines="@integer/details_description_lines"
|
||||||
|
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/textView_description_title"
|
||||||
|
tools:ignore="UnusedAttribute"
|
||||||
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
|
||||||
</FrameLayout>
|
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
||||||
|
android:id="@+id/chips_tags"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/margin_small"
|
||||||
|
android:paddingStart="@dimen/screen_padding"
|
||||||
|
android:paddingEnd="@dimen/screen_padding"
|
||||||
|
app:chipSpacingHorizontal="6dp"
|
||||||
|
app:chipSpacingVertical="6dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/textView_description" />
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
<TextView
|
||||||
android:id="@+id/tabs"
|
android:id="@+id/textView_scrobbling_title"
|
||||||
style="@style/Widget.Material3.TabLayout.Secondary"
|
android:layout_width="0dp"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@null"
|
android:layout_marginStart="@dimen/margin_small"
|
||||||
app:tabUnboundedRipple="false" />
|
android:layout_marginTop="@dimen/margin_normal"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center_vertical|start"
|
||||||
|
android:padding="@dimen/grid_spacing"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/tracking"
|
||||||
|
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/button_scrobbling_more"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/chips_tags" />
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<Button
|
||||||
android:id="@+id/pager"
|
android:id="@+id/button_scrobbling_more"
|
||||||
android:layout_width="match_parent"
|
style="@style/Widget.Kotatsu.Button.More"
|
||||||
android:layout_height="match_parent" />
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:text="@string/manage"
|
||||||
|
app:layout_constraintBaseline_toBaselineOf="@id/textView_scrobbling_title"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerView_scrobbling"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
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/textView_scrobbling_title"
|
||||||
|
tools:itemCount="2"
|
||||||
|
tools:listitem="@layout/item_scrobbling_info"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:hideAnimationBehavior="outward"
|
||||||
|
app:layout_constraintBottom_toTopOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:showAnimationBehavior="inward"
|
||||||
|
app:trackCornerRadius="0dp"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/group_scrobbling"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:constraint_referenced_ids="recyclerView_scrobbling,textView_scrobbling_title,button_scrobbling_more"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_related_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/margin_small"
|
||||||
|
android:layout_marginTop="@dimen/margin_small"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center_vertical|start"
|
||||||
|
android:padding="@dimen/grid_spacing"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/related_manga"
|
||||||
|
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/button_related_more"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/recyclerView_scrobbling" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_related_more"
|
||||||
|
style="@style/Widget.Kotatsu.Button.More"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:text="@string/show_all"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBaseline_toBaselineOf="@id/textView_related_title"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerView_related"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="6dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:nestedScrollingEnabled="false"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/textView_related_title"
|
||||||
|
tools:listitem="@layout/item_manga_grid" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/group_related"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:constraint_referenced_ids="recyclerView_related,textView_related_title,button_related_more"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</LinearLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
</LinearLayout>
|
||||||
|
|||||||
@ -1,393 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout
|
|
||||||
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/layout_root"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:viewBindingType="android.view.ViewGroup">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:id="@+id/appbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:fitsSystemWindows="true">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
|
||||||
android:id="@+id/scrollView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:scrollIndicators="top"
|
|
||||||
android:scrollbars="vertical">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
|
||||||
android:id="@+id/imageView_cover"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="?colorSecondaryContainer"
|
|
||||||
android:foreground="?selectableItemBackground"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:layout_constraintDimensionRatio="H,13:18"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintWidth_percent="0.3"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
|
|
||||||
tools:background="@tools:sample/backgrounds/scenic[5]"
|
|
||||||
tools:ignore="ContentDescription,UnusedAttribute" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_nsfw"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="@dimen/card_indicator_offset"
|
|
||||||
android:background="@drawable/bg_chip"
|
|
||||||
android:backgroundTint="@color/warning"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingHorizontal="4dp"
|
|
||||||
android:paddingVertical="2dp"
|
|
||||||
android:text="@string/nsfw"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textAppearance="?textAppearanceLabelMedium"
|
|
||||||
android:textColor="?colorOnError"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintEnd_toEndOf="@id/imageView_cover" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
|
||||||
android:id="@+id/textView_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="5"
|
|
||||||
android:textAppearance="?attr/textAppearanceHeadlineSmall"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="@tools:sample/lorem" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
|
||||||
android:id="@+id/textView_subtitle"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="3"
|
|
||||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_title"
|
|
||||||
tools:text="@tools:sample/lorem[12]" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/imageView_state"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginVertical="0.5dp"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/textView_state"
|
|
||||||
app:layout_constraintDimensionRatio="1"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/textView_state"
|
|
||||||
tools:src="@drawable/ic_state_ongoing" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_state"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:drawablePadding="4dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:labelFor="@id/imageView_state"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textColor="?colorTertiary"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:drawableTint="?colorTertiary"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_state"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_subtitle"
|
|
||||||
app:layout_constraintWidth_default="wrap"
|
|
||||||
tools:text="@string/state_ongoing" />
|
|
||||||
|
|
||||||
<RatingBar
|
|
||||||
android:id="@+id/rating_bar"
|
|
||||||
style="?ratingBarStyleSmall"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:isIndicator="true"
|
|
||||||
android:max="1"
|
|
||||||
android:numStars="5"
|
|
||||||
android:stepSize="0.5"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_state"
|
|
||||||
tools:rating="4" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Barrier
|
|
||||||
android:id="@+id/barrier_header"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:barrierDirection="bottom"
|
|
||||||
app:barrierMargin="@dimen/margin_normal"
|
|
||||||
app:constraint_referenced_ids="imageView_cover,rating_bar" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/info_layout"
|
|
||||||
layout="@layout/layout_details_chips"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/barrier_header" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.ProgressButton
|
|
||||||
android:id="@+id/button_read"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="@dimen/margin_normal"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:foreground="?selectableItemBackground"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingHorizontal="6dp"
|
|
||||||
android:paddingVertical="8dp"
|
|
||||||
android:textColor="?colorOnPrimaryContainer"
|
|
||||||
app:baseColor="?colorSecondaryContainer"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/button_chapters"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/info_layout"
|
|
||||||
app:progressColor="?colorPrimary"
|
|
||||||
app:subtitleTextAppearance="?textAppearanceBodySmall"
|
|
||||||
app:titleTextAppearance="?textAppearanceButton"
|
|
||||||
tools:max="100"
|
|
||||||
tools:progress="40"
|
|
||||||
tools:subtitle="12 chapters"
|
|
||||||
tools:title="@string/read" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/button_chapters"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:background="@drawable/bg_circle_button"
|
|
||||||
android:backgroundTint="?colorSecondaryContainer"
|
|
||||||
android:contentDescription="@string/chapters"
|
|
||||||
android:scaleType="centerInside"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/button_read"
|
|
||||||
app:layout_constraintDimensionRatio="1"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/button_read"
|
|
||||||
app:srcCompat="@drawable/ic_list_sheet" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_description_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
|
||||||
android:layout_marginTop="@dimen/margin_normal"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical|start"
|
|
||||||
android:padding="@dimen/grid_spacing"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="@string/description"
|
|
||||||
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/button_description_more"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/button_read" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_description_more"
|
|
||||||
style="@style/Widget.Kotatsu.Button.More"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:text="@string/more"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/textView_description_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
|
||||||
android:id="@+id/textView_description"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_normal"
|
|
||||||
android:layout_marginTop="@dimen/margin_small"
|
|
||||||
android:layout_marginEnd="@dimen/margin_normal"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:maxLines="@integer/details_description_lines"
|
|
||||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_description_title"
|
|
||||||
tools:ignore="UnusedAttribute"
|
|
||||||
tools:text="@tools:sample/lorem/random" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
|
||||||
android:id="@+id/chips_tags"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/margin_small"
|
|
||||||
android:paddingStart="@dimen/screen_padding"
|
|
||||||
android:paddingEnd="@dimen/screen_padding"
|
|
||||||
app:chipSpacingHorizontal="6dp"
|
|
||||||
app:chipSpacingVertical="6dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/textView_description" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_scrobbling_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
|
||||||
android:layout_marginTop="@dimen/margin_normal"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical|start"
|
|
||||||
android:padding="@dimen/grid_spacing"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="@string/tracking"
|
|
||||||
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/button_scrobbling_more"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/chips_tags" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_scrobbling_more"
|
|
||||||
style="@style/Widget.Kotatsu.Button.More"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:text="@string/manage"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/textView_scrobbling_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/recyclerView_scrobbling"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
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/textView_scrobbling_title"
|
|
||||||
tools:itemCount="2"
|
|
||||||
tools:listitem="@layout/item_scrobbling_info"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
|
||||||
android:id="@+id/progressBar"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:indeterminate="true"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:hideAnimationBehavior="outward"
|
|
||||||
app:layout_constraintBottom_toTopOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:showAnimationBehavior="inward"
|
|
||||||
app:trackCornerRadius="0dp"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/group_scrobbling"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:constraint_referenced_ids="recyclerView_scrobbling,textView_scrobbling_title,button_scrobbling_more"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_related_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
|
||||||
android:layout_marginTop="@dimen/margin_small"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical|start"
|
|
||||||
android:padding="@dimen/grid_spacing"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="@string/related_manga"
|
|
||||||
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/button_related_more"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/recyclerView_scrobbling" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_related_more"
|
|
||||||
style="@style/Widget.Kotatsu.Button.More"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:text="@string/show_all"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/textView_related_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/recyclerView_related"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="6dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:nestedScrollingEnabled="false"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingStart="12dp"
|
|
||||||
android:paddingEnd="12dp"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_related_title"
|
|
||||||
tools:listitem="@layout/item_manga_grid" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/group_related"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:constraint_referenced_ids="recyclerView_related,textView_related_title,button_related_more"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
</LinearLayout>
|
|
||||||
@ -1,410 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.core.widget.NestedScrollView
|
|
||||||
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/scrollView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:scrollIndicators="top"
|
|
||||||
android:scrollbars="vertical"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
|
||||||
android:id="@+id/imageView_cover"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="?colorSecondaryContainer"
|
|
||||||
android:foreground="?selectableItemBackground"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:layout_constraintDimensionRatio="H,13:18"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintWidth_percent="0.3"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
|
|
||||||
tools:background="@tools:sample/backgrounds/scenic[5]"
|
|
||||||
tools:ignore="ContentDescription,UnusedAttribute" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.history.ui.util.ReadingProgressView
|
|
||||||
android:id="@+id/progressView"
|
|
||||||
android:layout_width="@dimen/card_indicator_size"
|
|
||||||
android:layout_height="@dimen/card_indicator_size"
|
|
||||||
android:layout_margin="@dimen/card_indicator_offset"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintEnd_toEndOf="@id/imageView_cover" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
|
||||||
android:id="@+id/textView_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="5"
|
|
||||||
android:textAppearance="?attr/textAppearanceHeadlineSmall"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="@tools:sample/lorem" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
|
||||||
android:id="@+id/textView_subtitle"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="3"
|
|
||||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_title"
|
|
||||||
tools:text="@tools:sample/lorem[12]" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_author"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:background="@drawable/list_selector"
|
|
||||||
android:padding="2dp"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textColor="?attr/colorTertiary"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_subtitle"
|
|
||||||
app:layout_constraintWidth_default="wrap"
|
|
||||||
tools:text="@tools:sample/full_names" />
|
|
||||||
|
|
||||||
<RatingBar
|
|
||||||
android:id="@+id/rating_bar"
|
|
||||||
style="?ratingBarStyleSmall"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:isIndicator="true"
|
|
||||||
android:max="1"
|
|
||||||
android:numStars="5"
|
|
||||||
android:stepSize="0.5"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_author"
|
|
||||||
tools:rating="4" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Barrier
|
|
||||||
android:id="@+id/barrier_header"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:barrierDirection="bottom"
|
|
||||||
app:barrierMargin="8dp"
|
|
||||||
app:constraint_referenced_ids="imageView_cover,rating_bar" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/info_layout"
|
|
||||||
layout="@layout/layout_details_info"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/barrier_header" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
|
||||||
android:id="@+id/chips_tags"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
app:chipSpacingHorizontal="6dp"
|
|
||||||
app:chipSpacingVertical="6dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/info_layout" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/approximate_read_time_layout"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/margin_normal"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/chips_tags"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:src="@drawable/ic_timelapse"
|
|
||||||
app:tint="?attr/colorControlNormal" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/approximate_read_time_title"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:paddingVertical="2dp"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="?attr/textAppearanceBodyLarge"
|
|
||||||
tools:text="@string/approximate_reading_time" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/approximate_read_time"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingVertical="2dp"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
|
||||||
tools:text="12 minutes" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_description_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
|
||||||
android:layout_marginTop="@dimen/margin_small"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical|start"
|
|
||||||
android:padding="@dimen/grid_spacing"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="@string/description"
|
|
||||||
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/button_description_more"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/approximate_read_time_layout" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_description_more"
|
|
||||||
style="@style/Widget.Kotatsu.Button.More"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:text="@string/more"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/textView_description_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
|
||||||
android:id="@+id/textView_description"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_normal"
|
|
||||||
android:layout_marginTop="@dimen/margin_small"
|
|
||||||
android:layout_marginEnd="@dimen/margin_normal"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:maxLines="@integer/details_description_lines"
|
|
||||||
android:paddingBottom="@dimen/margin_normal"
|
|
||||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_description_title"
|
|
||||||
tools:ignore="UnusedAttribute"
|
|
||||||
tools:text="@tools:sample/lorem/random" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_bookmarks_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
|
||||||
android:layout_marginTop="@dimen/margin_small"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical|start"
|
|
||||||
android:padding="@dimen/grid_spacing"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="@string/bookmarks"
|
|
||||||
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/button_bookmarks_more"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_description" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_bookmarks_more"
|
|
||||||
style="@style/Widget.Kotatsu.Button.More"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:text="@string/show_all"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/textView_bookmarks_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/recyclerView_bookmarks"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="6dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:nestedScrollingEnabled="false"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingStart="12dp"
|
|
||||||
android:paddingEnd="12dp"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_bookmarks_title"
|
|
||||||
tools:listitem="@layout/item_bookmark" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_scrobbling_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
|
||||||
android:layout_marginTop="@dimen/margin_small"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical|start"
|
|
||||||
android:padding="@dimen/grid_spacing"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="@string/tracking"
|
|
||||||
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/button_scrobbling_more"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/recyclerView_bookmarks" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_scrobbling_more"
|
|
||||||
style="@style/Widget.Kotatsu.Button.More"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:text="@string/manage"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/textView_scrobbling_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/recyclerView_scrobbling"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
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/textView_scrobbling_title"
|
|
||||||
tools:itemCount="2"
|
|
||||||
tools:listitem="@layout/item_scrobbling_info"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
|
||||||
android:id="@+id/progressBar"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:indeterminate="true"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:hideAnimationBehavior="outward"
|
|
||||||
app:layout_constraintBottom_toTopOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:showAnimationBehavior="inward"
|
|
||||||
app:trackCornerRadius="0dp"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/group_bookmarks"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:constraint_referenced_ids="recyclerView_bookmarks,textView_bookmarks_title,button_bookmarks_more"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/group_scrobbling"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:constraint_referenced_ids="recyclerView_scrobbling,textView_scrobbling_title,button_scrobbling_more"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView_related_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
|
||||||
android:layout_marginTop="@dimen/margin_small"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical|start"
|
|
||||||
android:padding="@dimen/grid_spacing"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="@string/related_manga"
|
|
||||||
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/button_related_more"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/recyclerView_scrobbling" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_related_more"
|
|
||||||
style="@style/Widget.Kotatsu.Button.More"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:text="@string/show_all"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/textView_related_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/recyclerView_related"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="6dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:nestedScrollingEnabled="false"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingStart="12dp"
|
|
||||||
android:paddingEnd="12dp"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_related_title"
|
|
||||||
tools:listitem="@layout/item_manga_grid" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/group_related"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:constraint_referenced_ids="recyclerView_related,textView_related_title,button_related_more"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout
|
|
||||||
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:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetHeaderBar
|
|
||||||
android:id="@+id/headerBar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:title="@string/chapters" />
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.list.fastscroll.FastScrollRecyclerView
|
|
||||||
android:id="@+id/recyclerView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_below="@id/headerBar"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:scrollIndicators="top"
|
|
||||||
app:bubbleSize="small"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
|
||||||
tools:listitem="@layout/item_chapter" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout
|
|
||||||
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:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetHeaderBar
|
|
||||||
android:id="@+id/headerBar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:minHeight="240dp">
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.list.fastscroll.FastScrollRecyclerView
|
|
||||||
android:id="@+id/recyclerView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:padding="@dimen/grid_spacing"
|
|
||||||
android:scrollIndicators="top"
|
|
||||||
app:bubbleSize="small"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
|
||||||
app:spanCount="3"
|
|
||||||
tools:listitem="@layout/item_page_thumb"
|
|
||||||
tools:targetApi="m" />
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
|
||||||
android:id="@+id/progressBar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="top"
|
|
||||||
android:indeterminate="true"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:hideAnimationBehavior="outward"
|
|
||||||
app:showAnimationBehavior="inward"
|
|
||||||
app:trackCornerRadius="0dp"
|
|
||||||
app:trackThickness="2dp"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
Loading…
Reference in New Issue