diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/MultilineEllipsizeTextView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/MultilineEllipsizeTextView.kt new file mode 100644 index 000000000..51076c5ab --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/MultilineEllipsizeTextView.kt @@ -0,0 +1,19 @@ +package org.koitharu.kotatsu.core.ui.widgets + +import android.content.Context +import android.util.AttributeSet +import androidx.annotation.AttrRes +import com.google.android.material.textview.MaterialTextView + +class MultilineEllipsizeTextView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + @AttrRes defStyleAttr: Int = android.R.attr.textViewStyle, +) : MaterialTextView(context, attrs, defStyleAttr) { + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + val lh = lineHeight + maxLines = if (lh > 0) h / lh else 1 + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/ProgressButton.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/ProgressButton.kt index c704df313..24344d5ea 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/ProgressButton.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/ProgressButton.kt @@ -16,6 +16,7 @@ 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 @@ -37,10 +38,14 @@ class ProgressButton @JvmOverloads constructor( 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) { @@ -97,10 +102,19 @@ class ProgressButton @JvmOverloads constructor( override fun onDraw(canvas: Canvas) { super.onDraw(canvas) - canvas.drawColor(colorBase.getColorForState(drawableState, colorBase.defaultColor)) - paint.color = colorProgress.getColorForState(drawableState, colorProgress.defaultColor) - paint.alpha = 84 // 255 * 0.33F - canvas.drawRect(0f, 0f, width * progress, height.toFloat(), paint) + 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) { @@ -116,8 +130,10 @@ class ProgressButton @JvmOverloads constructor( } override fun onAnimationUpdate(animation: ValueAnimator) { - progress = animation.animatedValue as Float - invalidate() + if (animation === progressAnimator) { + progress = animation.animatedValue as Float + invalidate() + } } fun setTitle(@StringRes titleResId: Int) { @@ -129,19 +145,25 @@ class ProgressButton @JvmOverloads constructor( } fun setProgress(value: Float, animate: Boolean) { - progressAnimator?.cancel() + val prevAnimator = progressAnimator if (animate) { + if (value == targetProgress) { + return + } + targetProgress = value progressAnimator = ValueAnimator.ofFloat(progress, value).apply { - duration = context.getAnimationDuration(android.R.integer.config_shortAnimTime) + duration = context.getAnimationDuration(android.R.integer.config_mediumAnimTime) interpolator = AccelerateDecelerateInterpolator() addUpdateListener(this@ProgressButton) - start() } + progressAnimator?.start() } else { progressAnimator = null progress = value + targetProgress = value invalidate() } + prevAnimator?.cancel() } private fun applyGravity() { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/FlowObserver.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/FlowObserver.kt index 1f4aa2499..1e854bc4c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/FlowObserver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/FlowObserver.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.core.util.ext -import android.util.Log import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope @@ -28,11 +27,16 @@ fun Flow.observe(owner: LifecycleOwner, minState: Lifecycle.State, collec } fun Flow?>.observeEvent(owner: LifecycleOwner, collector: FlowCollector) { + observeEvent(owner, Lifecycle.State.STARTED, collector) +} + +fun Flow?>.observeEvent(owner: LifecycleOwner, minState: Lifecycle.State, collector: FlowCollector) { owner.lifecycleScope.launch { - owner.repeatOnLifecycle(Lifecycle.State.STARTED) { + owner.repeatOnLifecycle(minState) { collect { it?.consume(collector) } } } } + diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMapper.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMapper.kt index 28fad310e..ca5d93ed0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMapper.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMapper.kt @@ -72,8 +72,7 @@ fun MangaDetails.mapChapters( fun List.withVolumeHeaders(context: Context): List { var prevVolume = 0 val result = ArrayList((size * 1.4).toInt()) - var groupPos: Byte = 0 - for ((index, item) in this.withIndex()) { + for (item in this) { val chapter = item.chapter if (chapter.volume != prevVolume) { val text = if (chapter.volume == 0) { @@ -83,19 +82,8 @@ fun List.withVolumeHeaders(context: Context): List { } result.add(ListHeader(text)) prevVolume = chapter.volume - groupPos = ChapterListItem.GROUP_START - } else if (groupPos == ChapterListItem.GROUP_START) { - groupPos = ChapterListItem.GROUP_MIDDLE - } - if (groupPos != 0.toByte()) { - val next = this.getOrNull(index + 1) - if (next == null || next.chapter.volume != prevVolume) { - groupPos = ChapterListItem.GROUP_END - } - result.add(item.copy(groupPosition = groupPos)) - } else { - result.add(item) } + result.add(item) } return result } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt index 03a782490..0ef6f99b8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt @@ -54,12 +54,10 @@ 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.util.BottomSheetClollapseCallback -import org.koitharu.kotatsu.core.ui.util.BottomSheetNoHalfExpandedCallback 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.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.enqueueWith @@ -534,7 +532,9 @@ class DetailsActivity : info.totalChapters == -1 -> getString(R.string.error_occurred) else -> resources.getQuantityString(R.plurals.chapters, info.totalChapters, info.totalChapters) } - buttonRead.setProgress(info.history?.percent?.coerceIn(0f, 1f) ?: 0f, true) + val isFirstCall = buttonRead.tag == null + buttonRead.tag = Unit + buttonRead.setProgress(info.history?.percent?.coerceIn(0f, 1f) ?: 0f, !isFirstCall) buttonDownload?.isEnabled = info.isValid && info.canDownload buttonRead.isEnabled = info.isValid } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/adapter/ChapterListItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/adapter/ChapterListItemAD.kt index f1f08e9e9..5be9aff8c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/adapter/ChapterListItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/adapter/ChapterListItemAD.kt @@ -26,18 +26,9 @@ fun chapterListItemAD( itemView.setOnClickListener(eventListener) itemView.setOnLongClickListener(eventListener) - bind { payloads -> + bind { binding.textViewTitle.text = item.chapter.name binding.textViewDescription.textAndVisible = item.description - itemView.setBackgroundResource( - when { - item.isGroupStart && item.isGroupEnd -> R.drawable.bg_card_full - item.isGroupStart -> R.drawable.bg_card_top - item.isGroupMiddle -> R.drawable.bg_card_none - item.isGroupEnd -> R.drawable.bg_card_bottom - else -> R.drawable.list_selector - }, - ) when { item.isCurrent -> { binding.textViewTitle.drawableStart = ContextCompat.getDrawable(context, R.drawable.ic_current_chapter) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ChapterListItem.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ChapterListItem.kt index 434dbf199..ec3a1c547 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ChapterListItem.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ChapterListItem.kt @@ -10,8 +10,6 @@ import kotlin.experimental.and data class ChapterListItem( val chapter: MangaChapter, val flags: Byte, - private val uploadDateMs: Long, - private val groupPosition: Byte, ) : ListModel { var description: String? = null @@ -26,9 +24,9 @@ data class ChapterListItem( private set get() { if (field != null) return field - if (uploadDateMs == 0L) return null + if (chapter.uploadDate == 0L) return null field = DateUtils.getRelativeTimeSpanString( - uploadDateMs, + chapter.uploadDate, System.currentTimeMillis(), DateUtils.DAY_IN_MILLIS, ) @@ -53,15 +51,6 @@ data class ChapterListItem( val isGrid: Boolean get() = hasFlag(FLAG_GRID) - val isGroupStart: Boolean - get() = (groupPosition and GROUP_START) == GROUP_START - - val isGroupMiddle: Boolean - get() = (groupPosition and GROUP_MIDDLE) == GROUP_MIDDLE - - val isGroupEnd: Boolean - get() = (groupPosition and GROUP_END) == GROUP_END - private fun buildDescription(): String { val joiner = StringJoiner(" • ") chapter.formatNumber()?.let { @@ -105,9 +94,5 @@ data class ChapterListItem( const val FLAG_BOOKMARKED: Byte = 16 const val FLAG_DOWNLOADED: Byte = 32 const val FLAG_GRID: Byte = 64 - - const val GROUP_START: Byte = 2 - const val GROUP_MIDDLE: Byte = 4 - const val GROUP_END: Byte = 8 } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ListModelConversionExt.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ListModelConversionExt.kt index 5d1aff72d..449ca8947 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ListModelConversionExt.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ListModelConversionExt.kt @@ -27,7 +27,5 @@ fun MangaChapter.toListItem( return ChapterListItem( chapter = this, flags = flags, - uploadDateMs = uploadDate, - groupPosition = 0, ) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/ChaptersPagesSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/ChaptersPagesSheet.kt index c360aadac..f7ed9000f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/ChaptersPagesSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/ChaptersPagesSheet.kt @@ -87,8 +87,9 @@ class ChaptersPagesSheet : BaseAdaptiveSheet(), Actio if (newState == STATE_DRAGGING || newState == STATE_SETTLING) { return } + val binding = viewBinding ?: return val isActionModeStarted = actionModeDelegate?.isActionModeStarted == true - viewBinding?.toolbar?.menuView?.isVisible = newState != STATE_COLLAPSED && !isActionModeStarted + binding.toolbar.menuView?.isVisible = newState != STATE_COLLAPSED && !isActionModeStarted } override fun onActionModeStarted(mode: ActionMode) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/chapters/ChaptersFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/chapters/ChaptersFragment.kt index 3966c7b0d..764f87738 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/chapters/ChaptersFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/chapters/ChaptersFragment.kt @@ -8,10 +8,12 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.view.ActionMode import androidx.core.graphics.Insets +import androidx.core.view.ancestors import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager +import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.firstOrNull @@ -260,6 +262,9 @@ class ChaptersFragment : } private suspend fun onSelectChapter(chapterId: Long) { + if (!isResumed) { + view?.ancestors?.firstNotNullOfOrNull { it as? ViewPager2 }?.setCurrentItem(0, true) + } val position = withContext(Dispatchers.Default) { val predicate: (ListModel) -> Boolean = { x -> x is ChapterListItem && x.chapter.id == chapterId } val items = chaptersAdapter?.observeItems()?.firstOrNull { it.any(predicate) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt index f181d5088..ecbb50d91 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt @@ -85,7 +85,6 @@ fun recommendationMangaItemAD( binding.root.setOnClickListener { v -> itemClickListener.onItemClick(item.manga, v) } - bind { binding.textViewTitle.text = item.manga.title binding.textViewSubtitle.textAndVisible = item.subtitle diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/ListHeaderAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/ListHeaderAD.kt index e29e1e49a..a8042d08d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/ListHeaderAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/ListHeaderAD.kt @@ -4,14 +4,14 @@ import androidx.core.view.isInvisible import androidx.core.view.isVisible import com.google.android.material.badge.BadgeDrawable import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding -import org.koitharu.kotatsu.databinding.ItemHeaderButtonBinding +import org.koitharu.kotatsu.databinding.ItemHeaderBinding import org.koitharu.kotatsu.list.ui.model.ListHeader import org.koitharu.kotatsu.list.ui.model.ListModel fun listHeaderAD( listener: ListHeaderClickListener?, -) = adapterDelegateViewBinding( - { inflater, parent -> ItemHeaderButtonBinding.inflate(inflater, parent, false) }, +) = adapterDelegateViewBinding( + { inflater, parent -> ItemHeaderBinding.inflate(inflater, parent, false) }, ) { var badge: BadgeDrawable? = null diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingHeaderAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingHeaderAD.kt index 6a3b088a7..f22cc842f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingHeaderAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingHeaderAD.kt @@ -3,12 +3,12 @@ package org.koitharu.kotatsu.scrobbling.common.ui.config.adapter import androidx.core.view.isInvisible import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.databinding.ItemHeaderButtonBinding +import org.koitharu.kotatsu.databinding.ItemHeaderBinding import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus -fun scrobblingHeaderAD() = adapterDelegateViewBinding( - { inflater, parent -> ItemHeaderButtonBinding.inflate(inflater, parent, false) }, +fun scrobblingHeaderAD() = adapterDelegateViewBinding( + { inflater, parent -> ItemHeaderBinding.inflate(inflater, parent, false) }, ) { binding.buttonMore.isInvisible = true diff --git a/app/src/main/res/layout/item_chapter.xml b/app/src/main/res/layout/item_chapter.xml index 43ac59102..fbc65d73d 100644 --- a/app/src/main/res/layout/item_chapter.xml +++ b/app/src/main/res/layout/item_chapter.xml @@ -5,7 +5,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="?attr/listPreferredItemHeight" - android:background="?selectableItemBackground" + android:background="@drawable/list_selector" android:baselineAligned="false" android:gravity="center_vertical" android:minHeight="@dimen/chapter_list_item_height" diff --git a/app/src/main/res/layout/item_header_button.xml b/app/src/main/res/layout/item_header.xml similarity index 100% rename from app/src/main/res/layout/item_header_button.xml rename to app/src/main/res/layout/item_header.xml diff --git a/app/src/main/res/layout/item_recommendation_manga.xml b/app/src/main/res/layout/item_recommendation_manga.xml index d647fde3a..e8e9164c1 100644 --- a/app/src/main/res/layout/item_recommendation_manga.xml +++ b/app/src/main/res/layout/item_recommendation_manga.xml @@ -34,7 +34,7 @@ app:layout_constraintTop_toTopOf="@+id/imageView_cover" tools:text="@tools:sample/lorem" /> - -