UI improvements and author search support
This commit is contained in:
@@ -4,6 +4,7 @@ import android.text.style.ForegroundColorSpan
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.text.inSpans
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil3.ImageLoader
|
||||
import coil3.request.ImageRequest
|
||||
@@ -51,7 +52,13 @@ fun alternativeAD(
|
||||
binding.chipSource.setOnClickListener(clickListener)
|
||||
|
||||
bind { payloads ->
|
||||
binding.textViewTitle.text = item.manga.title
|
||||
binding.textViewTitle.text = item.mangaModel.title
|
||||
with(binding.iconsView) {
|
||||
clearIcons()
|
||||
if (item.mangaModel.isSaved) addIcon(R.drawable.ic_storage)
|
||||
if (item.mangaModel.isFavorite) addIcon(R.drawable.ic_heart_outline)
|
||||
isVisible = iconsCount > 0
|
||||
}
|
||||
binding.textViewSubtitle.text = buildSpannedString {
|
||||
if (item.chaptersCount > 0) {
|
||||
append(context.resources.getQuantityString(R.plurals.chapters, item.chaptersCount, item.chaptersCount))
|
||||
@@ -70,7 +77,7 @@ fun alternativeAD(
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.progressView.setProgress(item.progress, ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads)
|
||||
binding.progressView.setProgress(item.mangaModel.progress, ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads)
|
||||
binding.chipSource.also { chip ->
|
||||
chip.text = item.manga.source.getTitle(chip.context)
|
||||
ImageRequest.Builder(context)
|
||||
|
||||
@@ -15,17 +15,17 @@ import org.koitharu.kotatsu.core.model.chaptersCount
|
||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
||||
import org.koitharu.kotatsu.core.nav.AppRouter
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||
import org.koitharu.kotatsu.core.util.ext.call
|
||||
import org.koitharu.kotatsu.core.util.ext.require
|
||||
import org.koitharu.kotatsu.history.data.HistoryRepository
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.list.domain.MangaListMapper
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.list.ui.model.MangaGridModel
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
import javax.inject.Inject
|
||||
@@ -36,8 +36,7 @@ class AlternativesViewModel @Inject constructor(
|
||||
private val mangaRepositoryFactory: MangaRepository.Factory,
|
||||
private val alternativesUseCase: AlternativesUseCase,
|
||||
private val migrateUseCase: MigrateUseCase,
|
||||
private val historyRepository: HistoryRepository,
|
||||
private val settings: AppSettings,
|
||||
private val mangaListMapper: MangaListMapper,
|
||||
) : BaseViewModel() {
|
||||
|
||||
val manga = savedStateHandle.require<ParcelableManga>(AppRouter.KEY_MANGA).manga
|
||||
@@ -55,8 +54,7 @@ class AlternativesViewModel @Inject constructor(
|
||||
alternativesUseCase(ref)
|
||||
.map {
|
||||
MangaAlternativeModel(
|
||||
manga = it,
|
||||
progress = getProgress(it.id),
|
||||
mangaModel = mangaListMapper.toListModel(it, ListMode.GRID) as MangaGridModel,
|
||||
referenceChapters = refCount,
|
||||
)
|
||||
}.runningFold<MangaAlternativeModel, List<ListModel>>(listOf(LoadingState)) { acc, item ->
|
||||
@@ -88,8 +86,4 @@ class AlternativesViewModel @Inject constructor(
|
||||
onMigrated.call(target)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getProgress(mangaId: Long): ReadingProgress? {
|
||||
return historyRepository.getProgress(mangaId, settings.progressIndicatorMode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
package org.koitharu.kotatsu.alternatives.ui
|
||||
|
||||
import org.koitharu.kotatsu.core.model.chaptersCount
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.MangaGridModel
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
|
||||
data class MangaAlternativeModel(
|
||||
val manga: Manga,
|
||||
val progress: ReadingProgress?,
|
||||
val mangaModel: MangaGridModel,
|
||||
private val referenceChapters: Int,
|
||||
) : ListModel {
|
||||
|
||||
val manga: Manga
|
||||
get() = mangaModel.manga
|
||||
|
||||
val chaptersCount = manga.chaptersCount()
|
||||
|
||||
val chaptersDiff: Int
|
||||
@@ -19,4 +21,10 @@ data class MangaAlternativeModel(
|
||||
override fun areItemsTheSame(other: ListModel): Boolean {
|
||||
return other is MangaAlternativeModel && other.manga.id == manga.id
|
||||
}
|
||||
|
||||
override fun getChangePayload(previousState: ListModel): Any? = if (previousState is MangaAlternativeModel) {
|
||||
mangaModel.getChangePayload(previousState.mangaModel)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
package org.koitharu.kotatsu.core.ui.widgets
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
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.graphics.ColorUtils
|
||||
import androidx.core.view.children
|
||||
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.getThemeColorStateList
|
||||
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
|
||||
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
|
||||
|
||||
@Deprecated("")
|
||||
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 targetProgress = 0f
|
||||
private var colorBase: ColorStateList = ColorStateList.valueOf(Color.TRANSPARENT)
|
||||
private var colorProgress: ColorStateList = ColorStateList.valueOf(Color.TRANSPARENT)
|
||||
private var progressAnimator: ValueAnimator? = null
|
||||
|
||||
private var colorBaseCurrent = colorProgress.defaultColor
|
||||
private var colorProgressCurrent = colorProgress.defaultColor
|
||||
|
||||
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 = getColorStateList(R.styleable.ProgressButton_baseColor)
|
||||
?: context.getThemeColorStateList(materialR.attr.colorPrimaryContainer) ?: colorBase
|
||||
colorProgress = getColorStateList(R.styleable.ProgressButton_progressColor)
|
||||
?: context.getThemeColorStateList(materialR.attr.colorPrimary) ?: colorProgress
|
||||
getColorStateList(R.styleable.ProgressButton_android_textColor)?.let { colorText ->
|
||||
textViewTitle.setTextColor(colorText)
|
||||
textViewSubtitle.setTextColor(colorText)
|
||||
}
|
||||
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
|
||||
applyGravity()
|
||||
setWillNotDraw(false)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
canvas.drawColor(colorBaseCurrent)
|
||||
if (progress > 0f) {
|
||||
canvas.drawRect(0f, 0f, width * progress, height.toFloat(), paint)
|
||||
}
|
||||
}
|
||||
|
||||
override fun drawableStateChanged() {
|
||||
super.drawableStateChanged()
|
||||
val state = drawableState
|
||||
colorBaseCurrent = colorBase.getColorForState(state, colorBase.defaultColor)
|
||||
colorProgressCurrent = colorProgress.getColorForState(state, colorProgress.defaultColor)
|
||||
colorProgressCurrent = ColorUtils.setAlphaComponent(colorProgressCurrent, 84 /* 255 * 0.33F */)
|
||||
paint.color = colorProgressCurrent
|
||||
}
|
||||
|
||||
override fun setGravity(gravity: Int) {
|
||||
super.setGravity(gravity)
|
||||
if (childCount != 0) {
|
||||
applyGravity()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setEnabled(enabled: Boolean) {
|
||||
super.setEnabled(enabled)
|
||||
children.forEach { it.isEnabled = enabled }
|
||||
}
|
||||
|
||||
override fun onAnimationUpdate(animation: ValueAnimator) {
|
||||
if (animation === progressAnimator) {
|
||||
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) {
|
||||
val prevAnimator = progressAnimator
|
||||
if (animate && context.isAnimationsEnabled) {
|
||||
if (value == targetProgress) {
|
||||
return
|
||||
}
|
||||
targetProgress = value
|
||||
progressAnimator = ValueAnimator.ofFloat(progress, value).apply {
|
||||
duration = context.getAnimationDuration(android.R.integer.config_mediumAnimTime)
|
||||
interpolator = AccelerateDecelerateInterpolator()
|
||||
addUpdateListener(this@ProgressButton)
|
||||
}
|
||||
progressAnimator?.start()
|
||||
} else {
|
||||
progressAnimator = null
|
||||
progress = value
|
||||
targetProgress = value
|
||||
invalidate()
|
||||
}
|
||||
prevAnimator?.cancel()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,7 @@ 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.list.domain.MangaListMapper
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
||||
import org.koitharu.kotatsu.list.ui.adapter.mangaGridItemAD
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
@@ -101,6 +102,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.util.ifNullOrEmpty
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo
|
||||
import org.koitharu.kotatsu.search.domain.SearchKind
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.roundToInt
|
||||
import com.google.android.material.R as materialR
|
||||
@@ -203,8 +205,8 @@ class DetailsActivity :
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.textView_author -> {
|
||||
val manga = viewModel.manga.value ?: return
|
||||
router.openSearch(manga.source, manga.author ?: return)
|
||||
val author = viewModel.manga.value?.author ?: return
|
||||
router.openSearch(author, SearchKind.AUTHOR)
|
||||
}
|
||||
|
||||
R.id.textView_source -> {
|
||||
@@ -484,7 +486,7 @@ class DetailsActivity :
|
||||
textViewProgress.textAndVisible = if (info.percent <= 0f) {
|
||||
null
|
||||
} else {
|
||||
val displayPercent = if (info.percent >= 0.999999f) 100 else (info.percent * 100f).toInt()
|
||||
val displayPercent = if (ReadingProgress.isCompleted(info.percent)) 100 else (info.percent * 100f).toInt()
|
||||
getString(R.string.percent_string_pattern, displayPercent.toString())
|
||||
}
|
||||
|
||||
|
||||
@@ -287,6 +287,15 @@ class FilterCoordinator @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun setAuthor(value: String?) {
|
||||
currentListFilter.update { oldValue ->
|
||||
oldValue.copy(
|
||||
author = value,
|
||||
query = oldValue.takeQueryIfSupported(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setOriginalLocale(value: Locale?) {
|
||||
currentListFilter.update { oldValue ->
|
||||
oldValue.copy(
|
||||
|
||||
@@ -60,7 +60,11 @@ class FilterHeaderFragment : BaseFragment<FragmentFilterHeaderBinding>(), ChipsV
|
||||
|
||||
override fun onChipCloseClick(chip: Chip, data: Any?) {
|
||||
when (data) {
|
||||
is String -> filter.setQuery(null)
|
||||
is String -> if (data == filter.snapshot().listFilter.author) {
|
||||
filter.setAuthor(null)
|
||||
} else {
|
||||
filter.setQuery(null)
|
||||
}
|
||||
is ContentRating -> filter.toggleContentRating(data, false)
|
||||
is Demographic -> filter.toggleDemographic(data, false)
|
||||
is ContentType -> filter.toggleContentType(data, false)
|
||||
|
||||
@@ -135,6 +135,16 @@ class FilterHeaderProducer @Inject constructor(
|
||||
),
|
||||
)
|
||||
}
|
||||
if (!snapshot.author.isNullOrEmpty()) {
|
||||
result.addFirst(
|
||||
ChipsView.ChipModel(
|
||||
title = snapshot.author,
|
||||
icon = R.drawable.ic_user,
|
||||
isCloseable = true,
|
||||
data = snapshot.author,
|
||||
),
|
||||
)
|
||||
}
|
||||
val hasTags = result.any { it.data is MangaTag }
|
||||
if (hasTags) {
|
||||
result.addLast(moreTagsChip())
|
||||
|
||||
@@ -154,7 +154,7 @@ class HistoryRepository @Inject constructor(
|
||||
|
||||
suspend fun getProgress(mangaId: Long, mode: ProgressIndicatorMode): ReadingProgress? {
|
||||
val entity = db.getHistoryDao().find(mangaId) ?: return null
|
||||
val fixedPercent = if (entity.percent >= 0.999999f) 1f else entity.percent
|
||||
val fixedPercent = if (ReadingProgress.isCompleted(entity.percent)) 1f else entity.percent
|
||||
return ReadingProgress(
|
||||
percent = fixedPercent,
|
||||
totalChapters = entity.chaptersCount,
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.list.domain.ListSortOrder
|
||||
import org.koitharu.kotatsu.list.domain.MangaListMapper
|
||||
import org.koitharu.kotatsu.list.domain.QuickFilterListener
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.InfoModel
|
||||
@@ -207,10 +208,10 @@ class HistoryListViewModel @Inject constructor(
|
||||
|
||||
ListSortOrder.UNREAD,
|
||||
ListSortOrder.PROGRESS -> ListHeader(
|
||||
when (percent) {
|
||||
1f -> R.string.status_completed
|
||||
in 0f..0.01f -> R.string.status_planned
|
||||
in 0f..1f -> R.string.status_reading
|
||||
when {
|
||||
ReadingProgress.isCompleted(percent) -> R.string.status_completed
|
||||
percent in 0f..0.01f -> R.string.status_planned
|
||||
percent in 0f..1f -> R.string.status_reading
|
||||
else -> R.string.unknown
|
||||
},
|
||||
)
|
||||
|
||||
@@ -39,9 +39,10 @@ data class ReadingProgress(
|
||||
|
||||
const val PROGRESS_NONE = -1f
|
||||
const val PROGRESS_COMPLETED = 1f
|
||||
private const val PROGRESS_COMPLETED_THRESHOLD = 0.99999f
|
||||
|
||||
fun isValid(percent: Float) = percent in 0f..1f
|
||||
|
||||
fun isCompleted(percent: Float) = percent >= PROGRESS_COMPLETED
|
||||
fun isCompleted(percent: Float) = percent >= PROGRESS_COMPLETED_THRESHOLD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.koitharu.kotatsu.list.ui.adapter
|
||||
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemButtonFooterBinding
|
||||
import org.koitharu.kotatsu.list.ui.model.ButtonFooter
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
|
||||
fun buttonFooterAD(
|
||||
listener: ListStateHolderListener,
|
||||
) = adapterDelegateViewBinding<ButtonFooter, ListModel, ItemButtonFooterBinding>(
|
||||
{ inflater, parent -> ItemButtonFooterBinding.inflate(inflater, parent, false) },
|
||||
) {
|
||||
|
||||
binding.button.setOnClickListener {
|
||||
listener.onFooterButtonClick()
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.button.setText(item.textResId)
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import org.koitharu.kotatsu.list.ui.model.ErrorFooter
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
|
||||
fun errorFooterAD(
|
||||
listener: MangaListListener?,
|
||||
listener: ListStateHolderListener?,
|
||||
) = adapterDelegateViewBinding<ErrorFooter, ListModel, ItemErrorFooterBinding>(
|
||||
{ inflater, parent -> ItemErrorFooterBinding.inflate(inflater, parent, false) },
|
||||
) {
|
||||
|
||||
@@ -15,6 +15,7 @@ enum class ListItemType {
|
||||
MANGA_NESTED_GROUP,
|
||||
FOOTER_LOADING,
|
||||
FOOTER_ERROR,
|
||||
FOOTER_BUTTON,
|
||||
STATE_LOADING,
|
||||
STATE_ERROR,
|
||||
STATE_EMPTY,
|
||||
|
||||
@@ -7,4 +7,6 @@ interface ListStateHolderListener {
|
||||
fun onSecondaryErrorActionClick(error: Throwable) = Unit
|
||||
|
||||
fun onEmptyActionClick()
|
||||
|
||||
fun onFooterButtonClick() = Unit
|
||||
}
|
||||
|
||||
@@ -27,5 +27,6 @@ open class MangaListAdapter(
|
||||
addDelegate(ListItemType.QUICK_FILTER, quickFilterAD(listener))
|
||||
addDelegate(ListItemType.TIP, tipAD(listener))
|
||||
addDelegate(ListItemType.INFO, infoAD())
|
||||
addDelegate(ListItemType.FOOTER_BUTTON, buttonFooterAD(listener))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ class TypedListSpacingDecoration(
|
||||
|
||||
ListItemType.FOOTER_LOADING,
|
||||
ListItemType.FOOTER_ERROR,
|
||||
ListItemType.FOOTER_BUTTON,
|
||||
ListItemType.STATE_LOADING,
|
||||
ListItemType.STATE_ERROR,
|
||||
ListItemType.STATE_EMPTY,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.koitharu.kotatsu.list.ui.model
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
|
||||
data class ButtonFooter(
|
||||
@StringRes val textResId: Int,
|
||||
) : ListModel {
|
||||
|
||||
override fun areItemsTheSame(other: ListModel): Boolean {
|
||||
return other is ButtonFooter && textResId == other.textResId
|
||||
}
|
||||
}
|
||||
@@ -121,20 +121,14 @@ class PreviewFragment : BaseFragment<FragmentPreviewBinding>(), View.OnClickList
|
||||
private fun onFooterUpdated(footer: PreviewViewModel.FooterInfo?) {
|
||||
with(requireViewBinding()) {
|
||||
buttonRead.isEnabled = footer != null
|
||||
buttonRead.setTitle(if (footer?.isInProgress() == true) R.string._continue else R.string.read)
|
||||
buttonRead.subtitle = when {
|
||||
footer == null -> getString(R.string.loading_)
|
||||
footer.isIncognito -> getString(R.string.incognito_mode)
|
||||
footer.currentChapter >= 0 -> getString(
|
||||
R.string.chapter_d_of_d,
|
||||
footer.currentChapter + 1,
|
||||
footer.totalChapters,
|
||||
)
|
||||
|
||||
footer.totalChapters == 0 -> getString(R.string.no_chapters)
|
||||
else -> resources.getQuantityString(R.plurals.chapters, footer.totalChapters, footer.totalChapters)
|
||||
}
|
||||
buttonRead.setProgress(footer?.percent?.coerceIn(0f, 1f) ?: 0f, true)
|
||||
buttonRead.setText(
|
||||
when {
|
||||
footer == null -> R.string.loading_
|
||||
footer.isIncognito == true -> R.string.incognito
|
||||
footer.isInProgress() == true -> R.string._continue
|
||||
else -> R.string.read
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.MenuHost
|
||||
import androidx.core.view.OnApplyWindowInsetsListener
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isGone
|
||||
@@ -98,6 +99,9 @@ class ReaderActivity :
|
||||
scrollTimer.isEnabled = value
|
||||
}
|
||||
|
||||
private val secondaryMenuHost: MenuHost
|
||||
get() = viewBinding.toolbarBottom ?: this
|
||||
|
||||
private lateinit var scrollTimer: ScrollTimer
|
||||
private lateinit var pageSaveHelper: PageSaveHelper
|
||||
private lateinit var touchHelper: TapGridDispatcher
|
||||
@@ -150,7 +154,7 @@ class ReaderActivity :
|
||||
viewModel.isInfoBarTransparent.observe(this) { viewBinding.infoBar.drawBackground = !it }
|
||||
viewModel.isInfoBarEnabled.observe(this, ::onReaderBarChanged)
|
||||
viewModel.isBookmarkAdded.observe(this, MenuInvalidator(this))
|
||||
val bottomMenuInvalidator = MenuInvalidator(viewBinding.toolbarBottom)
|
||||
val bottomMenuInvalidator = MenuInvalidator(secondaryMenuHost)
|
||||
viewModel.isPagesSheetEnabled.observe(this, bottomMenuInvalidator)
|
||||
screenOrientationHelper.observeAutoOrientation().observe(this, bottomMenuInvalidator)
|
||||
viewModel.onShowToast.observeEvent(this) { msgId ->
|
||||
@@ -165,7 +169,7 @@ class ReaderActivity :
|
||||
viewBinding.zoomControl.isVisible = it
|
||||
}
|
||||
addMenuProvider(ReaderMenuTopProvider(viewModel))
|
||||
viewBinding.toolbarBottom.addMenuProvider(
|
||||
secondaryMenuHost.addMenuProvider(
|
||||
ReaderMenuBottomProvider(this, readerManager, screenOrientationHelper, this, viewModel),
|
||||
)
|
||||
}
|
||||
@@ -221,7 +225,7 @@ class ReaderActivity :
|
||||
} else {
|
||||
viewBinding.toastView.hide()
|
||||
}
|
||||
viewBinding.toolbarBottom.invalidateMenu()
|
||||
secondaryMenuHost.invalidateMenu()
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
@@ -242,7 +246,7 @@ class ReaderActivity :
|
||||
rawX >= viewBinding.root.width - gestureInsets.right ||
|
||||
rawY >= viewBinding.root.height - gestureInsets.bottom ||
|
||||
viewBinding.appbarTop.hasGlobalPoint(rawX, rawY) ||
|
||||
viewBinding.appbarBottom.hasGlobalPoint(rawX, rawY) == true
|
||||
viewBinding.appbarBottom?.hasGlobalPoint(rawX, rawY) == true
|
||||
) {
|
||||
false
|
||||
} else {
|
||||
@@ -306,7 +310,7 @@ class ReaderActivity :
|
||||
buttonPrev.isVisible = ReaderControl.PREV_CHAPTER in controls
|
||||
buttonNext.isVisible = ReaderControl.NEXT_CHAPTER in controls
|
||||
slider.isVisible = ReaderControl.SLIDER in controls
|
||||
toolbarBottom.invalidateMenu()
|
||||
secondaryMenuHost.invalidateMenu()
|
||||
}
|
||||
|
||||
private fun setUiIsVisible(isUiVisible: Boolean) {
|
||||
@@ -321,7 +325,7 @@ class ReaderActivity :
|
||||
}
|
||||
val isFullscreen = settings.isReaderFullscreenEnabled
|
||||
viewBinding.appbarTop.isVisible = isUiVisible
|
||||
viewBinding.appbarBottom.isVisible = isUiVisible
|
||||
viewBinding.appbarBottom?.isVisible = isUiVisible
|
||||
viewBinding.infoBar.isGone = isUiVisible || (!viewModel.isInfoBarEnabled.value)
|
||||
viewBinding.infoBar.isTimeVisible = isFullscreen
|
||||
systemUiController.setSystemUiVisible(isUiVisible || !isFullscreen)
|
||||
@@ -336,7 +340,7 @@ class ReaderActivity :
|
||||
right = systemBars.right,
|
||||
left = systemBars.left,
|
||||
)
|
||||
viewBinding.appbarBottom.updateLayoutParams<MarginLayoutParams> {
|
||||
viewBinding.appbarBottom?.updateLayoutParams<MarginLayoutParams> {
|
||||
bottomMargin = systemBars.bottom + topMargin
|
||||
rightMargin = systemBars.right + topMargin
|
||||
leftMargin = systemBars.left + topMargin
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.koitharu.kotatsu.databinding.FragmentListBinding
|
||||
import org.koitharu.kotatsu.filter.ui.FilterCoordinator
|
||||
import org.koitharu.kotatsu.list.ui.MangaListFragment
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.search.domain.SearchKind
|
||||
|
||||
@AndroidEntryPoint
|
||||
class RemoteListFragment : MangaListFragment(), FilterCoordinator.Owner {
|
||||
@@ -72,6 +73,15 @@ class RemoteListFragment : MangaListFragment(), FilterCoordinator.Owner {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFooterButtonClick() {
|
||||
val filter = filterCoordinator.snapshot().listFilter
|
||||
when {
|
||||
!filter.query.isNullOrEmpty() -> router.openSearch(filter.query.orEmpty(), SearchKind.SIMPLE)
|
||||
!filter.author.isNullOrEmpty() -> router.openSearch(filter.author.orEmpty(), SearchKind.AUTHOR)
|
||||
filter.tags.size == 1 -> router.openSearch(filter.tags.singleOrNull()?.title.orEmpty(), SearchKind.TAG)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSecondaryErrorActionClick(error: Throwable) {
|
||||
openInBrowser(error.getCauseUrl())
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.koitharu.kotatsu.explore.domain.ExploreRepository
|
||||
import org.koitharu.kotatsu.filter.ui.FilterCoordinator
|
||||
import org.koitharu.kotatsu.list.domain.MangaListMapper
|
||||
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
||||
import org.koitharu.kotatsu.list.ui.model.ButtonFooter
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
|
||||
@@ -90,6 +91,7 @@ open class RemoteListViewModel @Inject constructor(
|
||||
when {
|
||||
error != null -> add(error.toErrorFooter())
|
||||
hasNext -> add(LoadingFooter())
|
||||
else -> getFooter()?.let(::add)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,6 +180,18 @@ open class RemoteListViewModel @Inject constructor(
|
||||
mode: ListMode
|
||||
) = mangaListMapper.toListModelList(destination, manga, mode)
|
||||
|
||||
protected open fun getFooter(): ButtonFooter? {
|
||||
val filter = filterCoordinator.snapshot().listFilter
|
||||
val hasQuery = !filter.query.isNullOrEmpty()
|
||||
val hasAuthor = !filter.author.isNullOrEmpty()
|
||||
val isOneTag = filter.tags.size == 1
|
||||
return if ((hasQuery xor isOneTag xor hasAuthor) && !(hasQuery && isOneTag && hasAuthor)) {
|
||||
ButtonFooter(R.string.global_search)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun openRandom() {
|
||||
if (randomJob?.isActive == true) {
|
||||
return
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.core.util.ext.require
|
||||
import org.koitharu.kotatsu.core.util.ext.requireValue
|
||||
import org.koitharu.kotatsu.history.data.HistoryRepository
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
@@ -159,7 +160,7 @@ class ScrobblingSelectorViewModel @Inject constructor(
|
||||
rating = prevInfo?.rating ?: 0f,
|
||||
status = prevInfo?.status ?: when {
|
||||
history == null -> ScrobblingStatus.PLANNED
|
||||
history.percent == 1f -> ScrobblingStatus.COMPLETED
|
||||
ReadingProgress.isCompleted(history.percent) -> ScrobblingStatus.COMPLETED
|
||||
else -> ScrobblingStatus.READING
|
||||
},
|
||||
comment = prevInfo?.comment,
|
||||
|
||||
@@ -38,8 +38,15 @@ class SearchV2Helper @AssistedInject constructor(
|
||||
|
||||
private suspend fun MangaRepository.getFilter(query: String, kind: SearchKind): MangaListFilter? = when (kind) {
|
||||
SearchKind.SIMPLE,
|
||||
SearchKind.TITLE,
|
||||
SearchKind.AUTHOR -> if (filterCapabilities.isSearchSupported) { // TODO author support
|
||||
SearchKind.TITLE -> if (filterCapabilities.isSearchSupported) {
|
||||
MangaListFilter(query = query)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
SearchKind.AUTHOR -> if (filterCapabilities.isAuthorSearchSupported) {
|
||||
MangaListFilter(author = query)
|
||||
} else if (filterCapabilities.isSearchSupported) {
|
||||
MangaListFilter(query = query)
|
||||
} else {
|
||||
null
|
||||
|
||||
@@ -15,7 +15,6 @@ data class SearchResultsListModel(
|
||||
val source: MangaSource,
|
||||
val listFilter: MangaListFilter?,
|
||||
val sortOrder: SortOrder?,
|
||||
val hasMore: Boolean,
|
||||
val list: List<MangaListModel>,
|
||||
val error: Throwable?,
|
||||
) : ListModel {
|
||||
|
||||
@@ -44,7 +44,6 @@ import org.koitharu.kotatsu.search.domain.SearchV2Helper
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val MAX_PARALLELISM = 4
|
||||
private const val MIN_HAS_MORE_ITEMS = 8
|
||||
|
||||
@HiltViewModel
|
||||
class SearchViewModel @Inject constructor(
|
||||
@@ -132,7 +131,6 @@ class SearchViewModel @Inject constructor(
|
||||
SearchResultsListModel(
|
||||
titleResId = 0,
|
||||
source = source,
|
||||
hasMore = list.size > MIN_HAS_MORE_ITEMS,
|
||||
list = list,
|
||||
error = null,
|
||||
listFilter = result.listFilter,
|
||||
@@ -142,7 +140,7 @@ class SearchViewModel @Inject constructor(
|
||||
},
|
||||
onFailure = { error ->
|
||||
error.printStackTraceDebug()
|
||||
SearchResultsListModel(0, source, null, null, true, emptyList(), error)
|
||||
SearchResultsListModel(0, source, null, null, emptyList(), error)
|
||||
},
|
||||
)
|
||||
if (item != null) {
|
||||
@@ -163,7 +161,6 @@ class SearchViewModel @Inject constructor(
|
||||
SearchResultsListModel(
|
||||
titleResId = R.string.history,
|
||||
source = UnknownMangaSource,
|
||||
hasMore = false,
|
||||
list = mangaListMapper.toListModelList(manga = result, mode = ListMode.GRID),
|
||||
error = null,
|
||||
listFilter = null,
|
||||
@@ -177,7 +174,6 @@ class SearchViewModel @Inject constructor(
|
||||
SearchResultsListModel(
|
||||
titleResId = R.string.history,
|
||||
source = UnknownMangaSource,
|
||||
hasMore = false,
|
||||
list = emptyList(),
|
||||
error = error,
|
||||
listFilter = null,
|
||||
@@ -196,7 +192,6 @@ class SearchViewModel @Inject constructor(
|
||||
SearchResultsListModel(
|
||||
titleResId = R.string.favourites,
|
||||
source = UnknownMangaSource,
|
||||
hasMore = false,
|
||||
list = mangaListMapper.toListModelList(
|
||||
manga = result,
|
||||
mode = ListMode.GRID,
|
||||
@@ -214,7 +209,6 @@ class SearchViewModel @Inject constructor(
|
||||
SearchResultsListModel(
|
||||
titleResId = R.string.favourites,
|
||||
source = UnknownMangaSource,
|
||||
hasMore = false,
|
||||
list = emptyList(),
|
||||
error = error,
|
||||
listFilter = null,
|
||||
@@ -233,7 +227,6 @@ class SearchViewModel @Inject constructor(
|
||||
SearchResultsListModel(
|
||||
titleResId = 0,
|
||||
source = LocalMangaSource,
|
||||
hasMore = result.manga.size > MIN_HAS_MORE_ITEMS,
|
||||
list = mangaListMapper.toListModelList(
|
||||
manga = result.manga,
|
||||
mode = ListMode.GRID,
|
||||
@@ -251,7 +244,6 @@ class SearchViewModel @Inject constructor(
|
||||
SearchResultsListModel(
|
||||
titleResId = 0,
|
||||
source = LocalMangaSource,
|
||||
hasMore = true,
|
||||
list = emptyList(),
|
||||
error = error,
|
||||
listFilter = null,
|
||||
|
||||
139
app/src/main/res/layout-w600dp-land/activity_reader.xml
Normal file
139
app/src/main/res/layout-w600dp-land/activity_reader.xml
Normal file
@@ -0,0 +1,139 @@
|
||||
<?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:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:background="@color/grey" />
|
||||
|
||||
<org.koitharu.kotatsu.core.ui.widgets.ZoomControl
|
||||
android:id="@+id/zoomControl"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:orientation="vertical"
|
||||
android:spacing="2dp"
|
||||
android:visibility="gone"
|
||||
app:layout_dodgeInsetEdges="bottom"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<org.koitharu.kotatsu.reader.ui.ReaderInfoBarView
|
||||
android:id="@+id/infoBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:padding="6dp"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar_top"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="@dimen/m3_card_elevated_elevation"
|
||||
app:elevation="@dimen/m3_card_elevated_elevation"
|
||||
app:liftOnScroll="false">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="@dimen/m3_card_elevated_elevation"
|
||||
app:elevation="@dimen/m3_card_elevated_elevation"
|
||||
app:popupTheme="@style/ThemeOverlay.Kotatsu"
|
||||
tools:menu="@menu/opt_reader_top">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_slider"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:gravity="center_vertical|end">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_prev"
|
||||
style="?actionButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/prev_chapter"
|
||||
android:src="@drawable/ic_prev"
|
||||
android:tooltipText="@string/prev_chapter" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/slider"
|
||||
android:layout_width="260dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:stepSize="1.0"
|
||||
android:valueFrom="0"
|
||||
app:labelBehavior="floating"
|
||||
tools:value="6"
|
||||
tools:valueTo="20" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_next"
|
||||
style="?actionButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/next_chapter"
|
||||
android:src="@drawable/ic_next"
|
||||
android:tooltipText="@string/next_chapter" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<org.koitharu.kotatsu.reader.ui.ReaderToastView
|
||||
android:id="@+id/toastView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:background="@drawable/bg_reader_indicator"
|
||||
android:drawablePadding="6dp"
|
||||
android:elevation="1000dp"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
android:theme="@style/ThemeOverlay.Material3.Dark"
|
||||
app:layout_dodgeInsetEdges="bottom"
|
||||
tools:text="@string/loading_" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/bg_card"
|
||||
android:backgroundTint="?colorSurfaceContainer"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:outlineProvider="background"
|
||||
android:padding="@dimen/screen_padding">
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/loading_"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -6,7 +6,7 @@
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:background="@macro/m3_comp_filled_card_container_color">
|
||||
tools:background="?colorBackgroundFloating">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -122,50 +122,38 @@
|
||||
app:barrierMargin="8dp"
|
||||
app:constraint_referenced_ids="imageView_cover,rating_bar" />
|
||||
|
||||
<org.koitharu.kotatsu.core.ui.widgets.ProgressButton
|
||||
<Button
|
||||
android:id="@+id/button_read"
|
||||
style="?materialButtonStyle"
|
||||
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:baseColor="@color/m3_chip_background_color"
|
||||
app:layout_constraintEnd_toStartOf="@id/button_open"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/barrier_header"
|
||||
app:progressColor="?colorControlNormal"
|
||||
app:subtitleTextAppearance="?textAppearanceBodySmall"
|
||||
app:titleTextAppearance="?textAppearanceButton"
|
||||
tools:max="100"
|
||||
tools:progress="40"
|
||||
tools:subtitle="12 chapters"
|
||||
tools:title="@string/read" />
|
||||
tools:text="@string/read" />
|
||||
|
||||
<ImageView
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_open"
|
||||
style="?materialIconButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/bg_circle_button"
|
||||
android:backgroundTint="@color/m3_chip_background_color"
|
||||
android:contentDescription="@string/details"
|
||||
android:scaleType="centerInside"
|
||||
app:icon="@drawable/ic_expand"
|
||||
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_expand" />
|
||||
app:layout_constraintTop_toTopOf="@id/button_read" />
|
||||
|
||||
<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_normal"
|
||||
android:layout_marginHorizontal="@dimen/screen_padding"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
app:chipSpacingHorizontal="6dp"
|
||||
app:chipSpacingVertical="6dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
17
app/src/main/res/layout/item_button_footer.xml
Normal file
17
app/src/main/res/layout/item_button_footer.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="4dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
style="?materialButtonOutlinedStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@string/globally" />
|
||||
|
||||
</FrameLayout>
|
||||
@@ -23,6 +23,19 @@
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
|
||||
tools:src="@tools:sample/backgrounds/scenic" />
|
||||
|
||||
<org.koitharu.kotatsu.core.ui.widgets.IconsView
|
||||
android:id="@+id/iconsView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/bg_list_icons"
|
||||
android:orientation="horizontal"
|
||||
android:padding="4dp"
|
||||
app:iconSize="14dp"
|
||||
app:iconSpacing="4dp"
|
||||
app:layout_constraintStart_toStartOf="@id/imageView_cover"
|
||||
app:layout_constraintTop_toTopOf="@id/imageView_cover" />
|
||||
|
||||
<org.koitharu.kotatsu.history.ui.util.ReadingProgressView
|
||||
android:id="@+id/progressView"
|
||||
android:layout_width="@dimen/card_indicator_size"
|
||||
|
||||
@@ -803,4 +803,5 @@
|
||||
<string name="badges_in_lists">Badges in lists</string>
|
||||
<string name="search_everywhere">Search everywhere</string>
|
||||
<string name="simple">Simple</string>
|
||||
<string name="global_search">Global search</string>
|
||||
</resources>
|
||||
|
||||
@@ -31,7 +31,7 @@ material = "1.13.0-alpha10"
|
||||
moshi = "1.15.2"
|
||||
okhttp = "4.12.0"
|
||||
okio = "3.10.2"
|
||||
parsers = "198e859850"
|
||||
parsers = "88ea5215c0"
|
||||
preference = "1.2.1"
|
||||
recyclerview = "1.4.0"
|
||||
room = "2.6.1"
|
||||
|
||||
Reference in New Issue
Block a user