Details ui updates

This commit is contained in:
Koitharu
2024-11-25 09:32:36 +02:00
parent 2c2db1ca96
commit 8c79df3d35
21 changed files with 746 additions and 387 deletions

View File

@@ -18,8 +18,8 @@ android {
applicationId 'org.koitharu.kotatsu'
minSdk = 21
targetSdk = 35
versionCode = 693
versionName = '7.7.1'
versionCode = 700
versionName = '8.0-a1'
generatedDensities = []
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
ksp {

View File

@@ -6,11 +6,17 @@ import android.graphics.Canvas
import android.graphics.drawable.Animatable
import androidx.annotation.StyleRes
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import coil3.Image
import coil3.asImage
import coil3.getExtra
import coil3.request.ImageRequest
import com.google.android.material.animation.ArgbEvaluatorCompat
import com.google.android.material.color.MaterialColors
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.getTitle
import org.koitharu.kotatsu.core.util.KotatsuColors
import org.koitharu.kotatsu.core.util.ext.getAnimationDuration
import org.koitharu.kotatsu.core.util.ext.mangaSourceKey
import kotlin.math.abs
class AnimatedFaviconDrawable(
@@ -69,4 +75,16 @@ class AnimatedFaviconDrawable(
colorForeground = ArgbEvaluatorCompat.getInstance()
.evaluate(interpolator.getInterpolation(fraction), colorLow, colorHigh)
}
class Factory(
@StyleRes private val styleResId: Int,
) : ((ImageRequest) -> Image?) {
override fun invoke(request: ImageRequest): Image? {
val source = request.getExtra(mangaSourceKey) ?: return null
val context = request.context
val title = source.getTitle(context)
return AnimatedFaviconDrawable(context, styleResId, title).asImage()
}
}
}

View File

@@ -13,9 +13,15 @@ import android.graphics.drawable.Drawable
import androidx.annotation.StyleRes
import androidx.core.content.withStyledAttributes
import androidx.core.graphics.withClip
import coil3.Image
import coil3.asImage
import coil3.getExtra
import coil3.request.ImageRequest
import com.google.android.material.color.MaterialColors
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.getTitle
import org.koitharu.kotatsu.core.util.KotatsuColors
import org.koitharu.kotatsu.core.util.ext.mangaSourceKey
open class FaviconDrawable(
context: Context,
@@ -29,6 +35,7 @@ open class FaviconDrawable(
private var colorStroke = Color.LTGRAY
private val letter = name.take(1).uppercase()
private var cornerSize = 0f
private var intrinsicSize = -1
private val textBounds = Rect()
private val tempRect = Rect()
private val boundsF = RectF()
@@ -40,6 +47,7 @@ open class FaviconDrawable(
colorStroke = getColor(R.styleable.FaviconFallbackDrawable_strokeColor, colorStroke)
cornerSize = getDimension(R.styleable.FaviconFallbackDrawable_cornerSize, cornerSize)
paint.strokeWidth = getDimension(R.styleable.FaviconFallbackDrawable_strokeWidth, 0f) * 2f
intrinsicSize = getDimensionPixelSize(R.styleable.FaviconFallbackDrawable_drawableSize, intrinsicSize)
}
paint.textAlign = Paint.Align.CENTER
paint.isFakeBoldText = true
@@ -75,6 +83,10 @@ open class FaviconDrawable(
paint.colorFilter = colorFilter
}
override fun getIntrinsicWidth(): Int = intrinsicSize
override fun getIntrinsicHeight(): Int = intrinsicSize
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Deprecated in Java")
override fun getOpacity() = PixelFormat.TRANSPARENT
@@ -103,4 +115,16 @@ open class FaviconDrawable(
paint.getTextBounds(text, 0, text.length, tempRect)
return testTextSize * width / tempRect.width()
}
class Factory(
@StyleRes private val styleResId: Int,
) : ((ImageRequest) -> Image?) {
override fun invoke(request: ImageRequest): Image? {
val source = request.getExtra(mangaSourceKey) ?: return null
val context = request.context
val title = source.getTitle(context)
return FaviconDrawable(context, styleResId, title).asImage()
}
}
}

View File

@@ -0,0 +1,41 @@
package org.koitharu.kotatsu.core.ui.image
import android.graphics.drawable.Drawable
import android.view.Gravity
import android.widget.TextView
import androidx.annotation.GravityInt
import coil3.target.GenericViewTarget
class TextViewTarget(
override val view: TextView,
@GravityInt compoundDrawable: Int,
) : GenericViewTarget<TextView>() {
private val drawableIndex: Int = when (compoundDrawable) {
Gravity.START -> 0
Gravity.TOP -> 2
Gravity.END -> 3
Gravity.BOTTOM -> 4
else -> -1
}
override var drawable: Drawable?
get() = if (drawableIndex != -1) {
view.compoundDrawablesRelative[drawableIndex]
} else {
null
}
set(value) {
if (drawableIndex == -1) {
return
}
val drawables = view.compoundDrawablesRelative
drawables[drawableIndex] = value
view.setCompoundDrawablesRelativeWithIntrinsicBounds(
drawables[0],
drawables[1],
drawables[2],
drawables[3],
)
}
}

View File

@@ -9,6 +9,7 @@ import android.text.style.ForegroundColorSpan
import android.text.style.ImageSpan
import android.text.style.RelativeSizeSpan
import android.transition.TransitionManager
import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.view.View
@@ -37,6 +38,7 @@ import coil3.request.lifecycle
import coil3.request.placeholder
import coil3.request.target
import coil3.request.transformations
import coil3.size.Precision
import coil3.size.Scale
import coil3.transform.RoundedCornersTransformation
import coil3.util.CoilUtils
@@ -55,7 +57,6 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.LocalMangaSource
import org.koitharu.kotatsu.core.model.UnknownMangaSource
import org.koitharu.kotatsu.core.model.getTitle
import org.koitharu.kotatsu.core.model.iconResId
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
import org.koitharu.kotatsu.core.model.titleResId
import org.koitharu.kotatsu.core.os.AppShortcutManager
@@ -64,8 +65,9 @@ import org.koitharu.kotatsu.core.parser.favicon.faviconUri
import org.koitharu.kotatsu.core.ui.BaseActivity
import org.koitharu.kotatsu.core.ui.BaseListAdapter
import org.koitharu.kotatsu.core.ui.OnContextClickListenerCompat
import org.koitharu.kotatsu.core.ui.image.ChipIconTarget
import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver
import org.koitharu.kotatsu.core.ui.image.FaviconDrawable
import org.koitharu.kotatsu.core.ui.image.TextViewTarget
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.ui.sheet.BottomSheetCollapseCallback
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
@@ -86,9 +88,9 @@ import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.parentView
import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf
import org.koitharu.kotatsu.core.util.ext.setNavigationBarTransparentCompat
import org.koitharu.kotatsu.core.util.ext.setOnContextClickListenerCompat
import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.ActivityDetailsBinding
import org.koitharu.kotatsu.databinding.LayoutDetailsTableBinding
import org.koitharu.kotatsu.details.data.MangaDetails
import org.koitharu.kotatsu.details.data.ReadingTime
import org.koitharu.kotatsu.details.service.MangaPrefetchService
@@ -112,13 +114,12 @@ import org.koitharu.kotatsu.local.ui.info.LocalInfoDialog
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaListFilter
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.util.ellipsize
import org.koitharu.kotatsu.reader.ui.ReaderActivity
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo
import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet
import org.koitharu.kotatsu.search.ui.MangaListActivity
import org.koitharu.kotatsu.stats.ui.sheet.MangaStatsSheet
import javax.inject.Inject
import kotlin.math.roundToInt
import com.google.android.material.R as materialR
@AndroidEntryPoint
@@ -140,30 +141,24 @@ class DetailsActivity :
private val viewModel: DetailsViewModel by viewModels()
private lateinit var menuProvider: DetailsMenuProvider
private lateinit var infoBinding: LayoutDetailsTableBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(ActivityDetailsBinding.inflate(layoutInflater))
infoBinding = LayoutDetailsTableBinding.bind(viewBinding.root)
supportActionBar?.run {
setDisplayHomeAsUpEnabled(true)
setDisplayShowTitleEnabled(false)
}
viewBinding.buttonRead.setOnClickListener(this)
viewBinding.buttonRead.setOnLongClickListener(this)
viewBinding.buttonRead.setOnContextClickListenerCompat(this)
viewBinding.buttonDownload?.setOnClickListener(this)
viewBinding.infoLayout.chipBranch.setOnClickListener(this)
viewBinding.infoLayout.chipSize.setOnClickListener(this)
viewBinding.infoLayout.chipSource.setOnClickListener(this)
viewBinding.infoLayout.chipFavorite.setOnClickListener(this)
viewBinding.infoLayout.chipAuthor.setOnClickListener(this)
viewBinding.infoLayout.chipTime.setOnClickListener(this)
viewBinding.chipFavorite.setOnClickListener(this)
infoBinding.textViewLocal.setOnClickListener(this)
infoBinding.textViewAuthor.setOnClickListener(this)
infoBinding.textViewSource.setOnClickListener(this)
viewBinding.imageViewCover.setOnClickListener(this)
viewBinding.buttonDescriptionMore.setOnClickListener(this)
viewBinding.buttonScrobblingMore.setOnClickListener(this)
viewBinding.buttonRelatedMore.setOnClickListener(this)
viewBinding.infoLayout.chipSource.setOnClickListener(this)
viewBinding.infoLayout.chipSize.setOnClickListener(this)
viewBinding.textViewDescription.addOnLayoutChangeListener(this)
viewBinding.swipeRefreshLayout.setOnRefreshListener(this)
viewBinding.textViewDescription.viewTreeObserver.addOnDrawListener(this)
@@ -191,17 +186,17 @@ class DetailsActivity :
viewModel.localSize.observe(this, ::onLocalSizeChanged)
viewModel.relatedManga.observe(this, ::onRelatedMangaChanged)
viewModel.readingTime.observe(this, ::onReadingTimeChanged)
viewModel.selectedBranch.observe(this) {
viewBinding.infoLayout.chipBranch.text = it.ifNullOrEmpty { getString(R.string.system_default) }
}
// viewModel.selectedBranch.observe(this) {
// viewBinding.infoLayout?.chipBranch?.text = it.ifNullOrEmpty { getString(R.string.system_default) }
// }
viewModel.favouriteCategories.observe(this, ::onFavoritesChanged)
val menuInvalidator = MenuInvalidator(this)
viewModel.isStatsAvailable.observe(this, menuInvalidator)
viewModel.remoteManga.observe(this, menuInvalidator)
viewModel.branches.observe(this) {
viewBinding.infoLayout.chipBranch.isVisible = it.size > 1 || !it.firstOrNull()?.name.isNullOrEmpty()
viewBinding.infoLayout.chipBranch.isCloseIconVisible = it.size > 1
}
// viewModel.branches.observe(this) {
// viewBinding.infoLayout?.chipBranch?.isVisible = it.size > 1 || !it.firstOrNull()?.name.isNullOrEmpty()
// viewBinding.infoLayout?.chipBranch?.isCloseIconVisible = it.size > 1
// }
viewModel.chapters.observe(this, PrefetchObserver(this))
viewModel.onDownloadStarted
.filterNot { ChaptersPagesSheet.isShown(supportFragmentManager) }
@@ -221,14 +216,9 @@ class DetailsActivity :
override fun onClick(v: View) {
when (v.id) {
R.id.button_read -> openReader(isIncognitoMode = false)
R.id.chip_branch -> showBranchPopupMenu(v)
R.id.button_download -> {
val manga = viewModel.manga.value ?: return
DownloadDialogFragment.show(supportFragmentManager, listOf(manga))
}
// R.id.chip_branch -> showBranchPopupMenu(v)
R.id.chip_author -> {
R.id.textView_author -> {
val manga = viewModel.manga.value ?: return
startActivity(
MangaListActivity.newIntent(
@@ -239,7 +229,7 @@ class DetailsActivity :
)
}
R.id.chip_source -> {
R.id.textView_source -> {
val manga = viewModel.manga.value ?: return
startActivity(
MangaListActivity.newIntent(
@@ -250,7 +240,7 @@ class DetailsActivity :
)
}
R.id.chip_size -> {
R.id.textView_local -> {
val manga = viewModel.manga.value ?: return
LocalInfoDialog.show(supportFragmentManager, manga)
}
@@ -260,14 +250,14 @@ class DetailsActivity :
FavoriteSheet.show(supportFragmentManager, manga)
}
R.id.chip_time -> {
if (viewModel.isStatsAvailable.value) {
val manga = viewModel.manga.value ?: return
MangaStatsSheet.show(supportFragmentManager, manga)
} else {
// TODO
}
}
// R.id.chip_time -> {
// if (viewModel.isStatsAvailable.value) {
// val manga = viewModel.manga.value ?: return
// MangaStatsSheet.show(supportFragmentManager, manga)
// } else {
// // TODO
// }
// }
R.id.imageView_cover -> {
val manga = viewModel.manga.value ?: return
@@ -378,7 +368,7 @@ class DetailsActivity :
}
private fun onFavoritesChanged(categories: Set<FavouriteCategory>) {
val chip = viewBinding.infoLayout.chipFavorite
val chip = viewBinding.chipFavorite
chip.setChipIconResource(if (categories.isEmpty()) R.drawable.ic_heart_outline else R.drawable.ic_heart)
chip.text = if (categories.isEmpty()) {
getString(R.string.add_to_favourites)
@@ -388,17 +378,18 @@ class DetailsActivity :
}
private fun onReadingTimeChanged(time: ReadingTime?) {
val chip = viewBinding.infoLayout.chipTime
chip.textAndVisible = time?.formatShort(chip.resources)
// TODO
// chip.textAndVisible = time?.formatShort(chip.resources)
}
private fun onLocalSizeChanged(size: Long) {
val chip = viewBinding.infoLayout.chipSize
if (size == 0L) {
chip.isVisible = false
infoBinding.textViewLocal.isVisible = false
infoBinding.textViewLocalLabel.isVisible = false
} else {
chip.text = FileSize.BYTES.format(chip.context, size)
chip.isVisible = true
infoBinding.textViewLocal.text = FileSize.BYTES.format(this, size)
infoBinding.textViewLocal.isVisible = true
infoBinding.textViewLocalLabel.isVisible = true
}
}
@@ -442,62 +433,59 @@ class DetailsActivity :
}
private fun onMangaUpdated(details: MangaDetails) {
val manga = details.toManga()
loadCover(manga)
with(viewBinding) {
val manga = details.toManga()
// Main
loadCover(manga)
textViewTitle.text = manga.title
textViewSubtitle.textAndVisible = manga.altTitle
infoLayout.chipAuthor.textAndVisible = manga.author?.ellipsize(AUTHOR_LABEL_LIMIT)
textViewNsfw.isVisible = manga.isNsfw
textViewDescription.text = details.description.ifNullOrEmpty { getString(R.string.no_description) }
}
with(infoBinding) {
textViewAuthor.textAndVisible = manga.author
textViewAuthorLabel.isVisible = textViewAuthor.isVisible
if (manga.hasRating) {
ratingBar.rating = manga.rating * ratingBar.numStars
ratingBar.isVisible = true
ratingBarRating.rating = manga.rating * ratingBarRating.numStars
ratingBarRating.isVisible = true
textViewRatingLabel.isVisible = true
} else {
ratingBar.isVisible = false
ratingBarRating.isVisible = false
textViewRatingLabel.isVisible = false
}
manga.state?.let { state ->
textViewState.textAndVisible = resources.getString(state.titleResId)
imageViewState.setImageResource(state.iconResId)
imageViewState.isVisible = true
textViewStateLabel.isVisible = textViewState.isVisible
} ?: run {
textViewState.isVisible = false
imageViewState.isVisible = false
textViewStateLabel.isVisible = false
}
if (manga.source == LocalMangaSource || manga.source == UnknownMangaSource) {
infoLayout.chipSource.isVisible = false
textViewSource.isVisible = false
textViewSourceLabel.isVisible = false
} else {
infoLayout.chipSource.text = manga.source.getTitle(this@DetailsActivity)
infoLayout.chipSource.isVisible = true
textViewSource.textAndVisible = manga.source.getTitle(this@DetailsActivity)
textViewSourceLabel.isVisible = textViewSource.isVisible == true
}
textViewNsfw.isVisible = manga.isNsfw
// Chips
bindTags(manga)
textViewDescription.text = details.description.ifNullOrEmpty { getString(R.string.no_description) }
viewBinding.infoLayout.chipSource.also { chip ->
ImageRequest.Builder(this@DetailsActivity)
.data(manga.source.faviconUri())
.lifecycle(this@DetailsActivity)
.crossfade(false)
.size(resources.getDimensionPixelSize(materialR.dimen.m3_chip_icon_size))
.target(ChipIconTarget(chip))
.placeholder(R.drawable.ic_web)
.fallback(R.drawable.ic_web)
.error(R.drawable.ic_web)
.mangaSourceExtra(manga.source)
.transformations(RoundedCornersTransformation(resources.getDimension(R.dimen.chip_icon_corner)))
.allowRgb565(true)
.enqueueWith(coil)
}
title = manga.title
invalidateOptionsMenu()
val faviconPlaceholderFactory = FaviconDrawable.Factory(R.style.FaviconDrawable_Chip)
ImageRequest.Builder(this@DetailsActivity)
.data(manga.source.faviconUri())
.lifecycle(this@DetailsActivity)
.crossfade(false)
.precision(Precision.EXACT)
.size(resources.getDimensionPixelSize(materialR.dimen.m3_chip_icon_size))
.target(TextViewTarget(textViewSource, Gravity.START))
.placeholder(faviconPlaceholderFactory)
.error(faviconPlaceholderFactory)
.fallback(faviconPlaceholderFactory)
.mangaSourceExtra(manga.source)
.transformations(RoundedCornersTransformation(resources.getDimension(R.dimen.chip_icon_corner)))
.allowRgb565(true)
.enqueueWith(coil)
}
bindTags(manga)
title = manga.title
invalidateOptionsMenu()
}
private fun onMangaRemoved(manga: Manga) {
@@ -527,22 +515,28 @@ class DetailsActivity :
}
}
private fun onHistoryChanged(info: HistoryInfo, isLoading: Boolean) = with(viewBinding) {
buttonRead.setTitle(if (info.canContinue) R.string._continue else R.string.read)
buttonRead.subtitle = when {
isLoading -> getString(R.string.loading_)
info.isIncognitoMode -> getString(R.string.incognito_mode)
info.isChapterMissing -> getString(R.string.chapter_is_missing)
private fun onHistoryChanged(info: HistoryInfo, isLoading: Boolean) = with(infoBinding) {
textViewChapters.textAndVisible = when {
isLoading -> null
info.currentChapter >= 0 -> getString(R.string.chapter_d_of_d, info.currentChapter + 1, info.totalChapters)
info.totalChapters == 0 -> getString(R.string.no_chapters)
info.totalChapters == -1 -> getString(R.string.error_occurred)
else -> resources.getQuantityString(R.plurals.chapters, info.totalChapters, info.totalChapters)
}
val isFirstCall = buttonRead.tag == null
buttonRead.tag = Unit
buttonRead.setProgress(info.percent.coerceIn(0f, 1f), !isFirstCall)
buttonDownload?.isEnabled = info.isValid && info.canDownload
buttonRead.isEnabled = info.isValid
textViewProgress.textAndVisible = if (info.percent <= 0f) {
null
} else {
getString(R.string.percent_string_pattern, (info.percent * 100f).toInt().toString())
}
progress.setProgressCompat(
(progress.max * info.percent.coerceIn(0f, 1f)).roundToInt(),
true,
)
textViewProgressLabel.isVisible = info.history != null
textViewProgress.isVisible = info.history != null
progress.isVisible = info.history != null
// buttonRead.setProgress(info.percent.coerceIn(0f, 1f), !isFirstCall)
// buttonDownload?.isEnabled = info.isValid && info.canDownload
}
private fun showBranchPopupMenu(v: View) {
@@ -663,7 +657,6 @@ class DetailsActivity :
companion object {
private const val FAV_LABEL_LIMIT = 16
private const val AUTHOR_LABEL_LIMIT = 16
fun newIntent(context: Context, manga: Manga): Intent {
return Intent(context, DetailsActivity::class.java)

View File

@@ -0,0 +1,147 @@
package org.koitharu.kotatsu.details.ui
import android.content.Context
import android.graphics.Color
import android.text.style.DynamicDrawableSpan
import android.text.style.ForegroundColorSpan
import android.text.style.ImageSpan
import android.text.style.RelativeSizeSpan
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.appcompat.widget.PopupMenu
import androidx.core.text.buildSpannedString
import androidx.core.text.inSpans
import androidx.core.view.get
import androidx.lifecycle.LifecycleOwner
import com.google.android.material.button.MaterialButton
import com.google.android.material.button.MaterialSplitButton
import com.google.android.material.snackbar.Snackbar
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.details.ui.model.HistoryInfo
import org.koitharu.kotatsu.reader.ui.ReaderActivity
class ReadButtonDelegate(
splitButton: MaterialSplitButton,
private val viewModel: DetailsViewModel,
) : View.OnClickListener, PopupMenu.OnMenuItemClickListener, PopupMenu.OnDismissListener {
private val buttonRead = splitButton[0] as MaterialButton
private val buttonMenu = splitButton[1] as MaterialButton
private val context: Context
get() = buttonRead.context
override fun onClick(v: View) {
when (v.id) {
R.id.button_read -> openReader(isIncognitoMode = false)
R.id.button_read_menu -> showMenu()
}
}
override fun onMenuItemClick(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_incognito -> openReader(isIncognitoMode = true)
R.id.action_forget -> viewModel.removeFromHistory()
else -> {
val branch = viewModel.branches.value.getOrNull(item.order) ?: return false
viewModel.setSelectedBranch(branch.name)
}
}
return true
}
override fun onDismiss(menu: PopupMenu?) {
buttonMenu.isChecked = false
}
fun attach(lifecycleOwner: LifecycleOwner) {
buttonRead.setOnClickListener(this)
buttonMenu.setOnClickListener(this)
viewModel.historyInfo.observe(lifecycleOwner, this::onHistoryChanged)
}
private fun showMenu() {
val menu = PopupMenu(context, buttonMenu)
menu.inflate(R.menu.popup_read)
menu.menu.setGroupDividerEnabled(true)
menu.menu.populateBranchList()
menu.menu.findItem(R.id.action_forget)?.isVisible = viewModel.historyInfo.value.run {
!isIncognitoMode && history != null
}
menu.setOnMenuItemClickListener(this)
menu.setForceShowIcon(true)
menu.setOnDismissListener(this)
buttonMenu.isChecked = true
menu.show()
}
private fun openReader(isIncognitoMode: Boolean) {
val detailsViewModel = viewModel as? DetailsViewModel ?: return
val manga = viewModel.manga.value ?: return
if (detailsViewModel.historyInfo.value.isChapterMissing) {
Snackbar.make(buttonRead, R.string.chapter_is_missing, Snackbar.LENGTH_SHORT)
.show() // TODO
} else {
context.startActivity(
ReaderActivity.IntentBuilder(context)
.manga(manga)
.branch(detailsViewModel.selectedBranchValue)
.incognito(isIncognitoMode)
.build(),
)
if (isIncognitoMode) {
Toast.makeText(context, R.string.incognito_mode, Toast.LENGTH_SHORT).show()
}
}
}
private fun onHistoryChanged(info: HistoryInfo) {
buttonRead.setText(if (info.canContinue) R.string._continue else R.string.read)
buttonRead.isEnabled = info.isValid
}
private fun Menu.populateBranchList() {
val branches = viewModel.branches.value
if (branches.size <= 1) {
return
}
for ((i, branch) in branches.withIndex()) {
val title = buildSpannedString {
if (branch.isCurrent) {
inSpans(
ImageSpan(
context,
R.drawable.ic_current_chapter,
DynamicDrawableSpan.ALIGN_BASELINE,
),
) {
append(' ')
}
append(' ')
}
append(branch.name ?: context.getString(R.string.system_default))
append(' ')
append(' ')
inSpans(
ForegroundColorSpan(
context.getThemeColor(
android.R.attr.textColorSecondary,
Color.LTGRAY,
),
),
RelativeSizeSpan(0.74f),
) {
append(branch.count.toString())
}
}
val item = add(R.id.group_branches, Menu.NONE, i, title)
item.isCheckable = true
item.isChecked = branch.isSelected
}
setGroupCheckable(R.id.group_branches, true, true)
}
}

View File

@@ -25,13 +25,21 @@ class ChaptersPagesAdapter(
}
override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
tab.setText(
tab.setIcon(
when (position) {
0 -> R.string.chapters
1 -> if (isPagesTabEnabled) R.string.pages else R.string.bookmarks
2 -> R.string.bookmarks
0 -> R.drawable.ic_list
1 -> if (isPagesTabEnabled) R.drawable.ic_grid else R.drawable.ic_bookmark
2 -> R.drawable.ic_bookmark
else -> 0
},
)
// tab.setText(
// when (position) {
// 0 -> R.string.chapters
// 1 -> if (isPagesTabEnabled) R.string.pages else R.string.bookmarks
// 2 -> R.string.bookmarks
// else -> 0
// },
// )
}
}

View File

@@ -29,6 +29,8 @@ import org.koitharu.kotatsu.core.util.ext.setTabsEnabled
import org.koitharu.kotatsu.core.util.ext.showDistinct
import org.koitharu.kotatsu.core.util.ext.withArgs
import org.koitharu.kotatsu.databinding.SheetChaptersPagesBinding
import org.koitharu.kotatsu.details.ui.DetailsViewModel
import org.koitharu.kotatsu.details.ui.ReadButtonDelegate
import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver
import javax.inject.Inject
@@ -54,6 +56,9 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
if (!adapter.isPagesTabEnabled) {
defaultTab = (defaultTab - 1).coerceAtLeast(TAB_CHAPTERS)
}
(viewModel as? DetailsViewModel)?.let { dvm ->
ReadButtonDelegate(binding.splitButtonRead, dvm).attach(viewLifecycleOwner)
}
binding.pager.offscreenPageLimit = adapter.itemCount
binding.pager.recyclerView?.isNestedScrollingEnabled = false
binding.pager.adapter = adapter
@@ -88,6 +93,8 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
val binding = viewBinding ?: return
val isActionModeStarted = actionModeDelegate?.isActionModeStarted == true
binding.toolbar.menuView?.isVisible = newState != STATE_COLLAPSED && !isActionModeStarted
binding.splitButtonRead.isVisible = newState == STATE_COLLAPSED && !isActionModeStarted
&& viewModel is DetailsViewModel
}
override fun onActionModeStarted(mode: ActionMode) {

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
android:top="5dp">
<shape android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners
android:bottomLeftRadius="@dimen/list_selector_corner"
android:bottomRightRadius="@dimen/list_selector_corner"
android:topLeftRadius="@dimen/list_selector_corner"
android:topRightRadius="@dimen/list_selector_corner" />
</shape>
</item>
</layer-list>

View File

@@ -120,56 +120,17 @@
app:layout_constraintTop_toBottomOf="@id/textView_title"
tools:text="@tools:sample/lorem[12]" />
<ImageView
android:id="@+id/imageView_state"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginVertical="0.5dp"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="@id/textView_state"
app:layout_constraintDimensionRatio="1"
app:layout_constraintStart_toEndOf="@id/imageView_cover"
app:layout_constraintTop_toTopOf="@id/textView_state"
tools:src="@drawable/ic_state_ongoing" />
<TextView
android:id="@+id/textView_state"
<com.google.android.material.chip.Chip
android:id="@+id/chip_favorite"
style="@style/Widget.Kotatsu.Chip.Dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:drawablePadding="4dp"
android:gravity="center_vertical"
android:labelFor="@id/imageView_state"
android:singleLine="true"
android:textColor="?colorTertiary"
android:textStyle="bold"
app:drawableTint="?colorTertiary"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@id/imageView_state"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
app:chipIcon="@drawable/ic_heart_outline"
app:layout_constraintStart_toEndOf="@id/imageView_cover"
app:layout_constraintTop_toBottomOf="@id/textView_subtitle"
tools:text="@string/state_ongoing" />
<RatingBar
android:id="@+id/rating_bar"
style="?ratingBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:isIndicator="true"
android:max="1"
android:numStars="5"
android:stepSize="0.5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/imageView_cover"
app:layout_constraintTop_toBottomOf="@id/textView_state"
tools:rating="4" />
tools:text="@string/add_to_favourites" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_header"
@@ -177,41 +138,9 @@
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:barrierMargin="@dimen/margin_normal"
app:constraint_referenced_ids="imageView_cover,rating_bar" />
app:constraint_referenced_ids="imageView_cover,chip_favorite" />
<include
android:id="@+id/info_layout"
layout="@layout/layout_details_chips"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/screen_padding"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier_header" />
<org.koitharu.kotatsu.core.ui.widgets.ProgressButton
android:id="@+id/button_read"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginHorizontal="@dimen/screen_padding"
android:layout_marginTop="@dimen/margin_normal"
android:foreground="?selectableItemBackground"
android:gravity="center"
android:paddingHorizontal="6dp"
android:paddingVertical="8dp"
android:textColor="?colorOnPrimaryContainer"
app:baseColor="?colorSecondaryContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/info_layout"
app:progressColor="?colorPrimary"
app:subtitleTextAppearance="?textAppearanceBodySmall"
app:titleTextAppearance="?textAppearanceButton"
tools:max="100"
tools:progress="40"
tools:subtitle="12 chapters"
tools:title="@string/read" />
<include layout="@layout/layout_details_table" />
<TextView
android:id="@+id/textView_description_title"
@@ -226,7 +155,7 @@
android:textAppearance="?textAppearanceTitleSmall"
app:layout_constraintEnd_toStartOf="@id/button_description_more"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_read" />
app:layout_constraintTop_toBottomOf="@id/textView_progress_label" />
<Button
android:id="@+id/button_description_more"

View File

@@ -113,57 +113,17 @@
app:layout_constraintTop_toBottomOf="@id/textView_title"
tools:text="@tools:sample/lorem[12]" />
<ImageView
android:id="@+id/imageView_state"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginVertical="0.5dp"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="@id/textView_state"
app:layout_constraintDimensionRatio="1"
app:layout_constraintStart_toEndOf="@id/imageView_cover"
app:layout_constraintTop_toTopOf="@id/textView_state"
app:tint="?colorTertiary"
tools:src="@drawable/ic_state_ongoing" />
<TextView
android:id="@+id/textView_state"
<com.google.android.material.chip.Chip
android:id="@+id/chip_favorite"
style="@style/Widget.Kotatsu.Chip.Dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:drawablePadding="4dp"
android:gravity="center_vertical"
android:labelFor="@id/imageView_state"
android:singleLine="true"
android:textColor="?colorTertiary"
android:textStyle="bold"
app:drawableTint="?colorTertiary"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@id/imageView_state"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
app:chipIcon="@drawable/ic_heart_outline"
app:layout_constraintStart_toEndOf="@id/imageView_cover"
app:layout_constraintTop_toBottomOf="@id/textView_subtitle"
tools:text="@string/state_ongoing" />
<RatingBar
android:id="@+id/rating_bar"
style="?ratingBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:isIndicator="true"
android:max="1"
android:numStars="5"
android:stepSize="0.5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/imageView_cover"
app:layout_constraintTop_toBottomOf="@id/textView_state"
tools:rating="4" />
tools:text="@string/add_to_favourites" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_header"
@@ -171,55 +131,9 @@
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:barrierMargin="@dimen/margin_normal"
app:constraint_referenced_ids="imageView_cover,rating_bar" />
app:constraint_referenced_ids="imageView_cover,chip_favorite" />
<include
android:id="@+id/info_layout"
layout="@layout/layout_details_chips"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/screen_padding"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier_header" />
<org.koitharu.kotatsu.core.ui.widgets.ProgressButton
android:id="@+id/button_read"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="@dimen/margin_normal"
android:layout_marginEnd="12dp"
android:foreground="?selectableItemBackground"
android:gravity="center"
android:paddingHorizontal="6dp"
android:paddingVertical="8dp"
app:baseColor="?colorSecondaryContainer"
app:layout_constraintEnd_toStartOf="@id/button_download"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/info_layout"
app:progressColor="?colorPrimary"
app:subtitleTextAppearance="?textAppearanceBodySmall"
app:titleTextAppearance="?textAppearanceButton"
tools:max="100"
tools:progress="40"
tools:subtitle="12 chapters"
tools:title="@string/read" />
<ImageView
android:id="@+id/button_download"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_circle_button"
android:backgroundTint="?colorSecondaryContainer"
android:contentDescription="@string/download"
android:scaleType="centerInside"
app:layout_constraintBottom_toBottomOf="@id/button_read"
app:layout_constraintDimensionRatio="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/button_read"
app:srcCompat="@drawable/ic_download" />
<include layout="@layout/layout_details_table" />
<TextView
android:id="@+id/textView_description_title"
@@ -235,7 +149,7 @@
android:textAppearance="?textAppearanceTitleSmall"
app:layout_constraintEnd_toStartOf="@id/button_description_more"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_read" />
app:layout_constraintTop_toBottomOf="@id/textView_progress_label" />
<Button
android:id="@+id/button_description_more"
@@ -323,21 +237,6 @@
tools:listitem="@layout/item_scrobbling_info"
tools:visibility="visible" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progressBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone"
app:hideAnimationBehavior="outward"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:showAnimationBehavior="inward"
app:trackCornerRadius="0dp"
tools:visibility="visible" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_scrobbling"
android:layout_width="wrap_content"

View File

@@ -1,76 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.chip.ChipGroup
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/info_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:chipSpacingHorizontal="6dp"
app:chipSpacingVertical="6dp">
<com.google.android.material.chip.Chip
android:id="@+id/chip_favorite"
style="@style/Widget.Kotatsu.Chip.Dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:chipIcon="@drawable/ic_heart_outline"
app:ensureMinTouchTargetSize="false"
tools:text="@string/add_to_favourites" />
<com.google.android.material.chip.Chip
android:id="@+id/chip_source"
style="@style/Widget.Kotatsu.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:chipIcon="@drawable/ic_web"
app:ensureMinTouchTargetSize="false"
tools:text="Source"
tools:visibility="visible" />
<com.google.android.material.chip.Chip
android:id="@+id/chip_author"
style="@style/Widget.Kotatsu.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:chipIcon="@drawable/ic_user"
app:ensureMinTouchTargetSize="false"
tools:text="Author"
tools:visibility="visible" />
<com.google.android.material.chip.Chip
android:id="@+id/chip_size"
style="@style/Widget.Kotatsu.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:chipIcon="@drawable/ic_storage"
app:ensureMinTouchTargetSize="false"
tools:text="1.8 GiB"
tools:visibility="visible" />
<com.google.android.material.chip.Chip
android:id="@+id/chip_branch"
style="@style/Widget.Kotatsu.Chip.Dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:chipIcon="@drawable/ic_language"
app:ensureMinTouchTargetSize="false"
tools:text="English"
tools:visibility="visible" />
<com.google.android.material.chip.Chip
android:id="@+id/chip_time"
style="@style/Widget.Kotatsu.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:chipIcon="@drawable/ic_timelapse"
app:ensureMinTouchTargetSize="false"
tools:text="2 h 40 m"
tools:visibility="visible" />
</com.google.android.material.chip.ChipGroup>

View File

@@ -0,0 +1,235 @@
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_details"
style="?materialCardViewFilledStyle"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginHorizontal="@dimen/screen_padding"
android:layout_marginBottom="-12dp"
app:cardBackgroundColor="?colorBackgroundFloating"
app:layout_constraintBottom_toBottomOf="@id/textView_progress_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier_header" />
<TextView
android:id="@+id/textView_source_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:singleLine="true"
android:text="@string/source"
android:textAppearance="?textAppearanceTitleSmall"
app:layout_constraintStart_toStartOf="@id/card_details"
app:layout_constraintTop_toBottomOf="@id/barrier_header" />
<TextView
android:id="@+id/textView_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:background="@drawable/custom_selectable_item_background"
android:drawablePadding="4dp"
android:padding="4dp"
android:singleLine="true"
android:textAppearance="?textAppearanceBodyMedium"
app:layout_constraintBaseline_toBaselineOf="@id/textView_source_label"
app:layout_constraintEnd_toEndOf="@id/card_details"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@id/barrier_table"
tools:text="MangaSource" />
<TextView
android:id="@+id/textView_author_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:singleLine="true"
android:text="@string/author"
android:textAppearance="?textAppearanceTitleSmall"
app:layout_constraintStart_toStartOf="@id/card_details"
app:layout_constraintTop_toBottomOf="@id/textView_source_label" />
<TextView
android:id="@+id/textView_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:background="@drawable/custom_selectable_item_background"
android:padding="4dp"
android:singleLine="true"
android:textAppearance="?textAppearanceBodyMedium"
app:layout_constraintBaseline_toBaselineOf="@id/textView_author_label"
app:layout_constraintEnd_toEndOf="@id/card_details"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@id/barrier_table"
tools:text="Author name" />
<TextView
android:id="@+id/textView_rating_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:singleLine="true"
android:text="@string/rating"
android:textAppearance="?textAppearanceTitleSmall"
app:layout_constraintStart_toStartOf="@id/card_details"
app:layout_constraintTop_toBottomOf="@id/textView_author_label" />
<RatingBar
android:id="@+id/ratingBar_rating"
style="?ratingBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_normal"
android:layout_marginEnd="@dimen/screen_padding"
android:isIndicator="true"
android:max="1"
android:numStars="5"
android:stepSize="0.5"
android:textAppearance="?textAppearanceBodyMedium"
app:layout_constraintBottom_toBottomOf="@id/textView_rating_label"
app:layout_constraintEnd_toEndOf="@id/card_details"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@id/barrier_table"
app:layout_constraintTop_toTopOf="@id/textView_rating_label"
tools:text="Author name" />
<TextView
android:id="@+id/textView_state_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:singleLine="true"
android:text="@string/state"
android:textAppearance="?textAppearanceTitleSmall"
app:layout_constraintStart_toStartOf="@id/card_details"
app:layout_constraintTop_toBottomOf="@id/textView_rating_label" />
<TextView
android:id="@+id/textView_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_normal"
android:layout_marginEnd="@dimen/screen_padding"
android:singleLine="true"
android:textAppearance="?textAppearanceBodyMedium"
app:layout_constraintBaseline_toBaselineOf="@id/textView_state_label"
app:layout_constraintEnd_toEndOf="@id/card_details"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@id/barrier_table"
tools:text="Ongoing" />
<TextView
android:id="@+id/textView_chapters_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:singleLine="true"
android:text="@string/chapters"
android:textAppearance="?textAppearanceTitleSmall"
app:layout_constraintStart_toStartOf="@id/card_details"
app:layout_constraintTop_toBottomOf="@id/textView_state_label" />
<TextView
android:id="@+id/textView_chapters"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_normal"
android:layout_marginEnd="@dimen/screen_padding"
android:singleLine="true"
android:textAppearance="?textAppearanceBodyMedium"
app:layout_constraintBaseline_toBaselineOf="@id/textView_chapters_label"
app:layout_constraintEnd_toEndOf="@id/card_details"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@id/barrier_table"
tools:text="10 of 50" />
<TextView
android:id="@+id/textView_local_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:singleLine="true"
android:text="@string/on_device"
android:textAppearance="?textAppearanceTitleSmall"
app:layout_constraintStart_toStartOf="@id/card_details"
app:layout_constraintTop_toBottomOf="@id/textView_chapters_label" />
<TextView
android:id="@+id/textView_local"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:background="@drawable/custom_selectable_item_background"
android:padding="4dp"
android:singleLine="true"
android:textAppearance="?textAppearanceBodyMedium"
app:layout_constraintBaseline_toBaselineOf="@id/textView_local_label"
app:layout_constraintEnd_toEndOf="@id/card_details"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@id/barrier_table"
tools:text="25 Mb" />
<TextView
android:id="@+id/textView_progress_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:singleLine="true"
android:text="@string/progress"
android:textAppearance="?textAppearanceTitleSmall"
app:layout_constraintStart_toStartOf="@id/card_details"
app:layout_constraintTop_toBottomOf="@id/textView_local_label" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress"
style="@style/Widget.Material3.LinearProgressIndicator"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_normal"
android:layout_marginEnd="12dp"
android:indeterminate="false"
android:max="100"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="@id/textView_progress_label"
app:layout_constraintEnd_toStartOf="@id/textView_progress"
app:layout_constraintStart_toEndOf="@id/barrier_table"
app:layout_constraintTop_toTopOf="@id/textView_progress_label"
tools:progress="12" />
<TextView
android:id="@+id/textView_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/screen_padding"
android:singleLine="true"
android:textAppearance="?textAppearanceBodyMedium"
app:layout_constraintBaseline_toBaselineOf="@id/textView_progress_label"
app:layout_constraintEnd_toEndOf="@id/card_details"
tools:text="40%" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_table"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="textView_source_label,textView_author_label,textView_rating_label,textView_state_label,textView_progress_label,textView_chapters_label,textView_local_label" />
</merge>

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="?materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="12dp">
<TextView
android:id="@+id/textView_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:textAppearance="?textAppearanceTitleSmall"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="46 chapters" />
<TextView
android:id="@+id/textView_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:textAppearance="?textAppearanceSubtitle1"
app:layout_constraintBaseline_toBaselineOf="@id/textView_title"
app:layout_constraintEnd_toEndOf="parent"
tools:text="12%" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress"
style="@style/Widget.Material3.LinearProgressIndicator"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginTop="12dp"
android:indeterminate="false"
android:max="100"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView_title"
tools:progress="12" />
<TextView
android:id="@+id/textView_secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:textAppearance="?textAppearanceBodySmall"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/progress"
tools:text="@string/incognito_mode" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

View File

@@ -17,16 +17,57 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
style="@style/Widget.Material3.TabLayout.Secondary"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:background="@null"
app:tabGravity="start"
app:tabMode="scrollable"
app:tabUnboundedRipple="false" />
android:layout_marginEnd="8dp"
android:baselineAligned="false"
android:gravity="center_vertical"
android:orientation="horizontal">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
style="@style/Widget.Material3.TabLayout.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_weight="1"
android:background="@null"
app:tabGravity="start"
app:tabIndicator="@drawable/bg_tab_pill"
app:tabIndicatorAnimationMode="fade"
app:tabIndicatorColor="?colorSurfaceDim"
app:tabIndicatorFullWidth="true"
app:tabIndicatorGravity="stretch"
app:tabInlineLabel="true"
app:tabMinWidth="0dp"
app:tabMode="scrollable" />
<com.google.android.material.button.MaterialSplitButton
android:id="@+id/split_button_read"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end">
<Button
android:id="@+id/button_read"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/read" />
<Button
android:id="@+id/button_read_menu"
style="?attr/materialSplitButtonIconFilledStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:minWidth="48dp"
app:icon="@drawable/m3_split_button_chevron_avd"
app:toggleCheckedStateOnClick="false" />
</com.google.android.material.button.MaterialSplitButton>
</LinearLayout>
</com.google.android.material.appbar.MaterialToolbar>

View File

@@ -12,10 +12,11 @@
<item
android:id="@+id/action_save"
android:icon="@drawable/ic_download"
android:orderInCategory="40"
android:title="@string/download"
android:visible="false"
app:showAsAction="never" />
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_delete"

View File

@@ -132,6 +132,7 @@
<attr name="strokeColor" />
<attr name="strokeWidth" />
<attr name="cornerSize" />
<attr name="drawableSize" />
</declare-styleable>
<declare-styleable name="NestedRecyclerView">

View File

@@ -34,7 +34,7 @@
<dimen name="chapter_grid_width">80dp</dimen>
<dimen name="side_card_offset">8dp</dimen>
<dimen name="webtoon_pages_gap">24dp</dimen>
<dimen name="details_bs_peek_height">92dp</dimen>
<dimen name="details_bs_peek_height">120dp</dimen>
<dimen name="spinner_height">56dp</dimen>
<dimen name="search_suggestions_manga_height">142dp</dimen>

View File

@@ -769,4 +769,7 @@
<string name="handle_links_summary">Handle manga links from external applications (e.g. web browser). You may also need to enable it manually in the application\'s system settings</string>
<string name="email">Email</string>
<string name="captcha_required_message">This source requires solving a captcha to continue</string>
<string name="author">Author</string>
<string name="rating">Rating</string>
<string name="source">Source</string>
</resources>

View File

@@ -328,4 +328,10 @@
<item name="cornerSize">12dp</item>
</style>
<style name="FaviconDrawable.Chip">
<item name="strokeWidth">1px</item>
<item name="cornerSize">@dimen/chip_icon_corner</item>
<item name="drawableSize">@dimen/m3_chip_icon_size</item>
</style>
</resources>

View File

@@ -26,7 +26,7 @@ ksp = "2.0.21-1.0.28"
leakcanary = "3.0-alpha-8"
lifecycle = "2.8.7"
markwon = "4.6.2"
material = "1.12.0"
material = "1.13.0-alpha08"
moshi = "1.15.1"
okhttp = "4.12.0"
okio = "3.9.1"