Cleanup
This commit is contained in:
@@ -95,7 +95,7 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.details.ui.DetailsActivity2"
|
||||
android:name="org.koitharu.kotatsu.details.ui.DetailsActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="${applicationId}.action.VIEW_MANGA" />
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import org.koitharu.kotatsu.core.util.AcraScreenLogger
|
||||
import org.koitharu.kotatsu.core.util.IncognitoModeIndicator
|
||||
import org.koitharu.kotatsu.core.util.ext.connectivityManager
|
||||
import org.koitharu.kotatsu.core.util.ext.isLowRamDevice
|
||||
import org.koitharu.kotatsu.details.ui.pager.pages.MangaPageFetcher
|
||||
import org.koitharu.kotatsu.local.data.CacheDir
|
||||
import org.koitharu.kotatsu.local.data.CbzFetcher
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageChanges
|
||||
@@ -50,7 +51,6 @@ import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
import org.koitharu.kotatsu.main.domain.CoverRestoreInterceptor
|
||||
import org.koitharu.kotatsu.main.ui.protect.AppProtectHelper
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.MangaPageFetcher
|
||||
import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider
|
||||
import org.koitharu.kotatsu.settings.backup.BackupObserver
|
||||
import org.koitharu.kotatsu.sync.domain.SyncController
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -8,93 +8,118 @@ import android.text.style.DynamicDrawableSpan
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.ImageSpan
|
||||
import android.text.style.RelativeSizeSpan
|
||||
import android.transition.AutoTransition
|
||||
import android.transition.Slide
|
||||
import android.transition.TransitionManager
|
||||
import android.view.Gravity
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
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.view.MenuHost
|
||||
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.preference.PreferenceManager
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
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 com.google.android.material.tabs.TabLayoutMediator
|
||||
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.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.prefs.AppSettings
|
||||
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.util.ext.doOnExpansionsChanged
|
||||
import org.koitharu.kotatsu.core.util.ext.getAnimationDuration
|
||||
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.isAnimationsEnabled
|
||||
import org.koitharu.kotatsu.core.util.ext.measureHeight
|
||||
import org.koitharu.kotatsu.core.util.ext.menuView
|
||||
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.recyclerView
|
||||
import org.koitharu.kotatsu.core.util.ext.setNavigationBarTransparentCompat
|
||||
import org.koitharu.kotatsu.core.util.ext.setNavigationIconSafe
|
||||
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.ActivityDetailsBinding
|
||||
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.DetailsPagerAdapter
|
||||
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.main.ui.owners.NoModalBottomSheetOwner
|
||||
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.reader.ui.thumbnails.PagesThumbnailsSheet
|
||||
import java.lang.ref.WeakReference
|
||||
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
|
||||
|
||||
@Deprecated("")
|
||||
@AndroidEntryPoint
|
||||
class DetailsActivity :
|
||||
BaseActivity<ActivityDetailsBinding>(),
|
||||
View.OnClickListener,
|
||||
NoModalBottomSheetOwner,
|
||||
View.OnLongClickListener,
|
||||
PopupMenu.OnMenuItemClickListener {
|
||||
View.OnLongClickListener, PopupMenu.OnMenuItemClickListener, View.OnLayoutChangeListener,
|
||||
ViewTreeObserver.OnDrawListener, ChipsView.OnChipClickListener, OnListItemClickListener<Bookmark> {
|
||||
|
||||
@Inject
|
||||
lateinit var appShortcutManager: AppShortcutManager
|
||||
lateinit var shortcutManager: AppShortcutManager
|
||||
|
||||
@Inject
|
||||
lateinit var settings: AppSettings
|
||||
lateinit var coil: ImageLoader
|
||||
|
||||
private var buttonTip: WeakReference<ButtonTip>? = null
|
||||
@Inject
|
||||
lateinit var tagHighlighter: ListExtraProvider
|
||||
|
||||
private val viewModel: DetailsViewModel by viewModels()
|
||||
|
||||
val secondaryMenuHost: MenuHost
|
||||
get() = viewBinding.toolbarChapters ?: this
|
||||
|
||||
var bottomSheetMediator: ChaptersBottomSheetMediator? = null
|
||||
private set
|
||||
private lateinit var chaptersBadge: ViewBadge
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -106,78 +131,157 @@ class DetailsActivity :
|
||||
viewBinding.buttonRead.setOnClickListener(this)
|
||||
viewBinding.buttonRead.setOnLongClickListener(this)
|
||||
viewBinding.buttonRead.setOnContextClickListenerCompat(this)
|
||||
viewBinding.buttonDropdown.setOnClickListener(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)
|
||||
|
||||
if (viewBinding.layoutBottom != null) {
|
||||
val behavior = BottomSheetBehavior.from(checkNotNull(viewBinding.layoutBottom))
|
||||
val bsMediator = ChaptersBottomSheetMediator(behavior, viewBinding.pager, viewBinding.tabs)
|
||||
actionModeDelegate.addListener(bsMediator)
|
||||
checkNotNull(viewBinding.layoutBsHeader).addOnLayoutChangeListener(bsMediator)
|
||||
onBackPressedDispatcher.addCallback(bsMediator)
|
||||
bottomSheetMediator = bsMediator
|
||||
behavior.doOnExpansionsChanged(::onChaptersSheetStateChanged)
|
||||
viewBinding.toolbarChapters?.setNavigationOnClickListener {
|
||||
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
viewBinding.toolbarChapters?.setOnGenericMotionListener(bsMediator)
|
||||
}
|
||||
initPager()
|
||||
chaptersBadge = ViewBadge(viewBinding.buttonChapters ?: viewBinding.buttonRead, this)
|
||||
|
||||
viewModel.manga.filterNotNull().observe(this, ::onMangaUpdated)
|
||||
viewModel.details.filterNotNull().observe(this, ::onMangaUpdated)
|
||||
viewModel.onMangaRemoved.observeEvent(this, ::onMangaRemoved)
|
||||
viewModel.newChaptersCount.observe(this, ::onNewChaptersChanged)
|
||||
viewModel.onError.observeEvent(this, DetailsErrorObserver(this, viewModel, exceptionResolver))
|
||||
viewModel.onActionDone.observeEvent(
|
||||
this,
|
||||
ReversibleActionObserver(viewBinding.containerDetails, viewBinding.layoutBottom),
|
||||
)
|
||||
viewModel.onShowTip.observeEvent(this) { showTip() }
|
||||
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.toolbarChapters?.subtitle = it
|
||||
viewBinding.textViewSubtitle?.textAndVisible = it
|
||||
viewBinding.infoLayout.chipBranch.text = it.ifNullOrEmpty { getString(R.string.system_default) }
|
||||
}
|
||||
val chaptersMenuInvalidator = MenuInvalidator(viewBinding.toolbarChapters ?: this)
|
||||
viewModel.isChaptersReversed.observe(this, chaptersMenuInvalidator)
|
||||
viewModel.isChaptersEmpty.observe(this, chaptersMenuInvalidator)
|
||||
viewModel.favouriteCategories.observe(this, ::onFavoritesChanged)
|
||||
val menuInvalidator = MenuInvalidator(this)
|
||||
viewModel.favouriteCategories.observe(this, menuInvalidator)
|
||||
viewModel.isStatsAvailable.observe(this, menuInvalidator)
|
||||
viewModel.remoteManga.observe(this, menuInvalidator)
|
||||
viewModel.branches.observe(this) {
|
||||
viewBinding.buttonDropdown.isVisible = it.size > 1
|
||||
viewBinding.infoLayout.chipBranch.isVisible = it.size > 1
|
||||
}
|
||||
viewModel.chapters.observe(this, PrefetchObserver(this))
|
||||
viewModel.onDownloadStarted.observeEvent(
|
||||
this,
|
||||
DownloadStartedObserver(viewBinding.containerDetails),
|
||||
DownloadStartedObserver(viewBinding.scrollView),
|
||||
)
|
||||
|
||||
addMenuProvider(
|
||||
DetailsMenuProvider(
|
||||
activity = this,
|
||||
viewModel = viewModel,
|
||||
snackbarHost = viewBinding.pager,
|
||||
appShortcutManager = appShortcutManager,
|
||||
snackbarHost = viewBinding.scrollView,
|
||||
appShortcutManager = shortcutManager,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun getBottomSheetCollapsedHeight(): Int {
|
||||
return viewBinding.layoutBsHeader?.measureHeight() ?: 0
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.button_read -> openReader(isIncognitoMode = false)
|
||||
R.id.button_dropdown -> showBranchPopupMenu(v)
|
||||
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 -> {
|
||||
buttonTip?.get()?.remove()
|
||||
buttonTip = null
|
||||
val menu = PopupMenu(v.context, v)
|
||||
menu.inflate(R.menu.popup_read)
|
||||
menu.menu.findItem(R.id.action_forget)?.isVisible = viewModel.historyInfo.value.run {
|
||||
@@ -204,46 +308,203 @@ class DetailsActivity :
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_pages_thumbs -> {
|
||||
val history = viewModel.historyInfo.value.history
|
||||
PagesThumbnailsSheet.show(
|
||||
fm = supportFragmentManager,
|
||||
manga = viewModel.manga.value ?: return false,
|
||||
chapterId = history?.chapterId
|
||||
?: viewModel.chapters.value.firstOrNull()?.chapter?.id
|
||||
?: return false,
|
||||
currentPage = history?.page ?: 0,
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun onChaptersSheetStateChanged(isExpanded: Boolean) {
|
||||
val toolbar = viewBinding.toolbarChapters ?: return
|
||||
if (isAnimationsEnabled) {
|
||||
val transition = AutoTransition()
|
||||
transition.duration = getAnimationDuration(R.integer.config_shorterAnimTime)
|
||||
TransitionManager.beginDelayedTransition(toolbar, transition)
|
||||
}
|
||||
if (isExpanded) {
|
||||
toolbar.setNavigationIconSafe(materialR.drawable.abc_ic_clear_material)
|
||||
} else {
|
||||
toolbar.navigationIcon = null
|
||||
}
|
||||
toolbar.menuView?.isVisible = isExpanded
|
||||
viewBinding.buttonRead.isGone = isExpanded
|
||||
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()
|
||||
}
|
||||
|
||||
private fun onMangaUpdated(manga: Manga) {
|
||||
title = manga.title
|
||||
val hasChapters = !manga.chapters.isNullOrEmpty()
|
||||
viewBinding.buttonRead.isEnabled = hasChapters
|
||||
invalidateOptionsMenu()
|
||||
showBottomSheet(manga.chapters != null)
|
||||
viewBinding.groupHeader?.isVisible = hasChapters
|
||||
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(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@DetailsActivity)
|
||||
.data(manga.source.faviconUri())
|
||||
.lifecycle(this@DetailsActivity)
|
||||
.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) {
|
||||
@@ -260,32 +521,25 @@ class DetailsActivity :
|
||||
left = insets.left,
|
||||
right = insets.right,
|
||||
)
|
||||
if (insets.bottom > 0) {
|
||||
window.setNavigationBarTransparentCompat(
|
||||
this,
|
||||
viewBinding.layoutBottom?.elevation ?: 0f,
|
||||
0.9f,
|
||||
)
|
||||
}
|
||||
viewBinding.cardChapters?.updateLayoutParams<MarginLayoutParams> {
|
||||
bottomMargin = insets.bottom + marginEnd
|
||||
}
|
||||
viewBinding.dragHandle?.updateLayoutParams<MarginLayoutParams> {
|
||||
bottomMargin = insets.top
|
||||
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) {
|
||||
setText(R.string._continue)
|
||||
setIconResource(if (info.isIncognitoMode) R.drawable.ic_incognito else R.drawable.ic_play)
|
||||
setTitle(R.string._continue)
|
||||
} else {
|
||||
setText(R.string.read)
|
||||
setIconResource(if (info.isIncognitoMode) R.drawable.ic_incognito else R.drawable.ic_play)
|
||||
setTitle(R.string.read)
|
||||
}
|
||||
}
|
||||
val text = when {
|
||||
viewBinding.buttonRead.subtitle = when {
|
||||
!info.isValid -> getString(R.string.loading_)
|
||||
info.currentChapter >= 0 -> getString(
|
||||
R.string.chapter_d_of_d,
|
||||
@@ -300,20 +554,11 @@ class DetailsActivity :
|
||||
info.totalChapters,
|
||||
)
|
||||
}
|
||||
viewBinding.toolbarChapters?.title = text
|
||||
viewBinding.textViewTitle?.text = text
|
||||
viewBinding.buttonRead.setProgress(info.history?.percent?.coerceIn(0f, 1f) ?: 0f, true)
|
||||
}
|
||||
|
||||
private fun onNewChaptersChanged(count: Int) {
|
||||
val tab = viewBinding.tabs.getTabAt(0) ?: return
|
||||
if (count == 0) {
|
||||
tab.removeBadge()
|
||||
} else {
|
||||
val badge = tab.orCreateBadge
|
||||
badge.horizontalOffsetWithText = -resources.getDimensionPixelOffset(R.dimen.margin_small)
|
||||
badge.number = count
|
||||
badge.isVisible = true
|
||||
}
|
||||
chaptersBadge.counter = count
|
||||
}
|
||||
|
||||
private fun showBranchPopupMenu(v: View) {
|
||||
@@ -348,8 +593,11 @@ class DetailsActivity :
|
||||
append(branch.count.toString())
|
||||
}
|
||||
}
|
||||
menu.menu.add(Menu.NONE, Menu.NONE, i, title)
|
||||
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
|
||||
@@ -361,7 +609,7 @@ class DetailsActivity :
|
||||
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.containerDetails, R.string.chapter_is_missing, Snackbar.LENGTH_SHORT)
|
||||
Snackbar.make(viewBinding.scrollView, R.string.chapter_is_missing, Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
} else {
|
||||
startActivity(
|
||||
@@ -377,24 +625,47 @@ class DetailsActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun initPager() {
|
||||
val adapter = DetailsPagerAdapter(this, settings)
|
||||
viewBinding.pager.recyclerView?.isNestedScrollingEnabled = false
|
||||
viewBinding.pager.offscreenPageLimit = 1
|
||||
viewBinding.pager.adapter = adapter
|
||||
TabLayoutMediator(viewBinding.tabs, viewBinding.pager, adapter).attach()
|
||||
viewBinding.pager.setCurrentItem(settings.defaultDetailsTab, false)
|
||||
viewBinding.tabs.isVisible = adapter.itemCount > 1
|
||||
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 showBottomSheet(isVisible: Boolean) {
|
||||
val view = viewBinding.layoutBottom ?: return
|
||||
if (view.isVisible == isVisible) return
|
||||
val transition = Slide(Gravity.BOTTOM)
|
||||
transition.addTarget(view)
|
||||
transition.interpolator = AccelerateDecelerateInterpolator()
|
||||
TransitionManager.beginDelayedTransition(viewBinding.root as ViewGroup, transition)
|
||||
view.isVisible = isVisible
|
||||
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(
|
||||
@@ -415,34 +686,18 @@ class DetailsActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun showTip() {
|
||||
val tip = ButtonTip(viewBinding.root as ViewGroup, insetsDelegate, viewModel)
|
||||
tip.addToRoot()
|
||||
buttonTip = WeakReference(tip)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val TIP_BUTTON = "btn_read"
|
||||
private const val KEY_NEW_ACTIVITY = "new_details_screen"
|
||||
private const val FAV_LABEL_LIMIT = 10
|
||||
|
||||
fun newIntent(context: Context, manga: Manga): Intent {
|
||||
return getActivityIntent(context)
|
||||
return Intent(context, DetailsActivity::class.java)
|
||||
.putExtra(MangaIntent.KEY_MANGA, ParcelableManga(manga))
|
||||
}
|
||||
|
||||
fun newIntent(context: Context, mangaId: Long): Intent {
|
||||
return getActivityIntent(context)
|
||||
return Intent(context, DetailsActivity::class.java)
|
||||
.putExtra(MangaIntent.KEY_ID, mangaId)
|
||||
}
|
||||
|
||||
private fun getActivityIntent(context: Context): Intent {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val useNewActivity = prefs.getBoolean(KEY_NEW_ACTIVITY, true)
|
||||
return Intent(
|
||||
context,
|
||||
if (useNewActivity) DetailsActivity2::class.java else DetailsActivity::class.java,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class DetailsErrorObserver(
|
||||
private val viewModel: DetailsViewModel,
|
||||
resolver: ExceptionResolver?,
|
||||
) : ErrorObserver(
|
||||
activity.viewBinding.containerDetails, null, resolver,
|
||||
activity.viewBinding.scrollView, null, resolver,
|
||||
{ isResolved ->
|
||||
if (isResolved) {
|
||||
viewModel.reload()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import org.koitharu.kotatsu.core.os.AppShortcutManager
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.util.ShareHelper
|
||||
import org.koitharu.kotatsu.download.ui.dialog.DownloadOption
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteSheet
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet
|
||||
import org.koitharu.kotatsu.search.ui.multi.MultiSearchActivity
|
||||
@@ -35,7 +34,6 @@ class DetailsMenuProvider(
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.opt_details, menu)
|
||||
menu.findItem(R.id.action_favourite).isVisible = activity is DetailsActivity
|
||||
}
|
||||
|
||||
override fun onPrepareMenu(menu: Menu) {
|
||||
@@ -48,9 +46,6 @@ class DetailsMenuProvider(
|
||||
menu.findItem(R.id.action_scrobbling).isVisible = viewModel.isScrobblingAvailable
|
||||
menu.findItem(R.id.action_online).isVisible = viewModel.remoteManga.value != null
|
||||
menu.findItem(R.id.action_stats).isVisible = viewModel.isStatsAvailable.value
|
||||
menu.findItem(R.id.action_favourite).setIcon(
|
||||
if (viewModel.favouriteCategories.value.isNotEmpty()) R.drawable.ic_heart else R.drawable.ic_heart_outline,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
@@ -66,12 +61,6 @@ class DetailsMenuProvider(
|
||||
}
|
||||
}
|
||||
|
||||
R.id.action_favourite -> {
|
||||
viewModel.manga.value?.let {
|
||||
FavoriteSheet.show(activity.supportFragmentManager, it)
|
||||
}
|
||||
}
|
||||
|
||||
R.id.action_delete -> {
|
||||
val title = viewModel.manga.value?.title.orEmpty()
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
|
||||
@@ -11,8 +11,6 @@ import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNot
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
@@ -247,12 +245,6 @@ class DetailsViewModel @Inject constructor(
|
||||
localStorageChanges
|
||||
.collect { onDownloadComplete(it) }
|
||||
}
|
||||
launchJob(Dispatchers.Default) {
|
||||
if (settings.isTipEnabled(DetailsActivity.TIP_BUTTON)) {
|
||||
manga.filterNot { it?.chapters.isNullOrEmpty() }.first()
|
||||
onShowTip.call(Unit)
|
||||
}
|
||||
}
|
||||
launchJob(Dispatchers.Default) {
|
||||
val manga = details.firstOrNull { !it?.chapters.isNullOrEmpty() } ?: return@launchJob
|
||||
val h = history.firstOrNull()
|
||||
@@ -363,10 +355,6 @@ class DetailsViewModel @Inject constructor(
|
||||
onSelectChapter.call(chapter.chapter.id)
|
||||
}
|
||||
|
||||
fun onButtonTipClosed() {
|
||||
settings.closeTip(DetailsActivity.TIP_BUTTON)
|
||||
}
|
||||
|
||||
fun removeFromHistory() {
|
||||
launchJob(Dispatchers.Default) {
|
||||
val handle = historyRepository.delete(setOf(mangaId))
|
||||
|
||||
@@ -5,12 +5,11 @@ 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.bookmarks.MangaBookmarksFragment
|
||||
import org.koitharu.kotatsu.details.ui.pager.chapters.ChaptersFragment
|
||||
import org.koitharu.kotatsu.details.ui.pager.pages.PagesFragment
|
||||
|
||||
class DetailsPagerAdapter2(
|
||||
class ChaptersPagesAdapter(
|
||||
fragment: Fragment,
|
||||
val isPagesTabEnabled: Boolean,
|
||||
) : FragmentStateAdapter(fragment),
|
||||
@@ -39,7 +39,7 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
|
||||
disableFitToContents()
|
||||
|
||||
val args = arguments ?: Bundle.EMPTY
|
||||
val adapter = DetailsPagerAdapter2(this, args.getBoolean(ARG_SHOW_PAGES, settings.isPagesTabEnabled))
|
||||
val adapter = ChaptersPagesAdapter(this, args.getBoolean(ARG_SHOW_PAGES, settings.isPagesTabEnabled))
|
||||
binding.pager.recyclerView?.isNestedScrollingEnabled = false
|
||||
binding.pager.offscreenPageLimit = adapter.itemCount
|
||||
binding.pager.adapter = adapter
|
||||
|
||||
@@ -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
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import androidx.appcompat.view.ActionMode
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
@@ -28,8 +27,6 @@ import org.koitharu.kotatsu.core.util.ext.findParentCallback
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.databinding.FragmentChaptersBinding
|
||||
import org.koitharu.kotatsu.details.ui.ChaptersMenuProvider
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.details.ui.DetailsViewModel
|
||||
import org.koitharu.kotatsu.details.ui.adapter.ChaptersAdapter
|
||||
import org.koitharu.kotatsu.details.ui.adapter.ChaptersSelectionDecoration
|
||||
@@ -96,12 +93,6 @@ class ChaptersFragment :
|
||||
viewModel.onSelectChapter.observeEvent(viewLifecycleOwner) {
|
||||
selectionController?.onItemLongClick(it)
|
||||
}
|
||||
val detailsActivity = activity as? DetailsActivity
|
||||
if (detailsActivity != null) {
|
||||
val menuProvider = ChaptersMenuProvider(viewModel, detailsActivity.bottomSheetMediator)
|
||||
activity?.onBackPressedDispatcher?.addCallback(menuProvider)
|
||||
detailsActivity.secondaryMenuHost.addMenuProvider(menuProvider, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
||||
@@ -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.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.reader.ui.pager.ReaderPage
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.koitharu.kotatsu.reader.ui.thumbnails.adapter
|
||||
package org.koitharu.kotatsu.details.ui.pager.pages
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
@@ -15,7 +15,6 @@ import org.koitharu.kotatsu.core.util.ext.setTextColorAttr
|
||||
import org.koitharu.kotatsu.core.util.ext.source
|
||||
import org.koitharu.kotatsu.databinding.ItemPageThumbBinding
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.PageThumbnail
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
fun pageThumbnailAD(
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.koitharu.kotatsu.reader.ui.thumbnails.adapter
|
||||
package org.koitharu.kotatsu.details.ui.pager.pages
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
@@ -9,7 +9,6 @@ import org.koitharu.kotatsu.core.ui.list.fastscroll.FastScroller
|
||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
||||
import org.koitharu.kotatsu.list.ui.adapter.listHeaderAD
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.PageThumbnail
|
||||
|
||||
class PageThumbnailAdapter(
|
||||
coil: ImageLoader,
|
||||
@@ -37,8 +37,6 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderNavigationCallback
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.PageThumbnail
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.adapter.PageThumbnailAdapter
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import org.koitharu.kotatsu.details.data.MangaDetails
|
||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.reader.domain.ChaptersLoader
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.PageThumbnail
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -61,16 +61,13 @@ import org.koitharu.kotatsu.reader.ui.config.ReaderConfigSheet
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderUiState
|
||||
import org.koitharu.kotatsu.reader.ui.tapgrid.TapGridDispatcher
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.OnPageSelectListener
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ReaderActivity :
|
||||
BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
ChaptersSheet.OnChapterChangeListener,
|
||||
TapGridDispatcher.OnGridTouchListener,
|
||||
OnPageSelectListener,
|
||||
ReaderConfigSheet.Callback,
|
||||
ReaderControlDelegate.OnInteractionListener,
|
||||
OnApplyWindowInsetsListener,
|
||||
@@ -119,7 +116,7 @@ class ReaderActivity :
|
||||
controlDelegate = ReaderControlDelegate(resources, settings, tapGridSettings, this)
|
||||
viewBinding.slider.setLabelFormatter(PageLabelFormatter())
|
||||
viewBinding.zoomControl.listener = this
|
||||
ReaderSliderListener(this, viewModel).attachToSlider(viewBinding.slider)
|
||||
ReaderSliderListener(viewModel, this).attachToSlider(viewBinding.slider)
|
||||
insetsDelegate.interceptingWindowInsetsListener = this
|
||||
idlingDetector.bindToLifecycle(this)
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import androidx.fragment.app.FragmentActivity
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderConfigSheet
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.PagesThumbnailsSheet
|
||||
import org.koitharu.kotatsu.settings.SettingsActivity
|
||||
|
||||
class ReaderBottomMenuProvider(
|
||||
|
||||
@@ -2,11 +2,10 @@ package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import com.google.android.material.slider.Slider
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.OnPageSelectListener
|
||||
|
||||
class ReaderSliderListener(
|
||||
private val pageSelectListener: OnPageSelectListener,
|
||||
private val viewModel: ReaderViewModel,
|
||||
private val callback: ReaderNavigationCallback,
|
||||
) : Slider.OnChangeListener, Slider.OnSliderTouchListener {
|
||||
|
||||
private var isChanged = false
|
||||
@@ -43,6 +42,6 @@ class ReaderSliderListener(
|
||||
val pages = viewModel.getCurrentChapterPages()
|
||||
val page = pages?.getOrNull(index) ?: return
|
||||
val chapterId = viewModel.getCurrentState()?.chapterId ?: return
|
||||
pageSelectListener.onPageSelected(ReaderPage(page, index, chapterId))
|
||||
callback.onPageSelected(ReaderPage(page, index, chapterId))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,11 @@
|
||||
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.DetailsActivity">
|
||||
tools:context=".details.ui.DetailsActivity"
|
||||
tools:viewBindingType="android.view.ViewGroup">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
@@ -13,92 +15,377 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="0dp"
|
||||
android:fitsSystemWindows="true"
|
||||
android:stateListAnimator="@null"
|
||||
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="?attr/actionBarSize"
|
||||
app:layout_scrollFlags="noScroll"
|
||||
tools:menu="@menu/opt_details">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_read"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginHorizontal="@dimen/toolbar_button_margin"
|
||||
android:enabled="false"
|
||||
android:text="@string/read"
|
||||
android:textAllCaps="false"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar"
|
||||
tools:enabled="true"
|
||||
tools:icon="@drawable/ic_read" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/button_dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/translations"
|
||||
android:padding="@dimen/margin_small"
|
||||
android:src="@drawable/ic_expand_more" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/group_header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginHorizontal="@dimen/toolbar_button_margin"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?textAppearanceTitleMedium"
|
||||
tools:ignore="InconsistentLayout"
|
||||
tools:text="@string/chapter_d_of_d" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?textAppearanceTitleSmall"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:visibility="gone"
|
||||
tools:text="English"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/container_details"
|
||||
android:name="org.koitharu.kotatsu.details.ui.DetailsFragment"
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
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"
|
||||
app:layout_constraintWidth_percent="0.5"
|
||||
tools:layout="@layout/fragment_details" />
|
||||
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"
|
||||
@@ -110,29 +397,14 @@
|
||||
android:layout_marginBottom="@dimen/side_card_offset"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/container_details"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar">
|
||||
app:layout_constraintStart_toEndOf="@id/appbar"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
<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"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
style="@style/Widget.Material3.TabLayout.Secondary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
app:tabUnboundedRipple="false" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -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"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
<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"
|
||||
tools:context=".details.ui.DetailsActivity">
|
||||
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:elevation="0dp"
|
||||
android:fitsSystemWindows="true"
|
||||
android:stateListAnimator="@null"
|
||||
app:elevation="0dp"
|
||||
app:liftOnScroll="false">
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:theme="?attr/actionBarTheme"
|
||||
app:layout_scrollFlags="noScroll"
|
||||
tools:ignore="PrivateResource" />
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/container_details"
|
||||
android:name="org.koitharu.kotatsu.details.ui.DetailsFragment"
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
tools:layout="@layout/fragment_details" />
|
||||
android:clipToPadding="false"
|
||||
android:scrollIndicators="top"
|
||||
android:scrollbars="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_bottom"
|
||||
style="@style/Widget.Material3.BottomSheet"
|
||||
android:layout_width="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
|
||||
android:id="@+id/layout_bs_header"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
|
||||
android:id="@+id/dragHandle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?actionBarSize"
|
||||
android:layout_gravity="top"
|
||||
android:paddingTop="0dp"
|
||||
android:paddingBottom="32dp"
|
||||
tools:layout_marginBottom="14dp" />
|
||||
<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" />
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_chapters"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?actionBarSize"
|
||||
android:layout_gravity="bottom"
|
||||
android:theme="@style/ThemeOverlay.Kotatsu.MainToolbar"
|
||||
tools:menu="@menu/opt_chapters">
|
||||
<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" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/button_dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/translations"
|
||||
android:padding="@dimen/margin_small"
|
||||
android:src="@drawable/ic_expand_more" />
|
||||
<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" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_read"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginHorizontal="@dimen/toolbar_button_margin"
|
||||
android:enabled="false"
|
||||
android:text="@string/read"
|
||||
android:textAllCaps="false"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="8dp"
|
||||
tools:enabled="true"
|
||||
tools:icon="@drawable/ic_read" />
|
||||
<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]" />
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
<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" />
|
||||
|
||||
</FrameLayout>
|
||||
<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" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
style="@style/Widget.Material3.TabLayout.Secondary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
app:tabUnboundedRipple="false" />
|
||||
<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.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
<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" />
|
||||
|
||||
</LinearLayout>
|
||||
<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" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
<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,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>
|
||||
@@ -3,13 +3,6 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_favourite"
|
||||
android:icon="@drawable/ic_heart_outline"
|
||||
android:orderInCategory="10"
|
||||
android:title="@string/add_to_favourites"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_share"
|
||||
android:icon="?actionModeShareDrawable"
|
||||
|
||||
Reference in New Issue
Block a user