Show reading progress indicators in lists

This commit is contained in:
Koitharu
2022-06-28 09:07:03 +03:00
parent b82b46f7d7
commit 04dd8003f7
23 changed files with 414 additions and 68 deletions

View File

@@ -11,10 +11,10 @@ import org.koitharu.kotatsu.favourites.ui.list.FavouritesListViewModel
val favouritesModule
get() = module {
factory { FavouritesRepository(get(), get()) }
single { FavouritesRepository(get(), get()) }
viewModel { categoryId ->
FavouritesListViewModel(categoryId.get(), get(), get(), get())
FavouritesListViewModel(categoryId.get(), get(), get(), get(), get())
}
viewModel { FavouritesCategoriesViewModel(get(), get()) }
viewModel { manga ->

View File

@@ -11,7 +11,8 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID
import org.koitharu.kotatsu.list.domain.CountersProvider
import org.koitharu.kotatsu.history.domain.HistoryRepository
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.ui.MangaListViewModel
import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.LoadingState
@@ -25,8 +26,9 @@ class FavouritesListViewModel(
private val categoryId: Long,
private val repository: FavouritesRepository,
private val trackingRepository: TrackingRepository,
private val historyRepository: HistoryRepository,
settings: AppSettings,
) : MangaListViewModel(settings), CountersProvider {
) : MangaListViewModel(settings), ListExtraProvider {
var sortOrder: LiveData<SortOrder?> = if (categoryId == NO_ID) {
MutableLiveData(null)
@@ -92,4 +94,8 @@ class FavouritesListViewModel(
override suspend fun getCounter(mangaId: Long): Int {
return trackingRepository.getNewChaptersCount(mangaId)
}
override suspend fun getProgress(mangaId: Long): Float {
return historyRepository.getProgress(mangaId)
}
}

View File

@@ -8,6 +8,6 @@ import org.koitharu.kotatsu.history.ui.HistoryListViewModel
val historyModule
get() = module {
factory { HistoryRepository(get(), get(), get()) }
single { HistoryRepository(get(), get(), get()) }
viewModel { HistoryListViewModel(get(), get(), get(), get()) }
}

View File

@@ -45,26 +45,36 @@ abstract class HistoryDao {
@Query("SELECT COUNT(*) FROM history")
abstract fun observeCount(): Flow<Int>
@Query("SELECT percent FROM history WHERE manga_id = :id")
abstract fun findProgress(id: Long): Float?
@Query("DELETE FROM history")
abstract suspend fun clear()
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract suspend fun insert(entity: HistoryEntity): Long
@Query("UPDATE history SET page = :page, chapter_id = :chapterId, scroll = :scroll, updated_at = :updatedAt WHERE manga_id = :mangaId")
@Query("UPDATE history SET page = :page, chapter_id = :chapterId, scroll = :scroll, percent = :percent, updated_at = :updatedAt WHERE manga_id = :mangaId")
abstract suspend fun update(
mangaId: Long,
page: Int,
chapterId: Long,
scroll: Float,
updatedAt: Long
percent: Float,
updatedAt: Long,
): Int
@Query("DELETE FROM history WHERE manga_id = :mangaId")
abstract suspend fun delete(mangaId: Long)
suspend fun update(entity: HistoryEntity) =
update(entity.mangaId, entity.page, entity.chapterId, entity.scroll, entity.updatedAt)
suspend fun update(entity: HistoryEntity) = update(
mangaId = entity.mangaId,
page = entity.page,
chapterId = entity.chapterId,
scroll = entity.scroll,
percent = entity.percent,
updatedAt = entity.updatedAt
)
@Transaction
open suspend fun upsert(entity: HistoryEntity): Boolean {

View File

@@ -16,6 +16,8 @@ import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import org.koitharu.kotatsu.utils.ext.mapItems
const val PROGRESS_NONE = -1f
class HistoryRepository(
private val db: MangaDatabase,
private val trackingRepository: TrackingRepository,
@@ -86,6 +88,10 @@ class HistoryRepository(
return db.historyDao.find(manga.id)?.toMangaHistory()
}
suspend fun getProgress(mangaId: Long): Float {
return db.historyDao.findProgress(mangaId) ?: PROGRESS_NONE
}
suspend fun clear() {
db.historyDao.clear()
}

View File

@@ -2,8 +2,6 @@ package org.koitharu.kotatsu.history.ui
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import java.util.*
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
@@ -26,6 +24,8 @@ import org.koitharu.kotatsu.utils.SingleLiveEvent
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
import org.koitharu.kotatsu.utils.ext.daysDiff
import org.koitharu.kotatsu.utils.ext.onFirst
import java.util.*
import java.util.concurrent.TimeUnit
class HistoryListViewModel(
private val repository: HistoryRepository,
@@ -112,9 +112,9 @@ class HistoryListViewModel(
}
val counter = trackingRepository.getNewChaptersCount(manga.id)
result += when (mode) {
ListMode.LIST -> manga.toListModel(counter)
ListMode.DETAILED_LIST -> manga.toListDetailedModel(counter)
ListMode.GRID -> manga.toGridModel(counter)
ListMode.LIST -> manga.toListModel(counter, history.percent)
ListMode.DETAILED_LIST -> manga.toListDetailedModel(counter, history.percent)
ListMode.GRID -> manga.toGridModel(counter, history.percent)
}
}
return result

View File

@@ -0,0 +1,129 @@
package org.koitharu.kotatsu.history.ui.util
import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import androidx.annotation.StyleRes
import androidx.core.graphics.ColorUtils
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
import kotlin.math.roundToInt
class ReadingProgressDrawable(
context: Context,
@StyleRes styleResId: Int,
) : Drawable() {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val lineColor: Int
private val outlineColor: Int
private val backgroundColor: Int
private val textColor: Int
private val textPattern = context.getString(R.string.percent_string_pattern)
private val textBounds = Rect()
private val hasBackground: Boolean
private val hasOutline: Boolean
private val hasText: Boolean
private val desiredHeight: Int
private val desiredWidth: Int
private val autoFitTextSize: Boolean
var progress: Float = PROGRESS_NONE
set(value) {
field = value
text = textPattern.format((value * 100f).roundToInt().toString())
paint.getTextBounds(text, 0, text.length, textBounds)
invalidateSelf()
}
private var text = ""
init {
val ta = context.obtainStyledAttributes(styleResId, R.styleable.ProgressDrawable)
desiredHeight = ta.getDimensionPixelSize(R.styleable.ProgressDrawable_android_height, -1)
desiredWidth = ta.getDimensionPixelSize(R.styleable.ProgressDrawable_android_width, -1)
autoFitTextSize = ta.getBoolean(R.styleable.ProgressDrawable_autoFitTextSize, false)
lineColor = ta.getColor(R.styleable.ProgressDrawable_android_strokeColor, Color.BLACK)
outlineColor = ta.getColor(R.styleable.ProgressDrawable_outlineColor, Color.TRANSPARENT)
backgroundColor = ColorUtils.setAlphaComponent(
ta.getColor(R.styleable.ProgressDrawable_android_fillColor, Color.TRANSPARENT),
(255 * ta.getFloat(R.styleable.ProgressDrawable_android_fillAlpha, 0f)).toInt(),
)
textColor = ta.getColor(R.styleable.ProgressDrawable_android_textColor, lineColor)
paint.strokeCap = Paint.Cap.ROUND
paint.textAlign = Paint.Align.CENTER
paint.textSize = ta.getDimension(R.styleable.ProgressDrawable_android_textSize, paint.textSize)
paint.strokeWidth = ta.getDimension(R.styleable.ProgressDrawable_strokeWidth, 1f)
hasBackground = Color.alpha(backgroundColor) != 0
hasOutline = Color.alpha(outlineColor) != 0
hasText = Color.alpha(textColor) != 0 && paint.textSize > 0
ta.recycle()
}
override fun onBoundsChange(bounds: Rect) {
super.onBoundsChange(bounds)
if (autoFitTextSize) {
val innerWidth = bounds.width() - (paint.strokeWidth * 2f)
paint.textSize = getTextSizeForWidth(innerWidth, "100%")
}
}
override fun draw(canvas: Canvas) {
if (progress < 0f) {
return
}
val cx = bounds.exactCenterX()
val cy = bounds.exactCenterY()
val radius = minOf(bounds.width(), bounds.height()) / 2f
if (hasBackground) {
paint.style = Paint.Style.FILL
paint.color = backgroundColor
canvas.drawCircle(cx, cy, radius, paint)
}
val innerRadius = radius - paint.strokeWidth / 2f
paint.style = Paint.Style.STROKE
if (hasOutline) {
paint.color = outlineColor
canvas.drawCircle(cx, cy, innerRadius, paint)
}
paint.color = lineColor
canvas.drawArc(
cx - innerRadius,
cy - innerRadius,
cx + innerRadius,
cy + innerRadius,
-90f,
360f * progress,
false,
paint,
)
if (hasText) {
paint.style = Paint.Style.FILL
paint.color = textColor
val ty = bounds.height() / 2f + textBounds.height() / 2f - textBounds.bottom
canvas.drawText(text, cx, ty, paint)
}
}
override fun setAlpha(alpha: Int) {
paint.alpha = alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
paint.colorFilter = colorFilter
}
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Deprecated in Java")
override fun getOpacity() = PixelFormat.TRANSLUCENT
override fun getIntrinsicHeight() = desiredHeight
override fun getIntrinsicWidth() = desiredWidth
private fun getTextSizeForWidth(width: Float, text: String): Float {
val testTextSize = 48f
paint.textSize = testTextSize
paint.getTextBounds(text, 0, text.length, textBounds)
return testTextSize * width / textBounds.width()
}
}

View File

@@ -0,0 +1,114 @@
package org.koitharu.kotatsu.history.ui.util
import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Outline
import android.util.AttributeSet
import android.view.View
import android.view.ViewOutlineProvider
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.annotation.AttrRes
import androidx.annotation.StyleRes
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
class ReadingProgressView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
@AttrRes defStyleAttr: Int = 0,
) : View(context, attrs, defStyleAttr), ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
private var percentAnimator: ValueAnimator? = null
private val animationDuration = context.resources.getInteger(android.R.integer.config_shortAnimTime).toLong()
@StyleRes
private val drawableStyle: Int
var percent: Float
get() = peekProgressDrawable()?.progress ?: PROGRESS_NONE
set(value) {
cancelAnimation()
getProgressDrawable().progress = value
}
init {
val ta = context.obtainStyledAttributes(attrs, R.styleable.ReadingProgressView, defStyleAttr, 0)
drawableStyle = ta.getResourceId(R.styleable.ReadingProgressView_progressStyle, R.style.ProgressDrawable)
ta.recycle()
outlineProvider = OutlineProvider()
if (isInEditMode) {
percent = 0.27f
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
percentAnimator?.run {
if (isRunning) end()
}
percentAnimator = null
}
override fun onAnimationUpdate(animation: ValueAnimator) {
val p = animation.animatedValue as Float
getProgressDrawable().progress = p
}
override fun onAnimationStart(animation: Animator?) = Unit
override fun onAnimationEnd(animation: Animator?) {
if (percentAnimator === animation) {
percentAnimator = null
}
}
override fun onAnimationCancel(animation: Animator?) = Unit
override fun onAnimationRepeat(animation: Animator?) = Unit
fun setPercent(value: Float, animate: Boolean) {
val currentDrawable = peekProgressDrawable()
if (!animate || currentDrawable == null || value == PROGRESS_NONE) {
percent = value
return
}
percentAnimator?.cancel()
percentAnimator = ValueAnimator.ofFloat(
currentDrawable.progress.coerceAtLeast(0f),
value
).apply {
duration = animationDuration
interpolator = AccelerateDecelerateInterpolator()
addUpdateListener(this@ReadingProgressView)
addListener(this@ReadingProgressView)
start()
}
}
private fun cancelAnimation() {
percentAnimator?.cancel()
percentAnimator = null
}
private fun peekProgressDrawable(): ReadingProgressDrawable? {
return background as? ReadingProgressDrawable
}
private fun getProgressDrawable(): ReadingProgressDrawable {
var d = peekProgressDrawable()
if (d != null) {
return d
}
d = ReadingProgressDrawable(context, drawableStyle)
background = d
return d
}
private class OutlineProvider : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setOval(0, 0, view.width, view.height)
}
}
}

View File

@@ -1,6 +1,8 @@
package org.koitharu.kotatsu.list.domain
fun interface CountersProvider {
interface ListExtraProvider {
suspend fun getCounter(mangaId: Long): Int
suspend fun getProgress(mangaId: Long): Float
}

View File

@@ -11,6 +11,7 @@ import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.databinding.ItemMangaGridBinding
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.MangaGridModel
import org.koitharu.kotatsu.parsers.model.Manga
@@ -43,8 +44,9 @@ fun mangaGridItemAD(
}
}
bind {
bind { payloads ->
binding.textViewTitle.text = item.title
binding.progressView.setPercent(item.progress, MangaListAdapter.PAYLOAD_PROGRESS in payloads)
imageRequest?.dispose()
imageRequest = binding.imageViewCover.newImageRequest(item.coverUrl)
.referer(item.manga.publicUrl)
@@ -60,6 +62,7 @@ fun mangaGridItemAD(
onViewRecycled {
itemView.clearBadge(badge)
binding.progressView.percent = PROGRESS_NONE
badge = null
imageRequest?.dispose()
imageRequest = null

View File

@@ -54,9 +54,14 @@ class MangaListAdapter(
override fun getChangePayload(oldItem: ListModel, newItem: ListModel): Any? {
return when (newItem) {
is MangaListModel,
is MangaGridModel,
is MangaListDetailedModel,
is MangaItemModel -> {
oldItem as MangaItemModel
if (oldItem.progress != newItem.progress) {
PAYLOAD_PROGRESS
} else {
Unit
}
}
is CurrentFilterModel -> Unit
else -> super.getChangePayload(oldItem, newItem)
}
@@ -77,5 +82,7 @@ class MangaListAdapter(
const val ITEM_TYPE_HEADER = 9
const val ITEM_TYPE_FILTER = 10
const val ITEM_TYPE_HEADER_FILTER = 11
val PAYLOAD_PROGRESS = Any()
}
}

View File

@@ -10,6 +10,7 @@ import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.databinding.ItemMangaListDetailsBinding
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.MangaListDetailedModel
import org.koitharu.kotatsu.parsers.model.Manga
@@ -36,10 +37,11 @@ fun mangaListDetailedItemAD(
clickListener.onItemLongClick(item.manga, it)
}
bind {
bind { payloads ->
imageRequest?.dispose()
binding.textViewTitle.text = item.title
binding.textViewSubtitle.textAndVisible = item.subtitle
binding.progressView.setPercent(item.progress, MangaListAdapter.PAYLOAD_PROGRESS in payloads)
imageRequest = binding.imageViewCover.newImageRequest(item.coverUrl)
.referer(item.manga.publicUrl)
.placeholder(R.drawable.ic_placeholder)
@@ -56,6 +58,7 @@ fun mangaListDetailedItemAD(
onViewRecycled {
itemView.clearBadge(badge)
binding.progressView.percent = PROGRESS_NONE
badge = null
imageRequest?.dispose()
imageRequest = null

View File

@@ -4,21 +4,23 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.list.domain.CountersProvider
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.utils.ext.ifZero
fun Manga.toListModel(counter: Int) = MangaListModel(
fun Manga.toListModel(counter: Int, progress: Float) = MangaListModel(
id = id,
title = title,
subtitle = tags.joinToString(", ") { it.title },
coverUrl = coverUrl,
manga = this,
counter = counter,
progress = progress,
)
fun Manga.toListDetailedModel(counter: Int) = MangaListDetailedModel(
fun Manga.toListDetailedModel(counter: Int, progress: Float) = MangaListDetailedModel(
id = id,
title = title,
subtitle = altTitle,
@@ -27,50 +29,48 @@ fun Manga.toListDetailedModel(counter: Int) = MangaListDetailedModel(
coverUrl = coverUrl,
manga = this,
counter = counter,
progress = progress,
)
fun Manga.toGridModel(counter: Int) = MangaGridModel(
fun Manga.toGridModel(counter: Int, progress: Float) = MangaGridModel(
id = id,
title = title,
coverUrl = coverUrl,
manga = this,
counter = counter,
progress = progress,
)
suspend fun List<Manga>.toUi(
mode: ListMode,
countersProvider: CountersProvider,
extraProvider: ListExtraProvider,
): List<MangaItemModel> = when (mode) {
ListMode.LIST -> map { it.toListModel(countersProvider.getCounter(it.id)) }
ListMode.DETAILED_LIST -> map { it.toListDetailedModel(countersProvider.getCounter(it.id)) }
ListMode.GRID -> map { it.toGridModel(countersProvider.getCounter(it.id)) }
}
suspend fun <C : MutableCollection<ListModel>> List<Manga>.toUi(
destination: C,
mode: ListMode,
countersProvider: CountersProvider,
): C = when (mode) {
ListMode.LIST -> mapTo(destination) { it.toListModel(countersProvider.getCounter(it.id)) }
ListMode.DETAILED_LIST -> mapTo(destination) { it.toListDetailedModel(countersProvider.getCounter(it.id)) }
ListMode.GRID -> mapTo(destination) { it.toGridModel(countersProvider.getCounter(it.id)) }
ListMode.LIST -> map {
it.toListModel(extraProvider.getCounter(it.id), extraProvider.getProgress(it.id))
}
ListMode.DETAILED_LIST -> map {
it.toListDetailedModel(extraProvider.getCounter(it.id), extraProvider.getProgress(it.id))
}
ListMode.GRID -> map {
it.toGridModel(extraProvider.getCounter(it.id), extraProvider.getProgress(it.id))
}
}
fun List<Manga>.toUi(
mode: ListMode,
): List<MangaItemModel> = when (mode) {
ListMode.LIST -> map { it.toListModel(0) }
ListMode.DETAILED_LIST -> map { it.toListDetailedModel(0) }
ListMode.GRID -> map { it.toGridModel(0) }
ListMode.LIST -> map { it.toListModel(0, PROGRESS_NONE) }
ListMode.DETAILED_LIST -> map { it.toListDetailedModel(0, PROGRESS_NONE) }
ListMode.GRID -> map { it.toGridModel(0, PROGRESS_NONE) }
}
fun <C : MutableCollection<ListModel>> List<Manga>.toUi(
destination: C,
mode: ListMode,
): C = when (mode) {
ListMode.LIST -> mapTo(destination) { it.toListModel(0) }
ListMode.DETAILED_LIST -> mapTo(destination) { it.toListDetailedModel(0) }
ListMode.GRID -> mapTo(destination) { it.toGridModel(0) }
ListMode.LIST -> mapTo(destination) { it.toListModel(0, PROGRESS_NONE) }
ListMode.DETAILED_LIST -> mapTo(destination) { it.toListDetailedModel(0, PROGRESS_NONE) }
ListMode.GRID -> mapTo(destination) { it.toGridModel(0, PROGRESS_NONE) }
}
fun Throwable.toErrorState(canRetry: Boolean = true) = ErrorState(

View File

@@ -4,8 +4,9 @@ import org.koitharu.kotatsu.parsers.model.Manga
data class MangaGridModel(
override val id: Long,
val title: String,
val coverUrl: String,
override val title: String,
override val coverUrl: String,
override val manga: Manga,
val counter: Int,
override val counter: Int,
override val progress: Float,
) : MangaItemModel

View File

@@ -6,4 +6,8 @@ sealed interface MangaItemModel : ListModel {
val id: Long
val manga: Manga
val title: String
val coverUrl: String
val counter: Int
val progress: Float
}

View File

@@ -4,11 +4,12 @@ import org.koitharu.kotatsu.parsers.model.Manga
data class MangaListDetailedModel(
override val id: Long,
val title: String,
override val title: String,
val subtitle: String?,
val tags: String,
val coverUrl: String,
override val coverUrl: String,
val rating: String?,
override val manga: Manga,
val counter: Int,
override val counter: Int,
override val progress: Float,
) : MangaItemModel

View File

@@ -4,9 +4,10 @@ import org.koitharu.kotatsu.parsers.model.Manga
data class MangaListModel(
override val id: Long,
val title: String,
override val title: String,
val subtitle: String,
val coverUrl: String,
override val coverUrl: String,
override val manga: Manga,
val counter: Int,
override val counter: Int,
override val progress: Float,
) : MangaItemModel

View File

@@ -376,6 +376,9 @@ class ReaderViewModel(
val chapterIndex = chapters.indexOfFirst { x -> x.id == chapterId }
val pages = content.value?.pages ?: return -1f
val pagesCount = pages.count { x -> x.chapterId == chapterId }
if (chaptersCount == 0 || pagesCount == 0) {
return -1f
}
val chapterPercent = (chapterIndex + 1) / chaptersCount.toFloat()
val pagePercent = (pageIndex + 1) / pagesCount.toFloat()
return pagePercent * chapterPercent // FIXME

View File

@@ -13,15 +13,28 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<org.koitharu.kotatsu.base.ui.widgets.CoverImageView
android:id="@+id/imageView_cover"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
tools:ignore="ContentDescription"
tools:src="@tools:sample/backgrounds/scenic[3]" />
android:layout_height="wrap_content">
<org.koitharu.kotatsu.base.ui.widgets.CoverImageView
android:id="@+id/imageView_cover"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
tools:ignore="ContentDescription"
tools:src="@tools:sample/backgrounds/scenic[3]" />
<org.koitharu.kotatsu.history.ui.util.ReadingProgressView
android:id="@+id/progressView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="bottom|end"
android:layout_margin="4dp" />
</FrameLayout>
<TextView
android:id="@+id/textView_title"

View File

@@ -12,14 +12,27 @@
android:layout_height="match_parent"
android:orientation="horizontal">
<org.koitharu.kotatsu.base.ui.widgets.CoverImageView
android:id="@+id/imageView_cover"
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
tools:src="@tools:sample/backgrounds/scenic" />
android:layout_height="match_parent">
<org.koitharu.kotatsu.base.ui.widgets.CoverImageView
android:id="@+id/imageView_cover"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
tools:src="@tools:sample/backgrounds/scenic" />
<org.koitharu.kotatsu.history.ui.util.ReadingProgressView
android:id="@+id/progressView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="bottom|end"
android:layout_margin="4dp" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"

View File

@@ -35,4 +35,21 @@
<attr name="android:insetRight" />
</declare-styleable>
<declare-styleable name="ProgressDrawable">
<attr name="strokeWidth" />
<attr name="android:strokeColor" />
<attr name="android:textSize" />
<attr name="android:textColor" />
<attr name="android:fillColor" />
<attr name="android:fillAlpha" />
<attr name="android:width" />
<attr name="android:height" />
<attr name="outlineColor" format="color" />
<attr name="autoFitTextSize" format="boolean" />
</declare-styleable>
<declare-styleable name="ReadingProgressView">
<attr name="progressStyle" format="reference" />
</declare-styleable>
</resources>

View File

@@ -26,6 +26,8 @@
<dimen name="toolbar_button_margin">10dp</dimen>
<dimen name="widget_cover_height">116dp</dimen>
<dimen name="widget_cover_width">84dp</dimen>
<dimen name="reading_progress_stroke">4dp</dimen>
<dimen name="reading_progress_text_size">10dp</dimen>
<dimen name="search_suggestions_manga_height">124dp</dimen>
<dimen name="search_suggestions_manga_spacing">4dp</dimen>

View File

@@ -173,4 +173,15 @@
<item name="android:widgetLayout">@layout/preference_widget_material_switch</item>
</style>
<!-- Progress drawable -->
<style name="ProgressDrawable">
<item name="android:fillAlpha">0.8</item>
<item name="android:fillColor">?android:colorBackground</item>
<item name="android:strokeColor">?colorPrimaryDark</item>
<item name="android:textColor">?android:textColorPrimary</item>
<item name="strokeWidth">3dp</item>
<item name="android:textSize">9sp</item>
</style>
</resources>