New details activity
parent
905f565766
commit
b27d5607ac
@ -0,0 +1,144 @@
|
|||||||
|
package org.koitharu.kotatsu.core.ui.widgets
|
||||||
|
|
||||||
|
import android.animation.ValueAnimator
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Outline
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewOutlineProvider
|
||||||
|
import android.view.animation.AccelerateDecelerateInterpolator
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
import androidx.core.content.withStyledAttributes
|
||||||
|
import androidx.core.widget.TextViewCompat
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.getAnimationDuration
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.resolveDp
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.setTextAndVisible
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.textAndVisible
|
||||||
|
import com.google.android.material.R as materialR
|
||||||
|
|
||||||
|
class ProgressButton @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0,
|
||||||
|
) : LinearLayoutCompat(context, attrs, defStyleAttr), ValueAnimator.AnimatorUpdateListener {
|
||||||
|
|
||||||
|
private val textViewTitle = TextView(context)
|
||||||
|
private val textViewSubtitle = TextView(context)
|
||||||
|
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||||
|
|
||||||
|
private var progress = 0f
|
||||||
|
private var colorBase = context.getThemeColor(materialR.attr.colorSecondaryContainer)
|
||||||
|
private var colorProgress = context.getThemeColor(materialR.attr.colorPrimary)
|
||||||
|
private var progressAnimator: ValueAnimator? = null
|
||||||
|
|
||||||
|
var title: CharSequence?
|
||||||
|
get() = textViewTitle.textAndVisible
|
||||||
|
set(value) {
|
||||||
|
textViewTitle.textAndVisible = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var subtitle: CharSequence?
|
||||||
|
get() = textViewSubtitle.textAndVisible
|
||||||
|
set(value) {
|
||||||
|
textViewSubtitle.textAndVisible = value
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
orientation = VERTICAL
|
||||||
|
outlineProvider = OutlineProvider()
|
||||||
|
clipToOutline = true
|
||||||
|
|
||||||
|
context.withStyledAttributes(attrs, R.styleable.ProgressButton, defStyleAttr) {
|
||||||
|
val textAppearanceFallback = androidx.appcompat.R.style.TextAppearance_AppCompat
|
||||||
|
TextViewCompat.setTextAppearance(
|
||||||
|
textViewTitle,
|
||||||
|
getResourceId(R.styleable.ProgressButton_titleTextAppearance, textAppearanceFallback),
|
||||||
|
)
|
||||||
|
TextViewCompat.setTextAppearance(
|
||||||
|
textViewSubtitle,
|
||||||
|
getResourceId(R.styleable.ProgressButton_subtitleTextAppearance, textAppearanceFallback),
|
||||||
|
)
|
||||||
|
textViewTitle.text = getText(R.styleable.ProgressButton_title)
|
||||||
|
textViewSubtitle.text = getText(R.styleable.ProgressButton_subtitle)
|
||||||
|
colorBase = getColor(R.styleable.ProgressButton_baseColor, colorBase)
|
||||||
|
colorProgress = getColor(R.styleable.ProgressButton_progressColor, colorProgress)
|
||||||
|
progress = getInt(R.styleable.ProgressButton_android_progress, 0).toFloat() /
|
||||||
|
getInt(R.styleable.ProgressButton_android_max, 100).toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
addView(textViewTitle, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))
|
||||||
|
addView(
|
||||||
|
textViewSubtitle,
|
||||||
|
LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).also { lp ->
|
||||||
|
lp.topMargin = context.resources.resolveDp(2)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
paint.style = Paint.Style.FILL
|
||||||
|
paint.color = colorProgress
|
||||||
|
applyGravity()
|
||||||
|
setWillNotDraw(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
canvas.drawColor(colorBase)
|
||||||
|
canvas.drawRect(0f, 0f, width * progress, height.toFloat(), paint)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setGravity(gravity: Int) {
|
||||||
|
super.setGravity(gravity)
|
||||||
|
if (childCount != 0) {
|
||||||
|
applyGravity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationUpdate(animation: ValueAnimator) {
|
||||||
|
progress = animation.animatedValue as Float
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTitle(@StringRes titleResId: Int) {
|
||||||
|
textViewTitle.setTextAndVisible(titleResId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSubtitle(@StringRes titleResId: Int) {
|
||||||
|
textViewSubtitle.setTextAndVisible(titleResId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setProgress(value: Float, animate: Boolean) {
|
||||||
|
progressAnimator?.cancel()
|
||||||
|
if (animate) {
|
||||||
|
progressAnimator = ValueAnimator.ofFloat(progress, value).apply {
|
||||||
|
duration = context.getAnimationDuration(android.R.integer.config_shortAnimTime)
|
||||||
|
interpolator = AccelerateDecelerateInterpolator()
|
||||||
|
addUpdateListener(this@ProgressButton)
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
progressAnimator = null
|
||||||
|
progress = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyGravity() {
|
||||||
|
val value = (gravity and Gravity.HORIZONTAL_GRAVITY_MASK) or Gravity.CENTER_VERTICAL
|
||||||
|
textViewTitle.gravity = value
|
||||||
|
textViewSubtitle.gravity = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OutlineProvider : ViewOutlineProvider() {
|
||||||
|
|
||||||
|
override fun getOutline(view: View, outline: Outline) {
|
||||||
|
outline.setRoundRect(0, 0, view.width, view.height, view.height / 2f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,728 @@
|
|||||||
|
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.ViewTreeObserver
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.appcompat.widget.PopupMenu
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
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.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.bookmarks.ui.adapter.BookmarksAdapter
|
||||||
|
import org.koitharu.kotatsu.bookmarks.ui.sheet.BookmarksSheet
|
||||||
|
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.drawableStart
|
||||||
|
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.showDistinct
|
||||||
|
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
|
||||||
|
import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
|
||||||
|
import org.koitharu.kotatsu.reader.ui.thumbnails.PagesThumbnailsSheet
|
||||||
|
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
|
||||||
|
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.imageViewCover.setOnClickListener(this)
|
||||||
|
viewBinding.buttonDescriptionMore.setOnClickListener(this)
|
||||||
|
viewBinding.buttonBookmarksMore.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, 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.bookmarks.observe(this, ::onBookmarksChanged)
|
||||||
|
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().showDistinct(supportFragmentManager, "ChaptersPagesSheet")
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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_bookmarks_more -> {
|
||||||
|
val manga = viewModel.manga.value ?: return
|
||||||
|
BookmarksSheet.show(supportFragmentManager, manga)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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?) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
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 onBookmarksChanged(bookmarks: List<Bookmark>) {
|
||||||
|
var adapter = viewBinding.recyclerViewBookmarks.adapter as? BookmarksAdapter
|
||||||
|
viewBinding.groupBookmarks.isGone = bookmarks.isEmpty()
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter.items = bookmarks
|
||||||
|
} else {
|
||||||
|
adapter = BookmarksAdapter(coil, this, this)
|
||||||
|
adapter.items = bookmarks
|
||||||
|
viewBinding.recyclerViewBookmarks.adapter = adapter
|
||||||
|
val spacing = resources.getDimensionPixelOffset(R.dimen.bookmark_list_spacing)
|
||||||
|
viewBinding.recyclerViewBookmarks.addItemDecoration(SpacingItemDecoration(spacing))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
textViewState.apply {
|
||||||
|
manga.state?.let { state ->
|
||||||
|
textAndVisible = resources.getString(state.titleResId)
|
||||||
|
drawableStart = ContextCompat.getDrawable(context, state.iconResId)
|
||||||
|
} ?: run {
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
menu.menu.add(Menu.NONE, Menu.NONE, i, title)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
package org.koitharu.kotatsu.details.ui.pager
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.view.ActionMode
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
|
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
|
||||||
|
import org.koitharu.kotatsu.core.ui.util.ActionModeListener
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.recyclerView
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.setTabsEnabled
|
||||||
|
import org.koitharu.kotatsu.databinding.SheetChaptersPagesBinding
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), ActionModeListener {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var settings: AppSettings
|
||||||
|
|
||||||
|
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): SheetChaptersPagesBinding {
|
||||||
|
return SheetChaptersPagesBinding.inflate(inflater, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewBindingCreated(binding: SheetChaptersPagesBinding, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewBindingCreated(binding, savedInstanceState)
|
||||||
|
val adapter = DetailsPagerAdapter2(this, settings)
|
||||||
|
binding.pager.recyclerView?.isNestedScrollingEnabled = false
|
||||||
|
binding.pager.offscreenPageLimit = 1
|
||||||
|
binding.pager.adapter = adapter
|
||||||
|
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
|
||||||
|
binding.pager.setCurrentItem(settings.defaultDetailsTab, false)
|
||||||
|
binding.tabs.isVisible = adapter.itemCount > 1
|
||||||
|
|
||||||
|
actionModeDelegate.addListener(this, viewLifecycleOwner)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActionModeStarted(mode: ActionMode) {
|
||||||
|
setExpanded(true, true)
|
||||||
|
viewBinding?.run {
|
||||||
|
pager.isUserInputEnabled = false
|
||||||
|
tabs.setTabsEnabled(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActionModeFinished(mode: ActionMode) {
|
||||||
|
setExpanded(isExpanded, false)
|
||||||
|
viewBinding?.run {
|
||||||
|
pager.isUserInputEnabled = true
|
||||||
|
tabs.setTabsEnabled(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package org.koitharu.kotatsu.details.ui.pager
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
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 DetailsPagerAdapter2(
|
||||||
|
fragment: Fragment,
|
||||||
|
settings: AppSettings,
|
||||||
|
) : FragmentStateAdapter(fragment),
|
||||||
|
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
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
|
||||||
|
<corners android:radius="4dp" />
|
||||||
|
|
||||||
|
<solid android:color="?colorSurface" />
|
||||||
|
|
||||||
|
</shape>
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12.87,15.07L10.33,12.56L10.36,12.53C12.1,10.59 13.34,8.36 14.07,6H17V4H10V2H8V4H1V6H12.17C11.5,7.92 10.44,9.75 9,11.35C8.07,10.32 7.3,9.19 6.69,8H4.69C5.42,9.63 6.42,11.17 7.67,12.56L2.58,17.58L4,19L9,14L12.11,17.11L12.87,15.07M18.5,10H16.5L12,22H14L15.12,19H19.87L21,22H23L18.5,10M15.88,17L17.5,12.67L19.12,17H15.88Z" />
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:pathData="M17 7V9H15V7H17M13 7V9H7V7H13M13 11H7V13H13V11M15 11V13H17V11H15M21 22L18 20L15 22L12 20L9 22L6 20L3 22V3H21V22M19 18.26V5H5V18.26L6 17.6L9 19.6L12 17.6L15 19.6L18 17.6L19 18.26Z" />
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,6A2,2 0 0,0 10,8A2,2 0 0,0 12,10A2,2 0 0,0 14,8A2,2 0 0,0 12,6M12,13C14.67,13 20,14.33 20,17V20H4V17C4,14.33 9.33,13 12,13M12,14.9C9.03,14.9 5.9,16.36 5.9,17V18.1H18.1V17C18.1,16.36 14.97,14.9 12,14.9Z" />
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,428 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
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/coordinator"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<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"
|
||||||
|
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" />
|
||||||
|
|
||||||
|
<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]" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_state"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:drawablePadding="4dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
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_cover"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/textView_subtitle"
|
||||||
|
app:layout_constraintWidth_default="wrap"
|
||||||
|
tools:drawableStart="@drawable/ic_state_ongoing"
|
||||||
|
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"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/button_chapters"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/info_layout"
|
||||||
|
app:progressColor="?colorSecondaryFixedDim"
|
||||||
|
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.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_title" />
|
||||||
|
|
||||||
|
<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/chips_tags"
|
||||||
|
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>
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.chip.ChipGroup
|
||||||
|
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/info_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingHorizontal="@dimen/screen_padding"
|
||||||
|
app:chipSpacingHorizontal="6dp"
|
||||||
|
app:chipSpacingVertical="6dp">
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/chip_favorite"
|
||||||
|
style="@style/Widget.Kotatsu.Chip.Dropdown"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:chipIcon="@drawable/ic_heart_outline"
|
||||||
|
app:ensureMinTouchTargetSize="false"
|
||||||
|
tools:text="@string/add_to_favourites" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/chip_source"
|
||||||
|
style="@style/Widget.Kotatsu.Chip.Assist"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:chipIcon="@drawable/ic_web"
|
||||||
|
app:ensureMinTouchTargetSize="false"
|
||||||
|
tools:text="Source"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/chip_author"
|
||||||
|
style="@style/Widget.Kotatsu.Chip.Assist"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:chipIcon="@drawable/ic_user"
|
||||||
|
app:ensureMinTouchTargetSize="false"
|
||||||
|
tools:text="Source"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/chip_size"
|
||||||
|
style="@style/Widget.Kotatsu.Chip.Assist"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:chipIcon="@drawable/ic_storage"
|
||||||
|
app:ensureMinTouchTargetSize="false"
|
||||||
|
tools:text="1.8 GiB"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/chip_branch"
|
||||||
|
style="@style/Widget.Kotatsu.Chip.Dropdown"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:chipIcon="@drawable/ic_language"
|
||||||
|
app:ensureMinTouchTargetSize="false"
|
||||||
|
tools:text="English"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</com.google.android.material.chip.ChipGroup>
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
<?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"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
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" />
|
||||||
|
|
||||||
|
<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>
|
||||||
Loading…
Reference in New Issue