Misc improvements

master
Koitharu 1 year ago
parent 609f2bd134
commit 19446db192
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -19,9 +19,6 @@ abstract class LifecycleAwareViewHolder(
init { init {
parentLifecycleOwner.lifecycle.addObserver(ParentLifecycleObserver()) parentLifecycleOwner.lifecycle.addObserver(ParentLifecycleObserver())
if (parentLifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
} }
fun setIsCurrent(value: Boolean) { fun setIsCurrent(value: Boolean) {
@ -29,6 +26,9 @@ abstract class LifecycleAwareViewHolder(
dispatchResumed() dispatchResumed()
} }
@CallSuper
open fun onCreate() = lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
@CallSuper @CallSuper
open fun onStart() = lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START) open fun onStart() = lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START)
@ -41,6 +41,9 @@ abstract class LifecycleAwareViewHolder(
@CallSuper @CallSuper
open fun onStop() = lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP) open fun onStop() = lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
@CallSuper
open fun onDestroy() = lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
private fun dispatchResumed() { private fun dispatchResumed() {
val isParentResumed = parentLifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED) val isParentResumed = parentLifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)
if (isCurrent && isParentResumed) { if (isCurrent && isParentResumed) {
@ -60,28 +63,18 @@ abstract class LifecycleAwareViewHolder(
private inner class ParentLifecycleObserver : DefaultLifecycleObserver { private inner class ParentLifecycleObserver : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) { override fun onCreate(owner: LifecycleOwner) = this@LifecycleAwareViewHolder.onCreate()
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
override fun onStart(owner: LifecycleOwner) { override fun onStart(owner: LifecycleOwner) = this@LifecycleAwareViewHolder.onStart()
onStart()
}
override fun onResume(owner: LifecycleOwner) { override fun onResume(owner: LifecycleOwner) = this@LifecycleAwareViewHolder.dispatchResumed()
dispatchResumed()
}
override fun onPause(owner: LifecycleOwner) { override fun onPause(owner: LifecycleOwner) = this@LifecycleAwareViewHolder.dispatchResumed()
dispatchResumed()
}
override fun onStop(owner: LifecycleOwner) { override fun onStop(owner: LifecycleOwner) = this@LifecycleAwareViewHolder.onStop()
onStop()
}
override fun onDestroy(owner: LifecycleOwner) { override fun onDestroy(owner: LifecycleOwner) {
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) this@LifecycleAwareViewHolder.onDestroy()
owner.lifecycle.removeObserver(this) owner.lifecycle.removeObserver(this)
} }
} }

@ -25,7 +25,7 @@ import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.swiperefreshlayout.widget.CircularProgressDrawable import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import coil3.ImageLoader import coil3.ImageLoader
import coil3.request.ImageRequest import coil3.request.ImageRequest
import coil3.request.SuccessResult import coil3.request.SuccessResult
@ -127,7 +127,7 @@ class DetailsActivity :
View.OnClickListener, View.OnClickListener,
View.OnLongClickListener, PopupMenu.OnMenuItemClickListener, View.OnLayoutChangeListener, View.OnLongClickListener, PopupMenu.OnMenuItemClickListener, View.OnLayoutChangeListener,
ViewTreeObserver.OnDrawListener, ChipsView.OnChipClickListener, OnListItemClickListener<Bookmark>, ViewTreeObserver.OnDrawListener, ChipsView.OnChipClickListener, OnListItemClickListener<Bookmark>,
OnContextClickListenerCompat { OnContextClickListenerCompat, SwipeRefreshLayout.OnRefreshListener {
@Inject @Inject
lateinit var shortcutManager: AppShortcutManager lateinit var shortcutManager: AppShortcutManager
@ -165,6 +165,7 @@ class DetailsActivity :
viewBinding.infoLayout.chipSource.setOnClickListener(this) viewBinding.infoLayout.chipSource.setOnClickListener(this)
viewBinding.infoLayout.chipSize.setOnClickListener(this) viewBinding.infoLayout.chipSize.setOnClickListener(this)
viewBinding.textViewDescription.addOnLayoutChangeListener(this) viewBinding.textViewDescription.addOnLayoutChangeListener(this)
viewBinding.swipeRefreshLayout.setOnRefreshListener(this)
viewBinding.textViewDescription.viewTreeObserver.addOnDrawListener(this) viewBinding.textViewDescription.viewTreeObserver.addOnDrawListener(this)
viewBinding.textViewDescription.movementMethod = LinkMovementMethodCompat.getInstance() viewBinding.textViewDescription.movementMethod = LinkMovementMethodCompat.getInstance()
viewBinding.chipsTags.onChipClickListener = this viewBinding.chipsTags.onChipClickListener = this
@ -349,6 +350,10 @@ class DetailsActivity :
Toast.makeText(view.context, R.string.incognito_mode, Toast.LENGTH_SHORT).show() Toast.makeText(view.context, R.string.incognito_mode, Toast.LENGTH_SHORT).show()
} }
override fun onRefresh() {
viewModel.reload()
}
override fun onDraw() { override fun onDraw() {
viewBinding.run { viewBinding.run {
buttonDescriptionMore.isVisible = textViewDescription.maxLines == Int.MAX_VALUE || buttonDescriptionMore.isVisible = textViewDescription.maxLines == Int.MAX_VALUE ||
@ -420,18 +425,7 @@ class DetailsActivity :
} }
private fun onLoadingStateChanged(isLoading: Boolean) { private fun onLoadingStateChanged(isLoading: Boolean) {
val button = viewBinding.buttonDownload ?: return viewBinding.swipeRefreshLayout.isRefreshing = isLoading
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_download)
}
} }
private fun onScrobblingInfoChanged(scrobblings: List<ScrobblingInfo>) { private fun onScrobblingInfoChanged(scrobblings: List<ScrobblingInfo>) {

@ -44,7 +44,7 @@ interface ChaptersSelectMacro {
) : ChaptersSelectMacro { ) : ChaptersSelectMacro {
override fun getChaptersIds(mangaId: Long, chapters: List<MangaChapter>): Set<Long> { override fun getChaptersIds(mangaId: Long, chapters: List<MangaChapter>): Set<Long> {
val result = ArraySet<Long>(chaptersCount) val result = ArraySet<Long>(minOf(chaptersCount, chapters.size))
for (c in chapters) { for (c in chapters) {
if (c.branch == branch) { if (c.branch == branch) {
result.add(c.id) result.add(c.id)
@ -72,7 +72,7 @@ interface ChaptersSelectMacro {
val currentChapterId = currentChaptersIds.getOrDefault(mangaId, chapters.first().id) val currentChapterId = currentChaptersIds.getOrDefault(mangaId, chapters.first().id)
var branch: String? = null var branch: String? = null
var isAdding = false var isAdding = false
val result = ArraySet<Long>(chaptersCount) val result = ArraySet<Long>(minOf(chaptersCount, chapters.size))
for (c in chapters) { for (c in chapters) {
if (!isAdding) { if (!isAdding) {
if (c.id == currentChapterId) { if (c.id == currentChapterId) {

@ -122,6 +122,8 @@ abstract class HistoryDao : MangaQueryBuilder.ConditionCallback {
suspend fun deleteAfter(minDate: Long) = setDeletedAtAfter(minDate, System.currentTimeMillis()) suspend fun deleteAfter(minDate: Long) = setDeletedAtAfter(minDate, System.currentTimeMillis())
suspend fun deleteNotFavorite() = setDeletedAtNotFavorite(System.currentTimeMillis())
suspend fun clear() = setDeletedAtAfter(0L, System.currentTimeMillis()) suspend fun clear() = setDeletedAtAfter(0L, System.currentTimeMillis())
suspend fun update(entity: HistoryEntity) = update( suspend fun update(entity: HistoryEntity) = update(
@ -157,6 +159,9 @@ abstract class HistoryDao : MangaQueryBuilder.ConditionCallback {
@Query("UPDATE history SET deleted_at = :deletedAt WHERE created_at >= :minDate AND deleted_at = 0") @Query("UPDATE history SET deleted_at = :deletedAt WHERE created_at >= :minDate AND deleted_at = 0")
protected abstract suspend fun setDeletedAtAfter(minDate: Long, deletedAt: Long) protected abstract suspend fun setDeletedAtAfter(minDate: Long, deletedAt: Long)
@Query("UPDATE history SET deleted_at = :deletedAt WHERE deleted_at = 0 AND NOT EXISTS(SELECT * FROM favourites WHERE history.manga_id = favourites.manga_id)")
protected abstract suspend fun setDeletedAtNotFavorite(deletedAt: Long)
@Transaction @Transaction
@RawQuery(observedEntities = [HistoryEntity::class]) @RawQuery(observedEntities = [HistoryEntity::class])
protected abstract fun observeAllImpl(query: SupportSQLiteQuery): Flow<List<HistoryWithManga>> protected abstract fun observeAllImpl(query: SupportSQLiteQuery): Flow<List<HistoryWithManga>>

@ -164,6 +164,10 @@ class HistoryRepository @Inject constructor(
db.getHistoryDao().deleteAfter(minDate) db.getHistoryDao().deleteAfter(minDate)
} }
suspend fun deleteNotFavorite() {
db.getHistoryDao().deleteNotFavorite()
}
suspend fun delete(ids: Collection<Long>): ReversibleHandle { suspend fun delete(ids: Collection<Long>): ReversibleHandle {
db.withTransaction { db.withTransaction {
for (id in ids) { for (id in ids) {

@ -53,6 +53,7 @@ class HistoryListMenuProvider(
arrayOf( arrayOf(
context.getString(R.string.last_2_hours), context.getString(R.string.last_2_hours),
context.getString(R.string.today), context.getString(R.string.today),
context.getString(R.string.not_in_favorites),
context.getString(R.string.clear_all_history), context.getString(R.string.clear_all_history),
), ),
selectionListener.selection, selectionListener.selection,
@ -61,13 +62,12 @@ class HistoryListMenuProvider(
setIcon(R.drawable.ic_delete_all) setIcon(R.drawable.ic_delete_all)
setNegativeButton(android.R.string.cancel, null) setNegativeButton(android.R.string.cancel, null)
setPositiveButton(R.string.clear) { _, _ -> setPositiveButton(R.string.clear) { _, _ ->
val minDate = when (selectionListener.selection) { when (selectionListener.selection) {
0 -> Instant.now().minus(2, ChronoUnit.HOURS) 0 -> viewModel.clearHistory(Instant.now().minus(2, ChronoUnit.HOURS))
1 -> LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant() 1 -> viewModel.clearHistory(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant())
2 -> Instant.EPOCH 2 -> viewModel.removeNotFavorite()
else -> return@setPositiveButton 3 -> viewModel.clearHistory(null)
} }
viewModel.clearHistory(minDate)
} }
}.show() }.show()
} }

@ -101,9 +101,9 @@ class HistoryListViewModel @Inject constructor(
override fun onRetry() = Unit override fun onRetry() = Unit
fun clearHistory(minDate: Instant) { fun clearHistory(minDate: Instant?) {
launchJob(Dispatchers.Default) { launchJob(Dispatchers.Default) {
val stringRes = if (minDate <= Instant.EPOCH) { val stringRes = if (minDate == null) {
repository.clear() repository.clear()
R.string.history_cleared R.string.history_cleared
} else { } else {
@ -114,6 +114,13 @@ class HistoryListViewModel @Inject constructor(
} }
} }
fun removeNotFavorite() {
launchJob(Dispatchers.Default) {
repository.deleteNotFavorite()
onActionDone.call(ReversibleAction(R.string.removed_from_history, null))
}
}
fun removeFromHistory(ids: Set<Long>) { fun removeFromHistory(ids: Set<Long>) {
if (ids.isEmpty()) { if (ids.isEmpty()) {
return return

@ -169,7 +169,7 @@ class ImageActivity : BaseActivity<ActivityImageBinding>(), ImageRequest.Listene
private fun setDrawable(drawable: Drawable?) { private fun setDrawable(drawable: Drawable?) {
if (drawable != null) { if (drawable != null) {
view.setImage(ImageSource.Bitmap(drawable.toBitmap())) view.setImage(ImageSource.bitmap(drawable.toBitmap()))
} else { } else {
view.recycle() view.recycle()
} }

@ -6,6 +6,7 @@ import android.graphics.Color
import android.graphics.Point import android.graphics.Point
import android.graphics.Rect import android.graphics.Rect
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.collection.LruCache
import androidx.core.graphics.alpha import androidx.core.graphics.alpha
import androidx.core.graphics.blue import androidx.core.graphics.blue
import androidx.core.graphics.get import androidx.core.graphics.get
@ -28,38 +29,46 @@ import kotlin.math.abs
class EdgeDetector(private val context: Context) { class EdgeDetector(private val context: Context) {
private val mutex = Mutex() private val mutex = Mutex()
private val cache = LruCache<ImageSource, Rect>(CACHE_SIZE)
suspend fun getBounds(imageSource: ImageSource): Rect? = mutex.withLock { suspend fun getBounds(imageSource: ImageSource): Rect? {
withContext(Dispatchers.IO) { cache[imageSource]?.let { rect ->
val decoder = SkiaPooledImageRegionDecoder(Bitmap.Config.RGB_565) return if (rect.isEmpty) null else rect
try { }
val size = runInterruptible { return mutex.withLock {
decoder.init(context, imageSource) withContext(Dispatchers.IO) {
} val decoder = SkiaPooledImageRegionDecoder(Bitmap.Config.RGB_565)
val edges = coroutineScope { try {
listOf( val size = runInterruptible {
async { detectLeftRightEdge(decoder, size, isLeft = true) }, decoder.init(context, imageSource)
async { detectTopBottomEdge(decoder, size, isTop = true) },
async { detectLeftRightEdge(decoder, size, isLeft = false) },
async { detectTopBottomEdge(decoder, size, isTop = false) },
).awaitAll()
}
var hasEdges = false
for (edge in edges) {
if (edge > 0) {
hasEdges = true
} else if (edge < 0) {
return@withContext null
} }
val edges = coroutineScope {
listOf(
async { detectLeftRightEdge(decoder, size, isLeft = true) },
async { detectTopBottomEdge(decoder, size, isTop = true) },
async { detectLeftRightEdge(decoder, size, isLeft = false) },
async { detectTopBottomEdge(decoder, size, isTop = false) },
).awaitAll()
}
var hasEdges = false
for (edge in edges) {
if (edge > 0) {
hasEdges = true
} else if (edge < 0) {
return@withContext null
}
}
if (hasEdges) {
Rect(edges[0], edges[1], size.x - edges[2], size.y - edges[3])
} else {
null
}
} finally {
decoder.recycle()
} }
if (hasEdges) {
Rect(edges[0], edges[1], size.x - edges[2], size.y - edges[3])
} else {
null
}
} finally {
decoder.recycle()
} }
}.also {
cache.put(imageSource, it ?: EMPTY_RECT)
} }
} }
@ -135,6 +144,8 @@ class EdgeDetector(private val context: Context) {
private const val BLOCK_SIZE = 100 private const val BLOCK_SIZE = 100
private const val COLOR_TOLERANCE = 16 private const val COLOR_TOLERANCE = 16
private const val CACHE_SIZE = 24
private val EMPTY_RECT = Rect(0, 0, 0, 0)
fun isColorTheSame(@ColorInt a: Int, @ColorInt b: Int, tolerance: Int): Boolean { fun isColorTheSame(@ColorInt a: Int, @ColorInt b: Int, tolerance: Int): Boolean {
return abs(a.red - b.red) <= tolerance && return abs(a.red - b.red) <= tolerance &&

@ -164,7 +164,7 @@ class PageLoader @Inject constructor(
} }
suspend fun getTrimmedBounds(uri: Uri): Rect? = runCatchingCancellable { suspend fun getTrimmedBounds(uri: Uri): Rect? = runCatchingCancellable {
edgeDetector.getBounds(ImageSource.Uri(uri)) edgeDetector.getBounds(ImageSource.uri(uri))
}.onFailure { error -> }.onFailure { error ->
error.printStackTraceDebug() error.printStackTraceDebug()
}.getOrNull() }.getOrNull()

@ -57,6 +57,11 @@ abstract class BasePageHolder<B : ViewBinding>(
protected abstract fun onBind(data: ReaderPage) protected abstract fun onBind(data: ReaderPage)
override fun onCreate() {
super.onCreate()
context.registerComponentCallbacks(delegate)
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (delegate.state == State.ERROR && !delegate.isLoading()) { if (delegate.state == State.ERROR && !delegate.isLoading()) {
@ -64,6 +69,11 @@ abstract class BasePageHolder<B : ViewBinding>(
} }
} }
override fun onDestroy() {
context.unregisterComponentCallbacks(delegate)
super.onDestroy()
}
@CallSuper @CallSuper
open fun onAttachedToWindow() { open fun onAttachedToWindow() {
delegate.onAttachedToWindow() delegate.onAttachedToWindow()

@ -1,9 +1,12 @@
package org.koitharu.kotatsu.reader.ui.pager package org.koitharu.kotatsu.reader.ui.pager
import android.content.ComponentCallbacks2
import android.content.res.Configuration
import android.graphics.Rect import android.graphics.Rect
import android.net.Uri import android.net.Uri
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.davemorrissey.labs.subscaleview.DefaultOnImageEventListener import com.davemorrissey.labs.subscaleview.DefaultOnImageEventListener
import com.davemorrissey.labs.subscaleview.ImageSource
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -34,7 +37,7 @@ class PageHolderDelegate(
private val networkState: NetworkState, private val networkState: NetworkState,
private val exceptionResolver: ExceptionResolver, private val exceptionResolver: ExceptionResolver,
private val isWebtoon: Boolean, private val isWebtoon: Boolean,
) : DefaultOnImageEventListener, Observer<ReaderSettings> { ) : DefaultOnImageEventListener, Observer<ReaderSettings>, ComponentCallbacks2 {
private val scope = loader.loaderScope + Dispatchers.Main.immediate private val scope = loader.loaderScope + Dispatchers.Main.immediate
var state = State.EMPTY var state = State.EMPTY
@ -99,7 +102,7 @@ class PageHolderDelegate(
fun reload() { fun reload() {
if (state == State.SHOWN) { if (state == State.SHOWN) {
uri?.let { uri?.let {
callback.onImageReady(it, cachedBounds) callback.onImageReady(it.toImageSource(cachedBounds))
} }
} }
} }
@ -135,6 +138,15 @@ class PageHolderDelegate(
callback.onConfigChanged() callback.onConfigChanged()
} }
override fun onConfigurationChanged(newConfig: Configuration) = Unit
@Suppress("OVERRIDE_DEPRECATION")
override fun onLowMemory() = Unit
override fun onTrimMemory(level: Int) {
callback.onTrimMemory()
}
private fun tryConvert(uri: Uri, e: Exception) { private fun tryConvert(uri: Uri, e: Exception) {
val prevJob = job val prevJob = job
job = scope.launch { job = scope.launch {
@ -148,7 +160,7 @@ class PageHolderDelegate(
null null
} }
state = State.CONVERTED state = State.CONVERTED
callback.onImageReady(newUri, cachedBounds) callback.onImageReady(newUri.toImageSource(cachedBounds))
} catch (ce: CancellationException) { } catch (ce: CancellationException) {
throw ce throw ce
} catch (e2: Throwable) { } catch (e2: Throwable) {
@ -181,7 +193,7 @@ class PageHolderDelegate(
} else { } else {
null null
} }
callback.onImageReady(checkNotNull(uri), cachedBounds) callback.onImageReady(checkNotNull(uri).toImageSource(cachedBounds))
} catch (e: CancellationException) { } catch (e: CancellationException) {
throw e throw e
} catch (e: Throwable) { } catch (e: Throwable) {
@ -201,6 +213,15 @@ class PageHolderDelegate(
.onEach { callback.onProgressChanged((100 * it).toInt()) } .onEach { callback.onProgressChanged((100 * it).toInt()) }
.launchIn(scope) .launchIn(scope)
private fun Uri.toImageSource(bounds: Rect?): ImageSource {
val source = ImageSource.uri(this)
return if (bounds != null) {
source.region(bounds)
} else {
source
}
}
enum class State { enum class State {
EMPTY, LOADING, LOADED, CONVERTING, CONVERTED, SHOWING, SHOWN, ERROR EMPTY, LOADING, LOADED, CONVERTING, CONVERTED, SHOWING, SHOWN, ERROR
} }
@ -211,7 +232,7 @@ class PageHolderDelegate(
fun onError(e: Throwable) fun onError(e: Throwable)
fun onImageReady(uri: Uri, bounds: Rect?) fun onImageReady(source: ImageSource)
fun onImageShowing(settings: ReaderSettings) fun onImageShowing(settings: ReaderSettings)
@ -220,5 +241,7 @@ class PageHolderDelegate(
fun onProgressChanged(progress: Int) fun onProgressChanged(progress: Int)
fun onConfigChanged() fun onConfigChanged()
fun onTrimMemory()
} }
} }

@ -2,8 +2,6 @@ package org.koitharu.kotatsu.reader.ui.pager.standard
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.PointF import android.graphics.PointF
import android.graphics.Rect
import android.net.Uri
import android.view.View import android.view.View
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -91,11 +89,7 @@ open class PageHolder(
} }
} }
override fun onImageReady(uri: Uri, bounds: Rect?) { override fun onImageReady(source: ImageSource) {
val source = ImageSource.Uri(uri)
if (bounds != null) {
source.region(bounds)
}
binding.ssiv.setImage(source) binding.ssiv.setImage(source)
} }
@ -143,6 +137,10 @@ open class PageHolder(
bindingInfo.progressBar.hide() bindingInfo.progressBar.hide()
} }
override fun onTrimMemory() {
// TODO https://developer.android.com/topic/performance/memory
}
final override fun onClick(v: View) { final override fun onClick(v: View) {
when (v.id) { when (v.id) {
R.id.button_retry -> delegate.retry(boundData?.toMangaPage() ?: return, isFromUser = true) R.id.button_retry -> delegate.retry(boundData?.toMangaPage() ?: return, isFromUser = true)

@ -1,7 +1,5 @@
package org.koitharu.kotatsu.reader.ui.pager.webtoon package org.koitharu.kotatsu.reader.ui.pager.webtoon
import android.graphics.Rect
import android.net.Uri
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
@ -91,11 +89,7 @@ class WebtoonHolder(
} }
} }
override fun onImageReady(uri: Uri, bounds: Rect?) { override fun onImageReady(source: ImageSource) {
val source = ImageSource.Uri(uri)
if (bounds != null) {
source.region(bounds)
}
binding.ssiv.setImage(source) binding.ssiv.setImage(source)
} }
@ -117,6 +111,10 @@ class WebtoonHolder(
bindingInfo.progressBar.hide() bindingInfo.progressBar.hide()
} }
override fun onTrimMemory() {
// TODO
}
override fun onClick(v: View) { override fun onClick(v: View) {
when (v.id) { when (v.id) {
R.id.button_retry -> delegate.retry(boundData?.toMangaPage() ?: return, isFromUser = true) R.id.button_retry -> delegate.retry(boundData?.toMangaPage() ?: return, isFromUser = true)

@ -31,361 +31,368 @@
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/scrollView" android:id="@+id/swipeRefreshLayout"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:clipToPadding="false"
android:scrollIndicators="top"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/appbar" app:layout_constraintEnd_toEndOf="@id/appbar"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/appbar"> app:layout_constraintTop_toBottomOf="@id/appbar">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.core.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:clipToPadding="false"
<com.google.android.material.imageview.ShapeableImageView android:scrollIndicators="top"
android:id="@+id/imageView_cover" android:scrollbars="vertical">
android:layout_width="0dp"
android:layout_height="0dp" <androidx.constraintlayout.widget.ConstraintLayout
android:layout_marginStart="16dp" android:layout_width="match_parent"
android:layout_marginTop="16dp" android:layout_height="match_parent">
android:background="?colorSecondaryContainer"
android:foreground="?selectableItemBackground" <com.google.android.material.imageview.ShapeableImageView
android:scaleType="centerCrop" android:id="@+id/imageView_cover"
app:layout_constraintDimensionRatio="H,13:18" android:layout_width="0dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_height="0dp"
app:layout_constraintHorizontal_bias="0" android:layout_marginStart="16dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent" android:background="?colorSecondaryContainer"
app:layout_constraintWidth_percent="0.3" android:foreground="?selectableItemBackground"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover" android:scaleType="centerCrop"
tools:background="@tools:sample/backgrounds/scenic[5]" app:layout_constraintDimensionRatio="H,13:18"
tools:ignore="ContentDescription,UnusedAttribute" /> app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
<TextView app:layout_constraintStart_toStartOf="parent"
android:id="@+id/textView_nsfw" app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content" app:layout_constraintWidth_percent="0.3"
android:layout_height="wrap_content" app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
android:layout_margin="@dimen/card_indicator_offset" tools:background="@tools:sample/backgrounds/scenic[5]"
android:background="@drawable/bg_chip" tools:ignore="ContentDescription,UnusedAttribute" />
android:backgroundTint="@color/warning"
android:gravity="center" <TextView
android:paddingHorizontal="4dp" android:id="@+id/textView_nsfw"
android:paddingVertical="2dp" android:layout_width="wrap_content"
android:text="@string/nsfw" android:layout_height="wrap_content"
android:textAlignment="center" android:layout_margin="@dimen/card_indicator_offset"
android:textAppearance="?textAppearanceLabelMedium" android:background="@drawable/bg_chip"
android:textColor="?colorOnError" android:backgroundTint="@color/warning"
app:layout_constraintBottom_toBottomOf="@id/imageView_cover" android:gravity="center"
app:layout_constraintEnd_toEndOf="@id/imageView_cover" /> android:paddingHorizontal="4dp"
android:paddingVertical="2dp"
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView android:text="@string/nsfw"
android:id="@+id/textView_title" android:textAlignment="center"
android:layout_width="0dp" android:textAppearance="?textAppearanceLabelMedium"
android:layout_height="wrap_content" android:textColor="?colorOnError"
android:layout_marginStart="16dp" app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
android:layout_marginTop="16dp" app:layout_constraintEnd_toEndOf="@id/imageView_cover" />
android:layout_marginEnd="16dp"
android:ellipsize="end" <org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
android:maxLines="5" android:id="@+id/textView_title"
android:textAppearance="?attr/textAppearanceHeadlineSmall" android:layout_width="0dp"
android:textIsSelectable="true" android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginStart="16dp"
app:layout_constraintStart_toEndOf="@id/imageView_cover" android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent" android:layout_marginEnd="16dp"
tools:text="@tools:sample/lorem" /> android:ellipsize="end"
android:maxLines="5"
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView android:textAppearance="?attr/textAppearanceHeadlineSmall"
android:id="@+id/textView_subtitle" android:textIsSelectable="true"
android:layout_width="0dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_height="wrap_content" app:layout_constraintStart_toEndOf="@id/imageView_cover"
android:layout_marginStart="16dp" app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="4dp" tools:text="@tools:sample/lorem" />
android:layout_marginEnd="16dp"
android:ellipsize="end" <org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
android:maxLines="3" android:id="@+id/textView_subtitle"
android:textAppearance="?attr/textAppearanceBodyMedium" android:layout_width="0dp"
android:textIsSelectable="true" android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginStart="16dp"
app:layout_constraintStart_toEndOf="@id/imageView_cover" android:layout_marginTop="4dp"
app:layout_constraintTop_toBottomOf="@id/textView_title" android:layout_marginEnd="16dp"
tools:text="@tools:sample/lorem[12]" /> android:ellipsize="end"
android:maxLines="3"
<ImageView android:textAppearance="?attr/textAppearanceBodyMedium"
android:id="@+id/imageView_state" android:textIsSelectable="true"
android:layout_width="0dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_height="0dp" app:layout_constraintStart_toEndOf="@id/imageView_cover"
android:layout_marginVertical="0.5dp" app:layout_constraintTop_toBottomOf="@id/textView_title"
android:layout_marginStart="16dp" tools:text="@tools:sample/lorem[12]" />
app:layout_constraintBottom_toBottomOf="@id/textView_state"
app:layout_constraintDimensionRatio="1" <ImageView
app:layout_constraintStart_toEndOf="@id/imageView_cover" android:id="@+id/imageView_state"
app:layout_constraintTop_toTopOf="@id/textView_state" android:layout_width="0dp"
tools:src="@drawable/ic_state_ongoing" /> android:layout_height="0dp"
android:layout_marginVertical="0.5dp"
<TextView android:layout_marginStart="16dp"
android:id="@+id/textView_state" app:layout_constraintBottom_toBottomOf="@id/textView_state"
android:layout_width="wrap_content" app:layout_constraintDimensionRatio="1"
android:layout_height="wrap_content" app:layout_constraintStart_toEndOf="@id/imageView_cover"
android:layout_marginStart="4dp" app:layout_constraintTop_toTopOf="@id/textView_state"
android:layout_marginTop="4dp" tools:src="@drawable/ic_state_ongoing" />
android:layout_marginEnd="16dp"
android:drawablePadding="4dp" <TextView
android:gravity="center_vertical" android:id="@+id/textView_state"
android:labelFor="@id/imageView_state" android:layout_width="wrap_content"
android:singleLine="true" android:layout_height="wrap_content"
android:textColor="?colorTertiary" android:layout_marginStart="4dp"
android:textStyle="bold" android:layout_marginTop="4dp"
app:drawableTint="?colorTertiary" android:layout_marginEnd="16dp"
app:layout_constrainedWidth="true" android:drawablePadding="4dp"
app:layout_constraintEnd_toEndOf="parent" android:gravity="center_vertical"
app:layout_constraintHorizontal_bias="0" android:labelFor="@id/imageView_state"
app:layout_constraintStart_toEndOf="@id/imageView_state" android:singleLine="true"
app:layout_constraintTop_toBottomOf="@id/textView_subtitle" android:textColor="?colorTertiary"
tools:text="@string/state_ongoing" /> android:textStyle="bold"
app:drawableTint="?colorTertiary"
<RatingBar app:layout_constrainedWidth="true"
android:id="@+id/rating_bar" app:layout_constraintEnd_toEndOf="parent"
style="?ratingBarStyleSmall" app:layout_constraintHorizontal_bias="0"
android:layout_width="wrap_content" app:layout_constraintStart_toEndOf="@id/imageView_state"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/textView_subtitle"
android:layout_marginStart="16dp" tools:text="@string/state_ongoing" />
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp" <RatingBar
android:isIndicator="true" android:id="@+id/rating_bar"
android:max="1" style="?ratingBarStyleSmall"
android:numStars="5" android:layout_width="wrap_content"
android:stepSize="0.5" android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginStart="16dp"
app:layout_constraintHorizontal_bias="0.0" android:layout_marginTop="4dp"
app:layout_constraintStart_toEndOf="@id/imageView_cover" android:layout_marginEnd="16dp"
app:layout_constraintTop_toBottomOf="@id/textView_state" android:isIndicator="true"
tools:rating="4" /> android:max="1"
android:numStars="5"
<androidx.constraintlayout.widget.Barrier android:stepSize="0.5"
android:id="@+id/barrier_header" app:layout_constraintEnd_toEndOf="parent"
android:layout_width="0dp" app:layout_constraintHorizontal_bias="0.0"
android:layout_height="wrap_content" app:layout_constraintStart_toEndOf="@id/imageView_cover"
app:barrierDirection="bottom" app:layout_constraintTop_toBottomOf="@id/textView_state"
app:barrierMargin="@dimen/margin_normal" tools:rating="4" />
app:constraint_referenced_ids="imageView_cover,rating_bar" />
<androidx.constraintlayout.widget.Barrier
<include android:id="@+id/barrier_header"
android:id="@+id/info_layout" android:layout_width="0dp"
layout="@layout/layout_details_chips" android:layout_height="wrap_content"
android:layout_width="0dp" app:barrierDirection="bottom"
android:layout_height="wrap_content" app:barrierMargin="@dimen/margin_normal"
android:layout_marginHorizontal="@dimen/screen_padding" app:constraint_referenced_ids="imageView_cover,rating_bar" />
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" <include
app:layout_constraintTop_toBottomOf="@id/barrier_header" /> android:id="@+id/info_layout"
layout="@layout/layout_details_chips"
<org.koitharu.kotatsu.core.ui.widgets.ProgressButton android:layout_width="0dp"
android:id="@+id/button_read" android:layout_height="wrap_content"
android:layout_width="0dp" android:layout_marginHorizontal="@dimen/screen_padding"
android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent"
android:layout_gravity="end|center_vertical" app:layout_constraintStart_toStartOf="parent"
android:layout_marginHorizontal="@dimen/screen_padding" app:layout_constraintTop_toBottomOf="@id/barrier_header" />
android:layout_marginTop="@dimen/margin_normal"
android:foreground="?selectableItemBackground" <org.koitharu.kotatsu.core.ui.widgets.ProgressButton
android:gravity="center" android:id="@+id/button_read"
android:paddingHorizontal="6dp" android:layout_width="0dp"
android:paddingVertical="8dp" android:layout_height="wrap_content"
android:textColor="?colorOnPrimaryContainer" android:layout_gravity="end|center_vertical"
app:baseColor="?colorSecondaryContainer" android:layout_marginHorizontal="@dimen/screen_padding"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="@dimen/margin_normal"
app:layout_constraintStart_toStartOf="parent" android:foreground="?selectableItemBackground"
app:layout_constraintTop_toBottomOf="@id/info_layout" android:gravity="center"
app:progressColor="?colorPrimary" android:paddingHorizontal="6dp"
app:subtitleTextAppearance="?textAppearanceBodySmall" android:paddingVertical="8dp"
app:titleTextAppearance="?textAppearanceButton" android:textColor="?colorOnPrimaryContainer"
tools:max="100" app:baseColor="?colorSecondaryContainer"
tools:progress="40" app:layout_constraintEnd_toEndOf="parent"
tools:subtitle="12 chapters" app:layout_constraintStart_toStartOf="parent"
tools:title="@string/read" /> app:layout_constraintTop_toBottomOf="@id/info_layout"
app:progressColor="?colorPrimary"
<TextView app:subtitleTextAppearance="?textAppearanceBodySmall"
android:id="@+id/textView_description_title" app:titleTextAppearance="?textAppearanceButton"
android:layout_width="0dp" tools:max="100"
android:layout_height="wrap_content" tools:progress="40"
android:layout_marginStart="@dimen/margin_small" tools:subtitle="12 chapters"
android:layout_marginTop="@dimen/margin_normal" tools:title="@string/read" />
android:gravity="center_vertical|start"
android:padding="@dimen/grid_spacing" <TextView
android:singleLine="true" android:id="@+id/textView_description_title"
android:text="@string/description" android:layout_width="0dp"
android:textAppearance="?textAppearanceTitleSmall" android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="@id/button_description_more" android:layout_marginStart="@dimen/margin_small"
app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="@dimen/margin_normal"
app:layout_constraintTop_toBottomOf="@id/button_read" /> android:gravity="center_vertical|start"
android:padding="@dimen/grid_spacing"
<Button android:singleLine="true"
android:id="@+id/button_description_more" android:text="@string/description"
style="@style/Widget.Kotatsu.Button.More" android:textAppearance="?textAppearanceTitleSmall"
android:layout_width="wrap_content" app:layout_constraintEnd_toStartOf="@id/button_description_more"
android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent"
android:layout_marginEnd="8dp" app:layout_constraintTop_toBottomOf="@id/button_read" />
android:text="@string/more"
app:layout_constraintBaseline_toBaselineOf="@id/textView_description_title" <Button
app:layout_constraintEnd_toEndOf="parent" /> android:id="@+id/button_description_more"
style="@style/Widget.Kotatsu.Button.More"
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView android:layout_width="wrap_content"
android:id="@+id/textView_description" android:layout_height="wrap_content"
android:layout_width="0dp" android:layout_marginEnd="8dp"
android:layout_height="wrap_content" android:text="@string/more"
android:layout_marginStart="@dimen/margin_normal" app:layout_constraintBaseline_toBaselineOf="@id/textView_description_title"
android:layout_marginTop="@dimen/margin_small" app:layout_constraintEnd_toEndOf="parent" />
android:layout_marginEnd="@dimen/margin_normal"
android:ellipsize="end" <org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
android:lineSpacingMultiplier="1.2" android:id="@+id/textView_description"
android:maxLines="@integer/details_description_lines" android:layout_width="0dp"
android:textAppearance="?attr/textAppearanceBodyMedium" android:layout_height="wrap_content"
android:textIsSelectable="true" android:layout_marginStart="@dimen/margin_normal"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="@dimen/margin_small"
app:layout_constraintStart_toStartOf="parent" android:layout_marginEnd="@dimen/margin_normal"
app:layout_constraintTop_toBottomOf="@id/textView_description_title" android:ellipsize="end"
tools:ignore="UnusedAttribute" android:lineSpacingMultiplier="1.2"
tools:text="@tools:sample/lorem/random" /> android:maxLines="@integer/details_description_lines"
android:textAppearance="?attr/textAppearanceBodyMedium"
<org.koitharu.kotatsu.core.ui.widgets.ChipsView android:textIsSelectable="true"
android:id="@+id/chips_tags" app:layout_constraintEnd_toEndOf="parent"
android:layout_width="0dp" app:layout_constraintStart_toStartOf="parent"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/textView_description_title"
android:layout_marginHorizontal="@dimen/screen_padding" tools:ignore="UnusedAttribute"
android:layout_marginTop="@dimen/margin_small" tools:text="@tools:sample/lorem/random" />
app:chipSpacingHorizontal="6dp"
app:chipSpacingVertical="6dp" <org.koitharu.kotatsu.core.ui.widgets.ChipsView
app:layout_constraintEnd_toEndOf="parent" android:id="@+id/chips_tags"
app:layout_constraintStart_toStartOf="parent" android:layout_width="0dp"
app:layout_constraintTop_toBottomOf="@+id/textView_description" /> android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/screen_padding"
<TextView android:layout_marginTop="@dimen/margin_small"
android:id="@+id/textView_scrobbling_title" app:chipSpacingHorizontal="6dp"
android:layout_width="0dp" app:chipSpacingVertical="6dp"
android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginStart="@dimen/margin_small" app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="@dimen/margin_normal" app:layout_constraintTop_toBottomOf="@+id/textView_description" />
android:layout_weight="1"
android:gravity="center_vertical|start" <TextView
android:padding="@dimen/grid_spacing" android:id="@+id/textView_scrobbling_title"
android:singleLine="true" android:layout_width="0dp"
android:text="@string/tracking" android:layout_height="wrap_content"
android:textAppearance="?textAppearanceTitleSmall" android:layout_marginStart="@dimen/margin_small"
app:layout_constraintEnd_toStartOf="@id/button_scrobbling_more" android:layout_marginTop="@dimen/margin_normal"
app:layout_constraintStart_toStartOf="parent" android:layout_weight="1"
app:layout_constraintTop_toBottomOf="@id/chips_tags" /> android:gravity="center_vertical|start"
android:padding="@dimen/grid_spacing"
<Button android:singleLine="true"
android:id="@+id/button_scrobbling_more" android:text="@string/tracking"
style="@style/Widget.Kotatsu.Button.More" android:textAppearance="?textAppearanceTitleSmall"
android:layout_width="wrap_content" app:layout_constraintEnd_toStartOf="@id/button_scrobbling_more"
android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent"
android:layout_marginEnd="8dp" app:layout_constraintTop_toBottomOf="@id/chips_tags" />
android:text="@string/manage"
app:layout_constraintBaseline_toBaselineOf="@id/textView_scrobbling_title" <Button
app:layout_constraintEnd_toEndOf="parent" /> android:id="@+id/button_scrobbling_more"
style="@style/Widget.Kotatsu.Button.More"
<androidx.recyclerview.widget.RecyclerView android:layout_width="wrap_content"
android:id="@+id/recyclerView_scrobbling" android:layout_height="wrap_content"
android:layout_width="0dp" android:layout_marginEnd="8dp"
android:layout_height="wrap_content" android:text="@string/manage"
android:layout_marginStart="16dp" app:layout_constraintBaseline_toBaselineOf="@id/textView_scrobbling_title"
android:layout_marginEnd="16dp" app:layout_constraintEnd_toEndOf="parent" />
android:layout_marginBottom="12dp"
android:nestedScrollingEnabled="false" <androidx.recyclerview.widget.RecyclerView
android:orientation="vertical" android:id="@+id/recyclerView_scrobbling"
android:overScrollMode="never" android:layout_width="0dp"
android:scrollbars="none" android:layout_height="wrap_content"
android:visibility="gone" android:layout_marginStart="16dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginBottom="12dp"
app:layout_constraintStart_toStartOf="parent" android:nestedScrollingEnabled="false"
app:layout_constraintTop_toBottomOf="@id/textView_scrobbling_title" android:orientation="vertical"
tools:itemCount="2" android:overScrollMode="never"
tools:listitem="@layout/item_scrobbling_info" android:scrollbars="none"
tools:visibility="visible" /> android:visibility="gone"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
<com.google.android.material.progressindicator.LinearProgressIndicator app:layout_constraintEnd_toEndOf="parent"
android:id="@+id/progressBar" app:layout_constraintStart_toStartOf="parent"
android:layout_width="0dp" app:layout_constraintTop_toBottomOf="@id/textView_scrobbling_title"
android:layout_height="wrap_content" tools:itemCount="2"
android:indeterminate="true" tools:listitem="@layout/item_scrobbling_info"
android:visibility="gone" tools:visibility="visible" />
app:hideAnimationBehavior="outward"
app:layout_constraintBottom_toTopOf="parent" <com.google.android.material.progressindicator.LinearProgressIndicator
app:layout_constraintEnd_toEndOf="parent" android:id="@+id/progressBar"
app:layout_constraintStart_toStartOf="parent" android:layout_width="0dp"
app:layout_constraintTop_toTopOf="parent" android:layout_height="wrap_content"
app:showAnimationBehavior="inward" android:indeterminate="true"
app:trackCornerRadius="0dp" android:visibility="gone"
tools:visibility="visible" /> app:hideAnimationBehavior="outward"
app:layout_constraintBottom_toTopOf="parent"
<androidx.constraintlayout.widget.Group app:layout_constraintEnd_toEndOf="parent"
android:id="@+id/group_scrobbling" app:layout_constraintStart_toStartOf="parent"
android:layout_width="wrap_content" app:layout_constraintTop_toTopOf="parent"
android:layout_height="wrap_content" app:showAnimationBehavior="inward"
android:visibility="gone" app:trackCornerRadius="0dp"
app:constraint_referenced_ids="recyclerView_scrobbling,textView_scrobbling_title,button_scrobbling_more" tools:visibility="visible" />
tools:visibility="visible" />
<androidx.constraintlayout.widget.Group
<TextView android:id="@+id/group_scrobbling"
android:id="@+id/textView_related_title" android:layout_width="wrap_content"
android:layout_width="0dp" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:visibility="gone"
android:layout_marginStart="@dimen/margin_small" app:constraint_referenced_ids="recyclerView_scrobbling,textView_scrobbling_title,button_scrobbling_more"
android:layout_marginTop="@dimen/margin_small" tools:visibility="visible" />
android:layout_weight="1"
android:gravity="center_vertical|start" <TextView
android:padding="@dimen/grid_spacing" android:id="@+id/textView_related_title"
android:singleLine="true" android:layout_width="0dp"
android:text="@string/related_manga" android:layout_height="wrap_content"
android:textAppearance="?textAppearanceTitleSmall" android:layout_marginStart="@dimen/margin_small"
app:layout_constraintEnd_toStartOf="@id/button_related_more" android:layout_marginTop="@dimen/margin_small"
app:layout_constraintStart_toStartOf="parent" android:layout_weight="1"
app:layout_constraintTop_toBottomOf="@id/recyclerView_scrobbling" /> android:gravity="center_vertical|start"
android:padding="@dimen/grid_spacing"
<Button android:singleLine="true"
android:id="@+id/button_related_more" android:text="@string/related_manga"
style="@style/Widget.Kotatsu.Button.More" android:textAppearance="?textAppearanceTitleSmall"
android:layout_width="wrap_content" app:layout_constraintEnd_toStartOf="@id/button_related_more"
android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent"
android:layout_marginEnd="8dp" app:layout_constraintTop_toBottomOf="@id/recyclerView_scrobbling" />
android:text="@string/show_all"
android:visibility="gone" <Button
app:layout_constraintBaseline_toBaselineOf="@id/textView_related_title" android:id="@+id/button_related_more"
app:layout_constraintEnd_toEndOf="parent" /> style="@style/Widget.Kotatsu.Button.More"
android:layout_width="wrap_content"
<androidx.recyclerview.widget.RecyclerView android:layout_height="wrap_content"
android:id="@+id/recyclerView_related" android:layout_marginEnd="8dp"
android:layout_width="0dp" android:text="@string/show_all"
android:layout_height="wrap_content" android:visibility="gone"
android:layout_marginBottom="6dp" app:layout_constraintBaseline_toBaselineOf="@id/textView_related_title"
android:clipToPadding="false" app:layout_constraintEnd_toEndOf="parent" />
android:nestedScrollingEnabled="false"
android:orientation="horizontal" <androidx.recyclerview.widget.RecyclerView
android:paddingStart="12dp" android:id="@+id/recyclerView_related"
android:paddingEnd="12dp" android:layout_width="0dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginBottom="6dp"
app:layout_constraintStart_toStartOf="parent" android:clipToPadding="false"
app:layout_constraintTop_toBottomOf="@id/textView_related_title" android:nestedScrollingEnabled="false"
tools:listitem="@layout/item_manga_grid" /> android:orientation="horizontal"
android:paddingStart="12dp"
<androidx.constraintlayout.widget.Group android:paddingEnd="12dp"
android:id="@+id/group_related" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_width="wrap_content" app:layout_constraintEnd_toEndOf="parent"
android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent"
android:visibility="gone" app:layout_constraintTop_toBottomOf="@id/textView_related_title"
app:constraint_referenced_ids="recyclerView_related,textView_related_title,button_related_more" tools:listitem="@layout/item_manga_grid" />
tools:visibility="visible" />
<androidx.constraintlayout.widget.Group
</androidx.constraintlayout.widget.ConstraintLayout> android:id="@+id/group_related"
android:layout_width="wrap_content"
</androidx.core.widget.NestedScrollView> 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.swiperefreshlayout.widget.SwipeRefreshLayout>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:id="@+id/card_chapters" android:id="@+id/card_chapters"

@ -26,375 +26,381 @@
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/scrollView" android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false"
android:scrollIndicators="top"
android:scrollbars="vertical"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.core.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingBottom="@dimen/details_bs_peek_height"> android:clipToPadding="false"
android:scrollIndicators="top"
<com.google.android.material.imageview.ShapeableImageView android:scrollbars="vertical">
android:id="@+id/imageView_cover"
android:layout_width="0dp" <androidx.constraintlayout.widget.ConstraintLayout
android:layout_height="0dp" android:layout_width="match_parent"
android:layout_marginStart="16dp" android:layout_height="match_parent"
android:layout_marginTop="16dp" android:paddingBottom="@dimen/details_bs_peek_height">
android:background="?colorSecondaryContainer"
android:foreground="?selectableItemBackground" <com.google.android.material.imageview.ShapeableImageView
android:scaleType="centerCrop" android:id="@+id/imageView_cover"
app:layout_constraintDimensionRatio="H,13:18" android:layout_width="0dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_height="0dp"
app:layout_constraintHorizontal_bias="0" android:layout_marginStart="16dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent" android:background="?colorSecondaryContainer"
app:layout_constraintWidth_percent="0.3" android:foreground="?selectableItemBackground"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover" android:scaleType="centerCrop"
tools:background="@tools:sample/backgrounds/scenic[5]" app:layout_constraintDimensionRatio="H,13:18"
tools:ignore="ContentDescription,UnusedAttribute" /> app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
<TextView app:layout_constraintStart_toStartOf="parent"
android:id="@+id/textView_nsfw" app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content" app:layout_constraintWidth_percent="0.3"
android:layout_height="wrap_content" app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
android:layout_margin="@dimen/card_indicator_offset" tools:background="@tools:sample/backgrounds/scenic[5]"
android:background="@drawable/bg_chip" tools:ignore="ContentDescription,UnusedAttribute" />
android:backgroundTint="@color/warning"
android:gravity="center" <TextView
android:paddingHorizontal="4dp" android:id="@+id/textView_nsfw"
android:paddingVertical="2dp" android:layout_width="wrap_content"
android:text="@string/nsfw" android:layout_height="wrap_content"
android:textAlignment="center" android:layout_margin="@dimen/card_indicator_offset"
android:textAppearance="?textAppearanceLabelMedium" android:background="@drawable/bg_chip"
android:textColor="?colorOnError" android:backgroundTint="@color/warning"
app:layout_constraintBottom_toBottomOf="@id/imageView_cover" android:gravity="center"
app:layout_constraintEnd_toEndOf="@id/imageView_cover" /> android:paddingHorizontal="4dp"
android:paddingVertical="2dp"
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView android:text="@string/nsfw"
android:id="@+id/textView_title" android:textAlignment="center"
android:layout_width="0dp" android:textAppearance="?textAppearanceLabelMedium"
android:layout_height="wrap_content" android:textColor="?colorOnError"
android:layout_marginStart="16dp" app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
android:layout_marginTop="16dp" app:layout_constraintEnd_toEndOf="@id/imageView_cover" />
android:layout_marginEnd="16dp"
android:ellipsize="end" <org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
android:maxLines="@integer/details_title_lines" android:id="@+id/textView_title"
android:textAppearance="?attr/textAppearanceHeadlineSmall" android:layout_width="0dp"
android:textIsSelectable="true" android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginStart="16dp"
app:layout_constraintStart_toEndOf="@id/imageView_cover" android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent" android:layout_marginEnd="16dp"
tools:text="@tools:sample/lorem" /> android:ellipsize="end"
android:maxLines="@integer/details_title_lines"
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView android:textAppearance="?attr/textAppearanceHeadlineSmall"
android:id="@+id/textView_subtitle" android:textIsSelectable="true"
android:layout_width="0dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_height="wrap_content" app:layout_constraintStart_toEndOf="@id/imageView_cover"
android:layout_marginStart="16dp" app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="4dp" tools:text="@tools:sample/lorem" />
android:layout_marginEnd="16dp"
android:ellipsize="end" <org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
android:maxLines="3" android:id="@+id/textView_subtitle"
android:textAppearance="?attr/textAppearanceBodyMedium" android:layout_width="0dp"
android:textIsSelectable="true" android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginStart="16dp"
app:layout_constraintStart_toEndOf="@id/imageView_cover" android:layout_marginTop="4dp"
app:layout_constraintTop_toBottomOf="@id/textView_title" android:layout_marginEnd="16dp"
tools:text="@tools:sample/lorem[12]" /> android:ellipsize="end"
android:maxLines="3"
<ImageView android:textAppearance="?attr/textAppearanceBodyMedium"
android:id="@+id/imageView_state" android:textIsSelectable="true"
android:layout_width="0dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_height="0dp" app:layout_constraintStart_toEndOf="@id/imageView_cover"
android:layout_marginVertical="0.5dp" app:layout_constraintTop_toBottomOf="@id/textView_title"
android:layout_marginStart="16dp" tools:text="@tools:sample/lorem[12]" />
app:layout_constraintBottom_toBottomOf="@id/textView_state"
app:layout_constraintDimensionRatio="1" <ImageView
app:layout_constraintStart_toEndOf="@id/imageView_cover" android:id="@+id/imageView_state"
app:layout_constraintTop_toTopOf="@id/textView_state" android:layout_width="0dp"
app:tint="?colorTertiary" android:layout_height="0dp"
tools:src="@drawable/ic_state_ongoing" /> android:layout_marginVertical="0.5dp"
android:layout_marginStart="16dp"
<TextView app:layout_constraintBottom_toBottomOf="@id/textView_state"
android:id="@+id/textView_state" app:layout_constraintDimensionRatio="1"
android:layout_width="wrap_content" app:layout_constraintStart_toEndOf="@id/imageView_cover"
android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="@id/textView_state"
android:layout_marginStart="4dp" app:tint="?colorTertiary"
android:layout_marginTop="4dp" tools:src="@drawable/ic_state_ongoing" />
android:layout_marginEnd="16dp"
android:drawablePadding="4dp" <TextView
android:gravity="center_vertical" android:id="@+id/textView_state"
android:labelFor="@id/imageView_state" android:layout_width="wrap_content"
android:singleLine="true" android:layout_height="wrap_content"
android:textColor="?colorTertiary" android:layout_marginStart="4dp"
android:textStyle="bold" android:layout_marginTop="4dp"
app:drawableTint="?colorTertiary" android:layout_marginEnd="16dp"
app:layout_constrainedWidth="true" android:drawablePadding="4dp"
app:layout_constraintEnd_toEndOf="parent" android:gravity="center_vertical"
app:layout_constraintHorizontal_bias="0" android:labelFor="@id/imageView_state"
app:layout_constraintStart_toEndOf="@id/imageView_state" android:singleLine="true"
app:layout_constraintTop_toBottomOf="@id/textView_subtitle" android:textColor="?colorTertiary"
tools:text="@string/state_ongoing" /> android:textStyle="bold"
app:drawableTint="?colorTertiary"
<RatingBar app:layout_constrainedWidth="true"
android:id="@+id/rating_bar" app:layout_constraintEnd_toEndOf="parent"
style="?ratingBarStyleSmall" app:layout_constraintHorizontal_bias="0"
android:layout_width="wrap_content" app:layout_constraintStart_toEndOf="@id/imageView_state"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/textView_subtitle"
android:layout_marginStart="16dp" tools:text="@string/state_ongoing" />
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp" <RatingBar
android:isIndicator="true" android:id="@+id/rating_bar"
android:max="1" style="?ratingBarStyleSmall"
android:numStars="5" android:layout_width="wrap_content"
android:stepSize="0.5" android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginStart="16dp"
app:layout_constraintHorizontal_bias="0.0" android:layout_marginTop="4dp"
app:layout_constraintStart_toEndOf="@id/imageView_cover" android:layout_marginEnd="16dp"
app:layout_constraintTop_toBottomOf="@id/textView_state" android:isIndicator="true"
tools:rating="4" /> android:max="1"
android:numStars="5"
<androidx.constraintlayout.widget.Barrier android:stepSize="0.5"
android:id="@+id/barrier_header" app:layout_constraintEnd_toEndOf="parent"
android:layout_width="0dp" app:layout_constraintHorizontal_bias="0.0"
android:layout_height="wrap_content" app:layout_constraintStart_toEndOf="@id/imageView_cover"
app:barrierDirection="bottom" app:layout_constraintTop_toBottomOf="@id/textView_state"
app:barrierMargin="@dimen/margin_normal" tools:rating="4" />
app:constraint_referenced_ids="imageView_cover,rating_bar" />
<androidx.constraintlayout.widget.Barrier
<include android:id="@+id/barrier_header"
android:id="@+id/info_layout" android:layout_width="0dp"
layout="@layout/layout_details_chips" android:layout_height="wrap_content"
android:layout_width="0dp" app:barrierDirection="bottom"
android:layout_height="wrap_content" app:barrierMargin="@dimen/margin_normal"
android:layout_marginHorizontal="@dimen/screen_padding" app:constraint_referenced_ids="imageView_cover,rating_bar" />
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" <include
app:layout_constraintTop_toBottomOf="@id/barrier_header" /> android:id="@+id/info_layout"
layout="@layout/layout_details_chips"
<org.koitharu.kotatsu.core.ui.widgets.ProgressButton android:layout_width="0dp"
android:id="@+id/button_read" android:layout_height="wrap_content"
android:layout_width="0dp" android:layout_marginHorizontal="@dimen/screen_padding"
android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginStart="16dp" app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="@dimen/margin_normal" app:layout_constraintTop_toBottomOf="@id/barrier_header" />
android:layout_marginEnd="12dp"
android:foreground="?selectableItemBackground" <org.koitharu.kotatsu.core.ui.widgets.ProgressButton
android:gravity="center" android:id="@+id/button_read"
android:paddingHorizontal="6dp" android:layout_width="0dp"
android:paddingVertical="8dp" android:layout_height="wrap_content"
app:baseColor="?colorSecondaryContainer" android:layout_marginStart="16dp"
app:layout_constraintEnd_toStartOf="@id/button_download" android:layout_marginTop="@dimen/margin_normal"
app:layout_constraintStart_toStartOf="parent" android:layout_marginEnd="12dp"
app:layout_constraintTop_toBottomOf="@id/info_layout" android:foreground="?selectableItemBackground"
app:progressColor="?colorPrimary" android:gravity="center"
app:subtitleTextAppearance="?textAppearanceBodySmall" android:paddingHorizontal="6dp"
app:titleTextAppearance="?textAppearanceButton" android:paddingVertical="8dp"
tools:max="100" app:baseColor="?colorSecondaryContainer"
tools:progress="40" app:layout_constraintEnd_toStartOf="@id/button_download"
tools:subtitle="12 chapters" app:layout_constraintStart_toStartOf="parent"
tools:title="@string/read" /> app:layout_constraintTop_toBottomOf="@id/info_layout"
app:progressColor="?colorPrimary"
<ImageView app:subtitleTextAppearance="?textAppearanceBodySmall"
android:id="@+id/button_download" app:titleTextAppearance="?textAppearanceButton"
android:layout_width="0dp" tools:max="100"
android:layout_height="0dp" tools:progress="40"
android:layout_marginEnd="16dp" tools:subtitle="12 chapters"
android:background="@drawable/bg_circle_button" tools:title="@string/read" />
android:backgroundTint="?colorSecondaryContainer"
android:contentDescription="@string/download" <ImageView
android:scaleType="centerInside" android:id="@+id/button_download"
app:layout_constraintBottom_toBottomOf="@id/button_read" android:layout_width="0dp"
app:layout_constraintDimensionRatio="1" android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="16dp"
app:layout_constraintTop_toTopOf="@id/button_read" android:background="@drawable/bg_circle_button"
app:srcCompat="@drawable/ic_download" /> android:backgroundTint="?colorSecondaryContainer"
android:contentDescription="@string/download"
<TextView android:scaleType="centerInside"
android:id="@+id/textView_description_title" app:layout_constraintBottom_toBottomOf="@id/button_read"
android:layout_width="0dp" app:layout_constraintDimensionRatio="1"
android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginStart="@dimen/margin_small" app:layout_constraintTop_toTopOf="@id/button_read"
android:layout_marginTop="@dimen/margin_normal" app:srcCompat="@drawable/ic_download" />
android:layout_weight="1"
android:gravity="center_vertical|start" <TextView
android:padding="@dimen/grid_spacing" android:id="@+id/textView_description_title"
android:singleLine="true" android:layout_width="0dp"
android:text="@string/description" android:layout_height="wrap_content"
android:textAppearance="?textAppearanceTitleSmall" android:layout_marginStart="@dimen/margin_small"
app:layout_constraintEnd_toStartOf="@id/button_description_more" android:layout_marginTop="@dimen/margin_normal"
app:layout_constraintStart_toStartOf="parent" android:layout_weight="1"
app:layout_constraintTop_toBottomOf="@id/button_read" /> android:gravity="center_vertical|start"
android:padding="@dimen/grid_spacing"
<Button android:singleLine="true"
android:id="@+id/button_description_more" android:text="@string/description"
style="@style/Widget.Kotatsu.Button.More" android:textAppearance="?textAppearanceTitleSmall"
android:layout_width="wrap_content" app:layout_constraintEnd_toStartOf="@id/button_description_more"
android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent"
android:layout_marginEnd="8dp" app:layout_constraintTop_toBottomOf="@id/button_read" />
android:text="@string/more"
app:layout_constraintBaseline_toBaselineOf="@id/textView_description_title" <Button
app:layout_constraintEnd_toEndOf="parent" /> android:id="@+id/button_description_more"
style="@style/Widget.Kotatsu.Button.More"
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView android:layout_width="wrap_content"
android:id="@+id/textView_description" android:layout_height="wrap_content"
android:layout_width="0dp" android:layout_marginEnd="8dp"
android:layout_height="wrap_content" android:text="@string/more"
android:layout_marginStart="@dimen/margin_normal" app:layout_constraintBaseline_toBaselineOf="@id/textView_description_title"
android:layout_marginTop="@dimen/margin_small" app:layout_constraintEnd_toEndOf="parent" />
android:layout_marginEnd="@dimen/margin_normal"
android:ellipsize="end" <org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
android:lineSpacingMultiplier="1.2" android:id="@+id/textView_description"
android:maxLines="@integer/details_description_lines" android:layout_width="0dp"
android:textAppearance="?attr/textAppearanceBodyMedium" android:layout_height="wrap_content"
android:textIsSelectable="true" android:layout_marginStart="@dimen/margin_normal"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="@dimen/margin_small"
app:layout_constraintStart_toStartOf="parent" android:layout_marginEnd="@dimen/margin_normal"
app:layout_constraintTop_toBottomOf="@id/textView_description_title" android:ellipsize="end"
tools:ignore="UnusedAttribute" android:lineSpacingMultiplier="1.2"
tools:text="@tools:sample/lorem/random" /> android:maxLines="@integer/details_description_lines"
android:textAppearance="?attr/textAppearanceBodyMedium"
<org.koitharu.kotatsu.core.ui.widgets.ChipsView android:textIsSelectable="true"
android:id="@+id/chips_tags" app:layout_constraintEnd_toEndOf="parent"
android:layout_width="0dp" app:layout_constraintStart_toStartOf="parent"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/textView_description_title"
android:layout_marginHorizontal="@dimen/screen_padding" tools:ignore="UnusedAttribute"
android:layout_marginTop="@dimen/margin_normal" tools:text="@tools:sample/lorem/random" />
app:chipSpacingHorizontal="6dp"
app:chipSpacingVertical="6dp" <org.koitharu.kotatsu.core.ui.widgets.ChipsView
app:layout_constraintEnd_toEndOf="parent" android:id="@+id/chips_tags"
app:layout_constraintStart_toStartOf="parent" android:layout_width="0dp"
app:layout_constraintTop_toBottomOf="@+id/textView_description" /> android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/screen_padding"
<TextView android:layout_marginTop="@dimen/margin_normal"
android:id="@+id/textView_scrobbling_title" app:chipSpacingHorizontal="6dp"
android:layout_width="0dp" app:chipSpacingVertical="6dp"
android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginStart="@dimen/margin_small" app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="@dimen/margin_normal" app:layout_constraintTop_toBottomOf="@+id/textView_description" />
android:layout_weight="1"
android:gravity="center_vertical|start" <TextView
android:padding="@dimen/grid_spacing" android:id="@+id/textView_scrobbling_title"
android:singleLine="true" android:layout_width="0dp"
android:text="@string/tracking" android:layout_height="wrap_content"
android:textAppearance="?textAppearanceTitleSmall" android:layout_marginStart="@dimen/margin_small"
app:layout_constraintEnd_toStartOf="@id/button_scrobbling_more" android:layout_marginTop="@dimen/margin_normal"
app:layout_constraintStart_toStartOf="parent" android:layout_weight="1"
app:layout_constraintTop_toBottomOf="@id/chips_tags" /> android:gravity="center_vertical|start"
android:padding="@dimen/grid_spacing"
<Button android:singleLine="true"
android:id="@+id/button_scrobbling_more" android:text="@string/tracking"
style="@style/Widget.Kotatsu.Button.More" android:textAppearance="?textAppearanceTitleSmall"
android:layout_width="wrap_content" app:layout_constraintEnd_toStartOf="@id/button_scrobbling_more"
android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent"
android:layout_marginEnd="8dp" app:layout_constraintTop_toBottomOf="@id/chips_tags" />
android:text="@string/manage"
app:layout_constraintBaseline_toBaselineOf="@id/textView_scrobbling_title" <Button
app:layout_constraintEnd_toEndOf="parent" /> android:id="@+id/button_scrobbling_more"
style="@style/Widget.Kotatsu.Button.More"
<androidx.recyclerview.widget.RecyclerView android:layout_width="wrap_content"
android:id="@+id/recyclerView_scrobbling" android:layout_height="wrap_content"
android:layout_width="0dp" android:layout_marginEnd="8dp"
android:layout_height="wrap_content" android:text="@string/manage"
android:layout_marginStart="16dp" app:layout_constraintBaseline_toBaselineOf="@id/textView_scrobbling_title"
android:layout_marginEnd="16dp" app:layout_constraintEnd_toEndOf="parent" />
android:layout_marginBottom="12dp"
android:nestedScrollingEnabled="false" <androidx.recyclerview.widget.RecyclerView
android:orientation="vertical" android:id="@+id/recyclerView_scrobbling"
android:overScrollMode="never" android:layout_width="0dp"
android:scrollbars="none" android:layout_height="wrap_content"
android:visibility="gone" android:layout_marginStart="16dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginBottom="12dp"
app:layout_constraintStart_toStartOf="parent" android:nestedScrollingEnabled="false"
app:layout_constraintTop_toBottomOf="@id/textView_scrobbling_title" android:orientation="vertical"
tools:itemCount="2" android:overScrollMode="never"
tools:listitem="@layout/item_scrobbling_info" android:scrollbars="none"
tools:visibility="visible" /> android:visibility="gone"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
<com.google.android.material.progressindicator.LinearProgressIndicator app:layout_constraintEnd_toEndOf="parent"
android:id="@+id/progressBar" app:layout_constraintStart_toStartOf="parent"
android:layout_width="0dp" app:layout_constraintTop_toBottomOf="@id/textView_scrobbling_title"
android:layout_height="wrap_content" tools:itemCount="2"
android:indeterminate="true" tools:listitem="@layout/item_scrobbling_info"
android:visibility="gone" tools:visibility="visible" />
app:hideAnimationBehavior="outward"
app:layout_constraintBottom_toTopOf="parent" <com.google.android.material.progressindicator.LinearProgressIndicator
app:layout_constraintEnd_toEndOf="parent" android:id="@+id/progressBar"
app:layout_constraintStart_toStartOf="parent" android:layout_width="0dp"
app:layout_constraintTop_toTopOf="parent" android:layout_height="wrap_content"
app:showAnimationBehavior="inward" android:indeterminate="true"
app:trackCornerRadius="0dp" android:visibility="gone"
tools:visibility="visible" /> app:hideAnimationBehavior="outward"
app:layout_constraintBottom_toTopOf="parent"
<androidx.constraintlayout.widget.Group app:layout_constraintEnd_toEndOf="parent"
android:id="@+id/group_scrobbling" app:layout_constraintStart_toStartOf="parent"
android:layout_width="wrap_content" app:layout_constraintTop_toTopOf="parent"
android:layout_height="wrap_content" app:showAnimationBehavior="inward"
android:visibility="gone" app:trackCornerRadius="0dp"
app:constraint_referenced_ids="recyclerView_scrobbling,textView_scrobbling_title,button_scrobbling_more" tools:visibility="visible" />
tools:visibility="visible" />
<androidx.constraintlayout.widget.Group
<TextView android:id="@+id/group_scrobbling"
android:id="@+id/textView_related_title" android:layout_width="wrap_content"
android:layout_width="0dp" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:visibility="gone"
android:layout_marginStart="@dimen/margin_small" app:constraint_referenced_ids="recyclerView_scrobbling,textView_scrobbling_title,button_scrobbling_more"
android:layout_marginTop="@dimen/margin_small" tools:visibility="visible" />
android:layout_weight="1"
android:gravity="center_vertical|start" <TextView
android:padding="@dimen/grid_spacing" android:id="@+id/textView_related_title"
android:singleLine="true" android:layout_width="0dp"
android:text="@string/related_manga" android:layout_height="wrap_content"
android:textAppearance="?textAppearanceTitleSmall" android:layout_marginStart="@dimen/margin_small"
app:layout_constraintEnd_toStartOf="@id/button_related_more" android:layout_marginTop="@dimen/margin_small"
app:layout_constraintStart_toStartOf="parent" android:layout_weight="1"
app:layout_constraintTop_toBottomOf="@id/recyclerView_scrobbling" /> android:gravity="center_vertical|start"
android:padding="@dimen/grid_spacing"
<Button android:singleLine="true"
android:id="@+id/button_related_more" android:text="@string/related_manga"
style="@style/Widget.Kotatsu.Button.More" android:textAppearance="?textAppearanceTitleSmall"
android:layout_width="wrap_content" app:layout_constraintEnd_toStartOf="@id/button_related_more"
android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent"
android:layout_marginEnd="8dp" app:layout_constraintTop_toBottomOf="@id/recyclerView_scrobbling" />
android:text="@string/show_all"
android:visibility="gone" <Button
app:layout_constraintBaseline_toBaselineOf="@id/textView_related_title" android:id="@+id/button_related_more"
app:layout_constraintEnd_toEndOf="parent" /> style="@style/Widget.Kotatsu.Button.More"
android:layout_width="wrap_content"
<androidx.recyclerview.widget.RecyclerView android:layout_height="wrap_content"
android:id="@+id/recyclerView_related" android:layout_marginEnd="8dp"
android:layout_width="0dp" android:text="@string/show_all"
android:layout_height="wrap_content" android:visibility="gone"
android:layout_marginBottom="6dp" app:layout_constraintBaseline_toBaselineOf="@id/textView_related_title"
android:clipToPadding="false" app:layout_constraintEnd_toEndOf="parent" />
android:nestedScrollingEnabled="false"
android:orientation="horizontal" <androidx.recyclerview.widget.RecyclerView
android:paddingStart="12dp" android:id="@+id/recyclerView_related"
android:paddingEnd="12dp" android:layout_width="0dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginBottom="6dp"
app:layout_constraintStart_toStartOf="parent" android:clipToPadding="false"
app:layout_constraintTop_toBottomOf="@id/textView_related_title" android:nestedScrollingEnabled="false"
tools:listitem="@layout/item_manga_grid" /> android:orientation="horizontal"
android:paddingStart="12dp"
<androidx.constraintlayout.widget.Group android:paddingEnd="12dp"
android:id="@+id/group_related" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_width="wrap_content" app:layout_constraintEnd_toEndOf="parent"
android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent"
android:visibility="gone" app:layout_constraintTop_toBottomOf="@id/textView_related_title"
app:constraint_referenced_ids="recyclerView_related,textView_related_title,button_related_more" tools:listitem="@layout/item_manga_grid" />
tools:visibility="visible" />
<androidx.constraintlayout.widget.Group
</androidx.constraintlayout.widget.ConstraintLayout> android:id="@+id/group_related"
android:layout_width="wrap_content"
</androidx.core.widget.NestedScrollView> 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.swiperefreshlayout.widget.SwipeRefreshLayout>
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:id="@+id/container_bottom_sheet" android:id="@+id/container_bottom_sheet"

@ -36,7 +36,7 @@ recyclerview = "1.3.2"
room = "2.6.1" room = "2.6.1"
rules = "1.6.1" rules = "1.6.1"
runner = "1.6.2" runner = "1.6.2"
ssiv = "d1d10a6975" ssiv = "ba48c29803"
swiperefreshlayout = "1.1.0" swiperefreshlayout = "1.1.0"
transition = "1.5.1" transition = "1.5.1"
viewpager2 = "1.1.0" viewpager2 = "1.1.0"

Loading…
Cancel
Save