Fixes and improvements batch
This commit is contained in:
@@ -1,38 +1,5 @@
|
||||
package org.koitharu.kotatsu.bookmarks.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.fragment.app.commit
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import org.koitharu.kotatsu.main.ui.owners.SnackbarOwner
|
||||
import org.koitharu.kotatsu.core.ui.FragmentContainerActivity
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AllBookmarksActivity :
|
||||
BaseActivity<ActivityContainerBinding>(),
|
||||
AppBarOwner,
|
||||
SnackbarOwner {
|
||||
|
||||
override val appBar: AppBarLayout
|
||||
get() = viewBinding.appbar
|
||||
|
||||
override val snackbarHost: CoordinatorLayout
|
||||
get() = viewBinding.root
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityContainerBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
val fm = supportFragmentManager
|
||||
if (fm.findFragmentById(R.id.container) == null) {
|
||||
fm.commit {
|
||||
setReorderingAllowed(true)
|
||||
replace(R.id.container, AllBookmarksFragment::class.java, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class AllBookmarksActivity : FragmentContainerActivity(AllBookmarksFragment::class.java)
|
||||
|
||||
@@ -370,6 +370,25 @@ class AppRouter private constructor(
|
||||
}.show()
|
||||
}
|
||||
|
||||
fun showAuthorDialog(author: String, source: MangaSource) {
|
||||
buildAlertDialog(contextOrNull() ?: return) {
|
||||
setTitle(author)
|
||||
setItems(
|
||||
arrayOf(
|
||||
context.getString(R.string.search_on_s, source.getTitle(context)),
|
||||
context.getString(R.string.search_everywhere),
|
||||
),
|
||||
) { _, which ->
|
||||
when (which) {
|
||||
0 -> openList(source, MangaListFilter(author = author), null)
|
||||
1 -> openSearch(author, SearchKind.AUTHOR)
|
||||
}
|
||||
}
|
||||
setNegativeButton(R.string.close, null)
|
||||
setCancelable(true)
|
||||
}.show()
|
||||
}
|
||||
|
||||
fun showErrorDialog(error: Throwable, url: String? = null) {
|
||||
ErrorDetailsDialog().withArgs(2) {
|
||||
putSerializable(KEY_ERROR, error)
|
||||
|
||||
@@ -252,7 +252,7 @@ class ExternalPluginContentSource(
|
||||
publicUrl = getString(COLUMN_PUBLIC_URL),
|
||||
rating = getFloat(COLUMN_RATING),
|
||||
isNsfw = getBooleanOrDefault(COLUMN_IS_NSFW, false),
|
||||
coverUrl = getString(COLUMN_COVER_URL),
|
||||
coverUrl = getStringOrNull(COLUMN_COVER_URL),
|
||||
tags = getStringOrNull(COLUMN_TAGS)?.split(':')?.mapNotNullToSet {
|
||||
val parts = it.splitTwoParts('=') ?: return@mapNotNullToSet null
|
||||
MangaTag(key = parts.first, title = parts.second, source = source)
|
||||
|
||||
@@ -20,7 +20,7 @@ class ExternalPluginCursor(private val source: ExternalMangaSource, cursor: Curs
|
||||
return when {
|
||||
columnIndex < 0 -> null
|
||||
isNull(columnIndex) -> null
|
||||
else -> getString(columnIndex)
|
||||
else -> getString(columnIndex).takeUnless { it == "null" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
|
||||
@Inject
|
||||
lateinit var settings: AppSettings
|
||||
|
||||
override val recyclerView: RecyclerView
|
||||
override val recyclerView: RecyclerView?
|
||||
get() = listView
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.koitharu.kotatsu.core.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.commit
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import org.koitharu.kotatsu.main.ui.owners.SnackbarOwner
|
||||
|
||||
@AndroidEntryPoint
|
||||
abstract class FragmentContainerActivity(private val fragmentClass: Class<out Fragment>) :
|
||||
BaseActivity<ActivityContainerBinding>(),
|
||||
AppBarOwner,
|
||||
SnackbarOwner {
|
||||
|
||||
override val appBar: AppBarLayout
|
||||
get() = viewBinding.appbar
|
||||
|
||||
override val snackbarHost: CoordinatorLayout
|
||||
get() = viewBinding.root
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityContainerBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
val fm = supportFragmentManager
|
||||
if (fm.findFragmentById(R.id.container) == null) {
|
||||
fm.commit {
|
||||
setReorderingAllowed(true)
|
||||
replace(R.id.container, fragmentClass, getFragmentExtras())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun getFragmentExtras(): Bundle? = intent.extras
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.GravityInt
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.OnApplyWindowInsetsListener
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
@@ -14,19 +15,30 @@ import org.koitharu.kotatsu.core.util.ext.start
|
||||
class InsetsToMarginsListener(
|
||||
@GravityInt
|
||||
private val sides: Int,
|
||||
private val baseMargins: Insets,
|
||||
) : OnApplyWindowInsetsListener {
|
||||
|
||||
private val insetType = WindowInsetsCompat.Type.systemBars()
|
||||
|
||||
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
|
||||
val barsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val barsInsets = insets.getInsets(insetType)
|
||||
v.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
if (sides and Gravity.START == Gravity.START) marginStart = barsInsets.start(v)
|
||||
if (sides and Gravity.TOP == Gravity.TOP) topMargin = barsInsets.top
|
||||
if (sides and Gravity.END == Gravity.END) marginEnd = barsInsets.end(v)
|
||||
if (sides and Gravity.BOTTOM == Gravity.BOTTOM) bottomMargin = barsInsets.bottom
|
||||
if (sides and Gravity.START == Gravity.START) {
|
||||
marginStart = barsInsets.start(v) + baseMargins.start(v)
|
||||
}
|
||||
if (sides and Gravity.TOP == Gravity.TOP) {
|
||||
topMargin = barsInsets.top + baseMargins.top
|
||||
}
|
||||
if (sides and Gravity.END == Gravity.END) {
|
||||
marginEnd = barsInsets.end(v) + baseMargins.end(v)
|
||||
}
|
||||
if (sides and Gravity.BOTTOM == Gravity.BOTTOM) {
|
||||
bottomMargin = barsInsets.bottom + baseMargins.bottom
|
||||
}
|
||||
}
|
||||
return WindowInsetsCompat.Builder(insets)
|
||||
.setInsets(
|
||||
WindowInsetsCompat.Type.systemBars(),
|
||||
insetType,
|
||||
barsInsets.consumeRelative(
|
||||
v,
|
||||
start = sides and Gravity.START == Gravity.START,
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.core.ui.util
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import androidx.annotation.GravityInt
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.OnApplyWindowInsetsListener
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import org.koitharu.kotatsu.core.util.ext.consumeRelative
|
||||
@@ -12,19 +13,42 @@ import org.koitharu.kotatsu.core.util.ext.start
|
||||
class InsetsToPaddingListener(
|
||||
@GravityInt
|
||||
private val sides: Int,
|
||||
private val basePaddings: Insets,
|
||||
) : OnApplyWindowInsetsListener {
|
||||
|
||||
private val insetType = WindowInsetsCompat.Type.systemBars()
|
||||
|
||||
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
|
||||
val barsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val barsInsets = insets.getInsets(insetType)
|
||||
v.setPaddingRelative(
|
||||
/* start = */ if (sides and Gravity.START == Gravity.START) barsInsets.start(v) else v.paddingStart,
|
||||
/* top = */ if (sides and Gravity.TOP == Gravity.TOP) barsInsets.top else v.paddingTop,
|
||||
/* end = */ if (sides and Gravity.END == Gravity.END) barsInsets.end(v) else v.paddingEnd,
|
||||
/* bottom = */ if (sides and Gravity.BOTTOM == Gravity.BOTTOM) barsInsets.bottom else v.paddingBottom,
|
||||
/* start = */
|
||||
if (sides and Gravity.START == Gravity.START) {
|
||||
barsInsets.start(v) + basePaddings.start(v)
|
||||
} else {
|
||||
v.paddingStart
|
||||
},
|
||||
/* top = */
|
||||
if (sides and Gravity.TOP == Gravity.TOP) {
|
||||
barsInsets.top + basePaddings.top
|
||||
} else {
|
||||
v.paddingTop
|
||||
},
|
||||
/* end = */
|
||||
if (sides and Gravity.END == Gravity.END) {
|
||||
barsInsets.end(v) + basePaddings.end(v)
|
||||
} else {
|
||||
v.paddingEnd
|
||||
},
|
||||
/* bottom = */
|
||||
if (sides and Gravity.BOTTOM == Gravity.BOTTOM) {
|
||||
barsInsets.bottom + basePaddings.bottom
|
||||
} else {
|
||||
v.paddingBottom
|
||||
},
|
||||
)
|
||||
return WindowInsetsCompat.Builder(insets)
|
||||
.setInsets(
|
||||
WindowInsetsCompat.Type.systemBars(),
|
||||
insetType,
|
||||
barsInsets.consumeRelative(
|
||||
v,
|
||||
start = sides and Gravity.START == Gravity.START,
|
||||
|
||||
@@ -4,5 +4,5 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
interface RecyclerViewOwner {
|
||||
|
||||
val recyclerView: RecyclerView
|
||||
val recyclerView: RecyclerView?
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package org.koitharu.kotatsu.core.util.ext
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.widget.ImageView
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil3.Extras
|
||||
import coil3.ImageLoader
|
||||
@@ -116,8 +116,8 @@ fun ImageRequest.Builder.defaultPlaceholders(context: Context): ImageRequest.Bui
|
||||
0.25f,
|
||||
)
|
||||
return placeholder(AnimatedPlaceholderDrawable(context))
|
||||
.fallback(ColorDrawable(context.getThemeColor(materialR.attr.colorSurfaceContainer)))
|
||||
.error(ColorDrawable(errorColor))
|
||||
.fallback(context.getThemeColor(materialR.attr.colorSurfaceContainer).toDrawable())
|
||||
.error(errorColor.toDrawable())
|
||||
}
|
||||
|
||||
private fun ImageView.ScaleType.toCoilScale(): Scale = if (this == ImageView.ScaleType.CENTER_CROP) {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package org.koitharu.kotatsu.core.util.ext
|
||||
|
||||
import android.util.DisplayMetrics
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.core.view.isEmpty
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.AdapterDelegateViewBindingViewHolder
|
||||
@@ -52,7 +56,7 @@ fun <T> RecyclerView.ViewHolder.getItem(clazz: Class<T>): T? {
|
||||
|
||||
val RecyclerView.isScrolledToTop: Boolean
|
||||
get() {
|
||||
if (childCount == 0) {
|
||||
if (isEmpty()) {
|
||||
return true
|
||||
}
|
||||
val holder = findViewHolderForAdapterPosition(0)
|
||||
@@ -72,3 +76,35 @@ val RecyclerView.LayoutManager?.isLayoutReversed
|
||||
is StaggeredGridLayoutManager -> reverseLayout
|
||||
else -> false
|
||||
}
|
||||
|
||||
// https://medium.com/flat-pack-tech/quickly-scroll-to-the-top-of-a-recyclerview-da15b717f3c4
|
||||
fun RecyclerView.smoothScrollToTop() {
|
||||
val layoutManager = layoutManager as? LinearLayoutManager ?: return
|
||||
|
||||
if (!context.isAnimationsEnabled) {
|
||||
layoutManager.scrollToPositionWithOffset(0, 0)
|
||||
return
|
||||
}
|
||||
|
||||
val smoothScroller = object : LinearSmoothScroller(context) {
|
||||
init {
|
||||
targetPosition = 0
|
||||
}
|
||||
|
||||
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics?) =
|
||||
super.calculateSpeedPerPixel(displayMetrics) / DEFAULT_SPEED_FACTOR
|
||||
}
|
||||
|
||||
val jumpBeforeScroll = layoutManager.findFirstVisibleItemPosition() > DEFAULT_JUMP_THRESHOLD
|
||||
if (jumpBeforeScroll) {
|
||||
layoutManager.scrollToPositionWithOffset(DEFAULT_JUMP_THRESHOLD, 0)
|
||||
doOnNextLayout {
|
||||
layoutManager.startSmoothScroll(smoothScroller)
|
||||
}
|
||||
} else {
|
||||
layoutManager.startSmoothScroll(smoothScroller)
|
||||
}
|
||||
}
|
||||
|
||||
private const val DEFAULT_JUMP_THRESHOLD = 30
|
||||
private const val DEFAULT_SPEED_FACTOR = 1f
|
||||
|
||||
@@ -9,10 +9,13 @@ import android.widget.Checkable
|
||||
import androidx.annotation.GravityInt
|
||||
import androidx.appcompat.widget.ActionMenuView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.descendants
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
||||
@@ -72,6 +75,11 @@ fun ViewPager2.findCurrentViewHolder(): ViewHolder? {
|
||||
return recyclerView?.findViewHolderForAdapterPosition(currentItem)
|
||||
}
|
||||
|
||||
fun FragmentManager.findCurrentPagerFragment(pager: ViewPager2): Fragment? {
|
||||
val currentId = pager.adapter?.getItemId(pager.currentItem) ?: pager.currentItem
|
||||
return findFragmentByTag("f$currentId")
|
||||
}
|
||||
|
||||
fun View.resetTransformations() {
|
||||
alpha = 1f
|
||||
translationX = 0f
|
||||
@@ -187,12 +195,18 @@ fun Chip.setProgressIcon() {
|
||||
progressDrawable.start()
|
||||
}
|
||||
|
||||
private fun View.marginsSnapshot(): Insets = (layoutParams as? ViewGroup.MarginLayoutParams)?.let { lp ->
|
||||
Insets.of(lp.leftMargin, lp.topMargin, lp.rightMargin, lp.bottomMargin)
|
||||
} ?: Insets.NONE
|
||||
|
||||
private fun View.paddingSnapshot(): Insets = Insets.of(paddingLeft, paddingTop, paddingRight, paddingBottom)
|
||||
|
||||
fun View.consumeInsetsAsPadding(@GravityInt sides: Int) = ViewCompat.setOnApplyWindowInsetsListener(
|
||||
this,
|
||||
InsetsToPaddingListener(sides),
|
||||
InsetsToPaddingListener(sides, paddingSnapshot()),
|
||||
)
|
||||
|
||||
fun View.consumeInsetsAsMargins(@GravityInt sides: Int) = ViewCompat.setOnApplyWindowInsetsListener(
|
||||
this,
|
||||
InsetsToMarginsListener(sides),
|
||||
InsetsToMarginsListener(sides, marginsSnapshot()),
|
||||
)
|
||||
|
||||
@@ -4,6 +4,8 @@ import org.koitharu.kotatsu.core.model.isLocal
|
||||
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.util.ifNullOrEmpty
|
||||
import org.koitharu.kotatsu.parsers.util.nullIfEmpty
|
||||
import org.koitharu.kotatsu.reader.data.filterChapters
|
||||
|
||||
data class MangaDetails(
|
||||
@@ -29,6 +31,12 @@ data class MangaDetails(
|
||||
val local: LocalManga?
|
||||
get() = localManga ?: if (manga.isLocal) LocalManga(manga) else null
|
||||
|
||||
val coverUrl: String?
|
||||
get() = manga.largeCoverUrl
|
||||
.ifNullOrEmpty { manga.largeCoverUrl }
|
||||
.ifNullOrEmpty { localManga?.manga?.coverUrl }
|
||||
?.nullIfEmpty()
|
||||
|
||||
fun toManga() = manga
|
||||
|
||||
fun filterChapters(branch: String?) = MangaDetails(
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.text.Spanned
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import androidx.core.text.getSpans
|
||||
import androidx.core.text.parseAsHtml
|
||||
import coil3.request.CachePolicy
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -15,6 +16,7 @@ import kotlinx.coroutines.runInterruptible
|
||||
import okio.IOException
|
||||
import org.koitharu.kotatsu.core.model.isLocal
|
||||
import org.koitharu.kotatsu.core.nav.MangaIntent
|
||||
import org.koitharu.kotatsu.core.parser.CachingMangaRepository
|
||||
import org.koitharu.kotatsu.core.parser.MangaDataRepository
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.util.ext.peek
|
||||
@@ -40,7 +42,7 @@ class DetailsLoadUseCase @Inject constructor(
|
||||
private val newChaptersUseCaseProvider: Provider<CheckNewChaptersUseCase>,
|
||||
) {
|
||||
|
||||
operator fun invoke(intent: MangaIntent): Flow<MangaDetails> = channelFlow {
|
||||
operator fun invoke(intent: MangaIntent, force: Boolean): Flow<MangaDetails> = channelFlow {
|
||||
val manga = requireNotNull(mangaDataRepository.resolveIntent(intent)) {
|
||||
"Cannot resolve intent $intent"
|
||||
}.let { m ->
|
||||
@@ -59,7 +61,7 @@ class DetailsLoadUseCase @Inject constructor(
|
||||
null
|
||||
}
|
||||
try {
|
||||
val details = getDetails(manga)
|
||||
val details = getDetails(manga, force)
|
||||
launch { mangaDataRepository.updateChapters(details) }
|
||||
launch { updateTracker(details) }
|
||||
send(
|
||||
@@ -92,9 +94,13 @@ class DetailsLoadUseCase @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getDetails(seed: Manga) = runCatchingCancellable {
|
||||
private suspend fun getDetails(seed: Manga, force: Boolean) = runCatchingCancellable {
|
||||
val repository = mangaRepositoryFactory.create(seed.source)
|
||||
repository.getDetails(seed)
|
||||
if (repository is CachingMangaRepository) {
|
||||
repository.getDetails(seed, if (force) CachePolicy.WRITE_ONLY else CachePolicy.ENABLED)
|
||||
} else {
|
||||
repository.getDetails(seed)
|
||||
}
|
||||
}.recoverNotNull { e ->
|
||||
if (e is NotFoundException) {
|
||||
recoverUseCase(seed)
|
||||
|
||||
@@ -111,7 +111,6 @@ import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.util.ifNullOrEmpty
|
||||
import org.koitharu.kotatsu.parsers.util.nullIfEmpty
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo
|
||||
import org.koitharu.kotatsu.search.domain.SearchKind
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.roundToInt
|
||||
import com.google.android.material.R as materialR
|
||||
@@ -171,6 +170,7 @@ class DetailsActivity :
|
||||
|
||||
val appRouter = router
|
||||
viewModel.mangaDetails.filterNotNull().observe(this, ::onMangaUpdated)
|
||||
viewModel.coverUrl.observe(this, ::loadCover)
|
||||
viewModel.onMangaRemoved.observeEvent(this, ::onMangaRemoved)
|
||||
viewModel.onError
|
||||
.filterNot { appRouter.isChapterPagesSheetShown() }
|
||||
@@ -217,8 +217,9 @@ class DetailsActivity :
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.textView_author -> {
|
||||
val author = viewModel.manga.value?.author ?: return
|
||||
router.openSearch(author, SearchKind.AUTHOR)
|
||||
val manga = viewModel.manga.value
|
||||
val author = manga?.author ?: return
|
||||
router.showAuthorDialog(author, manga.source)
|
||||
}
|
||||
|
||||
R.id.textView_source -> {
|
||||
@@ -239,7 +240,7 @@ class DetailsActivity :
|
||||
R.id.imageView_cover -> {
|
||||
val manga = viewModel.manga.value ?: return
|
||||
router.openImage(
|
||||
url = manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl } ?: return,
|
||||
url = viewModel.coverUrl.value ?: return,
|
||||
source = manga.source,
|
||||
anchor = v,
|
||||
)
|
||||
@@ -443,7 +444,6 @@ class DetailsActivity :
|
||||
|
||||
private fun onMangaUpdated(details: MangaDetails) {
|
||||
val manga = details.toManga()
|
||||
loadCover(manga)
|
||||
with(viewBinding) {
|
||||
textViewTitle.text = manga.title
|
||||
textViewSubtitle.textAndVisible = manga.altTitle
|
||||
@@ -560,8 +560,7 @@ class DetailsActivity :
|
||||
viewBinding.chipsTags.setChips(listMapper.mapTags(manga.tags))
|
||||
}
|
||||
|
||||
private fun loadCover(manga: Manga) {
|
||||
val imageUrl = manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }
|
||||
private fun loadCover(imageUrl: String?) {
|
||||
val lastResult = CoilUtils.result(viewBinding.imageViewCover)
|
||||
if (lastResult is SuccessResult && lastResult.request.data == imageUrl) {
|
||||
return
|
||||
@@ -571,10 +570,9 @@ class DetailsActivity :
|
||||
.size(CoverSizeResolver(viewBinding.imageViewCover))
|
||||
.scale(Scale.FILL)
|
||||
.data(imageUrl)
|
||||
.mangaSourceExtra(manga.source)
|
||||
.mangaSourceExtra(viewModel.getMangaOrNull()?.source)
|
||||
.crossfade(this)
|
||||
.lifecycle(this)
|
||||
.placeholderMemoryCacheKey(manga.coverUrl)
|
||||
val previousDrawable = lastResult?.drawable
|
||||
if (previousDrawable != null) {
|
||||
request.fallback(previousDrawable)
|
||||
|
||||
@@ -176,7 +176,7 @@ class DetailsViewModel @Inject constructor(
|
||||
get() = selectedBranch.value
|
||||
|
||||
init {
|
||||
loadingJob = doLoad()
|
||||
loadingJob = doLoad(force = false)
|
||||
launchJob(Dispatchers.Default) {
|
||||
val manga = mangaDetails.firstOrNull { !it?.chapters.isNullOrEmpty() } ?: return@launchJob
|
||||
val h = history.firstOrNull()
|
||||
@@ -192,7 +192,7 @@ class DetailsViewModel @Inject constructor(
|
||||
|
||||
fun reload() {
|
||||
loadingJob.cancel()
|
||||
loadingJob = doLoad()
|
||||
loadingJob = doLoad(force = true)
|
||||
}
|
||||
|
||||
fun updateScrobbling(index: Int, rating: Float, status: ScrobblingStatus?) {
|
||||
@@ -223,8 +223,8 @@ class DetailsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun doLoad() = launchLoadingJob(Dispatchers.Default) {
|
||||
detailsLoadUseCase.invoke(intent)
|
||||
private fun doLoad(force: Boolean) = launchLoadingJob(Dispatchers.Default) {
|
||||
detailsLoadUseCase.invoke(intent, force)
|
||||
.onEachWhile {
|
||||
if (it.allChapters.isNotEmpty()) {
|
||||
val manga = it.toManga()
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
|
||||
@@ -20,13 +21,16 @@ import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetCallback
|
||||
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
|
||||
import org.koitharu.kotatsu.core.ui.util.ActionModeListener
|
||||
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
|
||||
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
||||
import org.koitharu.kotatsu.core.util.ext.doOnPageChanged
|
||||
import org.koitharu.kotatsu.core.util.ext.findCurrentPagerFragment
|
||||
import org.koitharu.kotatsu.core.util.ext.menuView
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.core.util.ext.recyclerView
|
||||
import org.koitharu.kotatsu.core.util.ext.setTabsEnabled
|
||||
import org.koitharu.kotatsu.core.util.ext.smoothScrollToTop
|
||||
import org.koitharu.kotatsu.databinding.SheetChaptersPagesBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsViewModel
|
||||
import org.koitharu.kotatsu.details.ui.ReadButtonDelegate
|
||||
@@ -34,7 +38,10 @@ import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), ActionModeListener, AdaptiveSheetCallback {
|
||||
class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(),
|
||||
TabLayout.OnTabSelectedListener,
|
||||
ActionModeListener,
|
||||
AdaptiveSheetCallback {
|
||||
|
||||
@Inject
|
||||
lateinit var settings: AppSettings
|
||||
@@ -63,6 +70,7 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
|
||||
binding.pager.adapter = adapter
|
||||
binding.pager.doOnPageChanged(::onPageChanged)
|
||||
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
|
||||
binding.tabs.addOnTabSelectedListener(this)
|
||||
binding.pager.setCurrentItem(defaultTab, false)
|
||||
binding.tabs.isVisible = adapter.itemCount > 1
|
||||
|
||||
@@ -109,6 +117,17 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
|
||||
viewBinding?.toolbar?.menuView?.isVisible = state != STATE_COLLAPSED
|
||||
}
|
||||
|
||||
override fun onTabSelected(tab: TabLayout.Tab?) = Unit
|
||||
|
||||
override fun onTabUnselected(tab: TabLayout.Tab?) = Unit
|
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab?) {
|
||||
val f = childFragmentManager.findCurrentPagerFragment(
|
||||
viewBinding?.pager ?: return,
|
||||
) as? RecyclerViewOwner ?: return
|
||||
f.recyclerView?.smoothScrollToTop()
|
||||
}
|
||||
|
||||
override fun expandAndLock() {
|
||||
super.expandAndLock()
|
||||
adjustLockState()
|
||||
|
||||
@@ -71,6 +71,10 @@ abstract class ChaptersPagesViewModel(
|
||||
.withErrorHandling()
|
||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null)
|
||||
|
||||
val coverUrl = mangaDetails.map { x -> x?.coverUrl }
|
||||
.withErrorHandling()
|
||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null)
|
||||
|
||||
val isChaptersReversed = settings.observeAsStateFlow(
|
||||
scope = viewModelScope + Dispatchers.Default,
|
||||
key = AppSettings.KEY_REVERSE_CHAPTERS,
|
||||
|
||||
@@ -11,6 +11,7 @@ import android.view.ViewGroup
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil3.ImageLoader
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -26,6 +27,7 @@ import org.koitharu.kotatsu.core.ui.BaseFragment
|
||||
import org.koitharu.kotatsu.core.ui.list.ListSelectionController
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.ui.util.PagerNestedScrollHelper
|
||||
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
||||
import org.koitharu.kotatsu.core.util.ext.consumeInsetsAsPadding
|
||||
import org.koitharu.kotatsu.core.util.ext.findAppCompatDelegate
|
||||
@@ -43,6 +45,7 @@ import javax.inject.Inject
|
||||
@AndroidEntryPoint
|
||||
class BookmarksFragment : BaseFragment<FragmentMangaBookmarksBinding>(),
|
||||
OnListItemClickListener<Bookmark>,
|
||||
RecyclerViewOwner,
|
||||
ListSelectionController.Callback {
|
||||
|
||||
private val activityViewModel by ChaptersPagesViewModel.ActivityVMLazy(this)
|
||||
@@ -54,6 +57,9 @@ class BookmarksFragment : BaseFragment<FragmentMangaBookmarksBinding>(),
|
||||
@Inject
|
||||
lateinit var settings: AppSettings
|
||||
|
||||
override val recyclerView: RecyclerView?
|
||||
get() = viewBinding?.recyclerView
|
||||
|
||||
private var bookmarksAdapter: BookmarksAdapter? = null
|
||||
private var spanResolver: GridSpanResolver? = null
|
||||
private var selectionController: ListSelectionController? = null
|
||||
|
||||
@@ -12,6 +12,7 @@ import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.chip.Chip
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -25,6 +26,7 @@ import org.koitharu.kotatsu.core.ui.BaseFragment
|
||||
import org.koitharu.kotatsu.core.ui.list.ListSelectionController
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.ui.util.PagerNestedScrollHelper
|
||||
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
|
||||
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
||||
import org.koitharu.kotatsu.core.util.RecyclerViewScrollCallback
|
||||
import org.koitharu.kotatsu.core.util.ext.findAppCompatDelegate
|
||||
@@ -48,6 +50,7 @@ class ChaptersFragment :
|
||||
BaseFragment<FragmentChaptersBinding>(),
|
||||
OnListItemClickListener<ChapterListItem>,
|
||||
OnApplyWindowInsetsListener,
|
||||
RecyclerViewOwner,
|
||||
ChipsView.OnChipClickListener {
|
||||
|
||||
private val viewModel by ChaptersPagesViewModel.ActivityVMLazy(this)
|
||||
@@ -55,6 +58,9 @@ class ChaptersFragment :
|
||||
private var chaptersAdapter: ChaptersAdapter? = null
|
||||
private var selectionController: ListSelectionController? = null
|
||||
|
||||
override val recyclerView: RecyclerView?
|
||||
get() = viewBinding?.recyclerViewChapters
|
||||
|
||||
override fun onCreateViewBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.koitharu.kotatsu.core.ui.list.BoundsScrollListener
|
||||
import org.koitharu.kotatsu.core.ui.list.ListSelectionController
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.ui.util.PagerNestedScrollHelper
|
||||
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
|
||||
import org.koitharu.kotatsu.core.util.RecyclerViewScrollCallback
|
||||
import org.koitharu.kotatsu.core.util.ext.consumeInsetsAsPadding
|
||||
import org.koitharu.kotatsu.core.util.ext.findAppCompatDelegate
|
||||
@@ -55,6 +56,7 @@ import kotlin.math.roundToInt
|
||||
class PagesFragment :
|
||||
BaseFragment<FragmentPagesBinding>(),
|
||||
OnListItemClickListener<PageThumbnail>,
|
||||
RecyclerViewOwner,
|
||||
ListSelectionController.Callback {
|
||||
|
||||
@Inject
|
||||
@@ -77,6 +79,9 @@ class PagesFragment :
|
||||
|
||||
private val spanSizeLookup = SpanSizeLookup()
|
||||
|
||||
override val recyclerView: RecyclerView?
|
||||
get() = viewBinding?.recyclerView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
pageSaveHelper = pageSaveHelperFactory.create(this)
|
||||
|
||||
@@ -1,30 +1,5 @@
|
||||
package org.koitharu.kotatsu.details.ui.related
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.commit
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import org.koitharu.kotatsu.core.ui.FragmentContainerActivity
|
||||
|
||||
@AndroidEntryPoint
|
||||
class RelatedMangaActivity : BaseActivity<ActivityContainerBinding>(), AppBarOwner {
|
||||
|
||||
override val appBar: AppBarLayout
|
||||
get() = viewBinding.appbar
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityContainerBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
val fm = supportFragmentManager
|
||||
if (fm.findFragmentById(R.id.container) == null) {
|
||||
fm.commit {
|
||||
setReorderingAllowed(true)
|
||||
replace(R.id.container, RelatedListFragment::class.java, intent.extras)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class RelatedMangaActivity : FragmentContainerActivity(RelatedListFragment::class.java)
|
||||
|
||||
@@ -60,8 +60,8 @@ class ExploreFragment :
|
||||
private var exploreAdapter: ExploreAdapter? = null
|
||||
private var sourceSelectionController: ListSelectionController? = null
|
||||
|
||||
override val recyclerView: RecyclerView
|
||||
get() = requireViewBinding().recyclerView
|
||||
override val recyclerView: RecyclerView?
|
||||
get() = viewBinding?.recyclerView
|
||||
|
||||
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentExploreBinding {
|
||||
return FragmentExploreBinding.inflate(inflater, container, false)
|
||||
|
||||
@@ -1,33 +1,18 @@
|
||||
package org.koitharu.kotatsu.favourites.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.commit
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.nav.AppRouter
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
||||
import org.koitharu.kotatsu.core.ui.FragmentContainerActivity
|
||||
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment
|
||||
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID
|
||||
|
||||
@AndroidEntryPoint
|
||||
class FavouritesActivity : BaseActivity<ActivityContainerBinding>() {
|
||||
class FavouritesActivity : FragmentContainerActivity(FavouritesListFragment::class.java) {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityContainerBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
val categoryTitle = intent.getStringExtra(AppRouter.KEY_TITLE)
|
||||
if (categoryTitle != null) {
|
||||
title = categoryTitle
|
||||
}
|
||||
val fm = supportFragmentManager
|
||||
if (fm.findFragmentById(R.id.container) == null) {
|
||||
fm.commit {
|
||||
setReorderingAllowed(true)
|
||||
val fragment = FavouritesListFragment.newInstance(intent.getLongExtra(AppRouter.KEY_ID, NO_ID))
|
||||
replace(R.id.container, fragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import android.view.ViewStub
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil3.ImageLoader
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@@ -16,9 +18,11 @@ import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.nav.router
|
||||
import org.koitharu.kotatsu.core.ui.BaseFragment
|
||||
import org.koitharu.kotatsu.core.ui.util.ActionModeListener
|
||||
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
||||
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
|
||||
import org.koitharu.kotatsu.core.util.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.core.util.ext.findCurrentPagerFragment
|
||||
import org.koitharu.kotatsu.core.util.ext.newImageRequest
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
@@ -30,14 +34,20 @@ import org.koitharu.kotatsu.databinding.ItemEmptyStateBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class FavouritesContainerFragment : BaseFragment<FragmentFavouritesContainerBinding>(), ActionModeListener,
|
||||
ViewStub.OnInflateListener, View.OnClickListener {
|
||||
class FavouritesContainerFragment : BaseFragment<FragmentFavouritesContainerBinding>(),
|
||||
ActionModeListener,
|
||||
RecyclerViewOwner,
|
||||
ViewStub.OnInflateListener,
|
||||
View.OnClickListener {
|
||||
|
||||
@Inject
|
||||
lateinit var coil: ImageLoader
|
||||
|
||||
private val viewModel: FavouritesContainerViewModel by viewModels()
|
||||
|
||||
override val recyclerView: RecyclerView?
|
||||
get() = (findCurrentFragment() as? RecyclerViewOwner)?.recyclerView
|
||||
|
||||
override fun onCreateViewBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
@@ -103,4 +113,10 @@ class FavouritesContainerFragment : BaseFragment<FragmentFavouritesContainerBind
|
||||
stubEmpty.isVisible = isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
private fun findCurrentFragment(): Fragment? {
|
||||
return childFragmentManager.findCurrentPagerFragment(
|
||||
viewBinding?.pager ?: return null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.fragment.app.viewModels
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.nav.AppRouter
|
||||
import org.koitharu.kotatsu.core.ui.list.ListSelectionController
|
||||
import org.koitharu.kotatsu.core.util.ext.sortedByOrdinal
|
||||
import org.koitharu.kotatsu.core.util.ext.withArgs
|
||||
@@ -90,10 +91,9 @@ class FavouritesListFragment : MangaListFragment(), PopupMenu.OnMenuItemClickLis
|
||||
companion object {
|
||||
|
||||
const val NO_ID = 0L
|
||||
const val ARG_CATEGORY_ID = "category_id"
|
||||
|
||||
fun newInstance(categoryId: Long) = FavouritesListFragment().withArgs(1) {
|
||||
putLong(ARG_CATEGORY_ID, categoryId)
|
||||
putLong(AppRouter.KEY_ID, categoryId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.nav.AppRouter
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
@@ -25,7 +26,6 @@ import org.koitharu.kotatsu.core.util.ext.flattenLatest
|
||||
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
|
||||
import org.koitharu.kotatsu.favourites.domain.FavoritesListQuickFilter
|
||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.ARG_CATEGORY_ID
|
||||
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID
|
||||
import org.koitharu.kotatsu.history.domain.MarkAsReadUseCase
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
@@ -54,7 +54,7 @@ class FavouritesListViewModel @Inject constructor(
|
||||
downloadScheduler: DownloadWorker.Scheduler,
|
||||
) : MangaListViewModel(settings, downloadScheduler), QuickFilterListener {
|
||||
|
||||
val categoryId: Long = savedStateHandle[ARG_CATEGORY_ID] ?: NO_ID
|
||||
val categoryId: Long = savedStateHandle[AppRouter.KEY_ID] ?: NO_ID
|
||||
private val quickFilter = quickFilterFactory.create(categoryId)
|
||||
private val refreshTrigger = MutableStateFlow(Any())
|
||||
private val limit = MutableStateFlow(PAGE_SIZE)
|
||||
|
||||
@@ -267,6 +267,25 @@ class FilterCoordinator @Inject constructor(
|
||||
currentListFilter.value = value
|
||||
}
|
||||
|
||||
fun setAdjusted(value: MangaListFilter) {
|
||||
var newFilter = value
|
||||
if (!newFilter.author.isNullOrEmpty() && !capabilities.isAuthorSearchSupported) {
|
||||
newFilter = newFilter.copy(
|
||||
query = newFilter.author,
|
||||
author = null,
|
||||
)
|
||||
}
|
||||
if (!capabilities.isSearchSupported && !newFilter.query.isNullOrEmpty()) {
|
||||
newFilter = newFilter.copy(
|
||||
query = null,
|
||||
)
|
||||
}
|
||||
if (!newFilter.query.isNullOrEmpty() && !newFilter.hasNonSearchOptions() && !capabilities.isSearchWithFiltersSupported) {
|
||||
newFilter = MangaListFilter(query = newFilter.query)
|
||||
}
|
||||
set(newFilter)
|
||||
}
|
||||
|
||||
fun setQuery(value: String?) {
|
||||
val newQuery = value?.trim()?.nullIfEmpty()
|
||||
currentListFilter.update { oldValue ->
|
||||
|
||||
@@ -1,33 +1,5 @@
|
||||
package org.koitharu.kotatsu.history.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.commit
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import org.koitharu.kotatsu.core.ui.FragmentContainerActivity
|
||||
|
||||
@AndroidEntryPoint
|
||||
class HistoryActivity :
|
||||
BaseActivity<ActivityContainerBinding>(),
|
||||
AppBarOwner {
|
||||
|
||||
override val appBar: AppBarLayout
|
||||
get() = viewBinding.appbar
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityContainerBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
val fm = supportFragmentManager
|
||||
if (fm.findFragmentById(R.id.container) == null) {
|
||||
fm.commit {
|
||||
setReorderingAllowed(true)
|
||||
val fragment = HistoryListFragment()
|
||||
replace(R.id.container, fragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class HistoryActivity : FragmentContainerActivity(HistoryListFragment::class.java)
|
||||
|
||||
@@ -12,6 +12,7 @@ import androidx.annotation.CallSuper
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.collection.ArraySet
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import coil3.ImageLoader
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@@ -31,6 +32,7 @@ import org.koitharu.kotatsu.core.ui.list.FitHeightLinearLayoutManager
|
||||
import org.koitharu.kotatsu.core.ui.list.ListSelectionController
|
||||
import org.koitharu.kotatsu.core.ui.list.PaginationScrollListener
|
||||
import org.koitharu.kotatsu.core.ui.list.fastscroll.FastScroller
|
||||
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
||||
import org.koitharu.kotatsu.core.ui.widgets.TipView
|
||||
import org.koitharu.kotatsu.core.util.ShareHelper
|
||||
@@ -62,6 +64,7 @@ abstract class MangaListFragment :
|
||||
BaseFragment<FragmentListBinding>(),
|
||||
PaginationScrollListener.Callback,
|
||||
MangaListListener,
|
||||
RecyclerViewOwner,
|
||||
SwipeRefreshLayout.OnRefreshListener,
|
||||
ListSelectionController.Callback,
|
||||
FastScroller.FastScrollListener {
|
||||
@@ -87,6 +90,9 @@ abstract class MangaListFragment :
|
||||
protected val selectedItems: Set<Manga>
|
||||
get() = collectSelectedItems()
|
||||
|
||||
override val recyclerView: RecyclerView?
|
||||
get() = viewBinding?.recyclerView
|
||||
|
||||
override fun onCreateViewBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
@@ -159,8 +165,7 @@ abstract class MangaListFragment :
|
||||
|
||||
override fun onTagClick(manga: Manga, tag: MangaTag, view: View) {
|
||||
if (selectionController?.onItemClick(manga.id) != true) {
|
||||
// TODO dialog
|
||||
router.openList(tag)
|
||||
router.showTagDialog(tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,7 @@ import org.koitharu.kotatsu.bookmarks.ui.AllBookmarksFragment
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.NavItem
|
||||
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
|
||||
import org.koitharu.kotatsu.core.util.ext.firstVisibleItemPosition
|
||||
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
|
||||
import org.koitharu.kotatsu.core.util.ext.smoothScrollToTop
|
||||
import org.koitharu.kotatsu.explore.ui.ExploreFragment
|
||||
import org.koitharu.kotatsu.favourites.ui.container.FavouritesContainerFragment
|
||||
import org.koitharu.kotatsu.history.ui.HistoryListFragment
|
||||
@@ -64,15 +63,8 @@ class MainNavigationDelegate(
|
||||
|
||||
override fun onNavigationItemReselected(item: MenuItem) {
|
||||
val fragment = fragmentManager.findFragmentByTag(TAG_PRIMARY)
|
||||
if (fragment == null || fragment !is RecyclerViewOwner || fragment.view == null) {
|
||||
return
|
||||
}
|
||||
val recyclerView = fragment.recyclerView
|
||||
if (recyclerView.context.isAnimationsEnabled) {
|
||||
recyclerView.smoothScrollToPosition(0)
|
||||
} else {
|
||||
recyclerView.firstVisibleItemPosition = 0
|
||||
}
|
||||
val recyclerView = (fragment as? RecyclerViewOwner)?.recyclerView ?: return
|
||||
recyclerView.smoothScrollToTop()
|
||||
}
|
||||
|
||||
override fun handleOnBackPressed() {
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.view.Gravity
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
@@ -20,6 +21,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.consumeInsetsAsPadding
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.core.util.ext.getParcelableExtraCompat
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
@@ -41,6 +43,7 @@ class ProtectActivity :
|
||||
super.onCreate(savedInstanceState)
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
setContentView(ActivityProtectBinding.inflate(layoutInflater))
|
||||
viewBinding.root.consumeInsetsAsPadding(Gravity.FILL)
|
||||
viewBinding.editPassword.setOnEditorActionListener(this)
|
||||
viewBinding.editPassword.addTextChangedListener(this)
|
||||
viewBinding.buttonNext.setOnClickListener(this)
|
||||
|
||||
@@ -394,7 +394,7 @@ class ReaderViewModel @Inject constructor(
|
||||
|
||||
private fun loadImpl() {
|
||||
loadingJob = launchLoadingJob(Dispatchers.Default) {
|
||||
val details = detailsLoadUseCase.invoke(intent).first { x -> x.isLoaded }
|
||||
val details = detailsLoadUseCase.invoke(intent, force = false).first { x -> x.isLoaded }
|
||||
mangaDetails.value = details
|
||||
chaptersLoader.init(details)
|
||||
val manga = details.toManga()
|
||||
|
||||
@@ -4,11 +4,13 @@ import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import androidx.core.net.toUri
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.consumeInsetsAsPadding
|
||||
import org.koitharu.kotatsu.databinding.ActivityKitsuAuthBinding
|
||||
import org.koitharu.kotatsu.parsers.util.urlEncoded
|
||||
|
||||
@@ -19,6 +21,7 @@ class KitsuAuthActivity : BaseActivity<ActivityKitsuAuthBinding>(), View.OnClick
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityKitsuAuthBinding.inflate(layoutInflater))
|
||||
viewBinding.root.consumeInsetsAsPadding(Gravity.FILL)
|
||||
viewBinding.buttonCancel.setOnClickListener(this)
|
||||
viewBinding.buttonDone.setOnClickListener(this)
|
||||
viewBinding.editEmail.addTextChangedListener(this)
|
||||
|
||||
@@ -218,7 +218,7 @@ class MangaListActivity :
|
||||
filterOwner.filterCoordinator.setSortOrder(sortOrder)
|
||||
}
|
||||
if (filter != null) {
|
||||
filterOwner.filterCoordinator.set(filter)
|
||||
filterOwner.filterCoordinator.setAdjusted(filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ class NavConfigFragment : BaseFragment<FragmentSettingsSourcesBinding>(), Recycl
|
||||
private var reorderHelper: ItemTouchHelper? = null
|
||||
private val viewModel by viewModels<NavConfigViewModel>()
|
||||
|
||||
override val recyclerView: RecyclerView
|
||||
get() = requireViewBinding().recyclerView
|
||||
override val recyclerView: RecyclerView?
|
||||
get() = viewBinding?.recyclerView
|
||||
|
||||
override fun onCreateViewBinding(
|
||||
inflater: LayoutInflater,
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.view.Gravity
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
@@ -17,6 +18,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.consumeInsetsAsPadding
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.databinding.ActivitySetupProtectBinding
|
||||
@@ -37,6 +39,7 @@ class ProtectSetupActivity :
|
||||
super.onCreate(savedInstanceState)
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
setContentView(ActivitySetupProtectBinding.inflate(layoutInflater))
|
||||
viewBinding.root.consumeInsetsAsPadding(Gravity.FILL)
|
||||
viewBinding.editPassword.addTextChangedListener(this)
|
||||
viewBinding.editPassword.setOnEditorActionListener(this)
|
||||
viewBinding.buttonNext.setOnClickListener(this)
|
||||
|
||||
@@ -39,7 +39,7 @@ class ReaderTapGridConfigActivity : BaseActivity<ActivityReaderTapActionsBinding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityReaderTapActionsBinding.inflate(layoutInflater))
|
||||
viewBinding.root.consumeInsetsAsPadding(Gravity.START or Gravity.END or Gravity.BOTTOM or Gravity.TOP)
|
||||
viewBinding.root.consumeInsetsAsPadding(Gravity.FILL)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
controls[TapGridArea.TOP_LEFT] = viewBinding.textViewTopLeft
|
||||
controls[TapGridArea.TOP_CENTER] = viewBinding.textViewTopCenter
|
||||
|
||||
@@ -56,8 +56,8 @@ class SourcesManageFragment :
|
||||
private var sourcesAdapter: SourceConfigAdapter? = null
|
||||
private val viewModel by viewModels<SourcesManageViewModel>()
|
||||
|
||||
override val recyclerView: RecyclerView
|
||||
get() = requireViewBinding().recyclerView
|
||||
override val recyclerView: RecyclerView?
|
||||
get() = viewBinding?.recyclerView
|
||||
|
||||
override fun onCreateViewBinding(
|
||||
inflater: LayoutInflater,
|
||||
|
||||
@@ -40,6 +40,7 @@ abstract class StatsDao {
|
||||
favouriteCategories: Set<Long>
|
||||
): Map<MangaEntity, Long> {
|
||||
val conditions = ArrayList<String>()
|
||||
conditions.add("(SELECT deleted_at FROM history WHERE history.manga_id = stats.manga_id) = 0")
|
||||
conditions.add("stats.started_at >= $fromDate")
|
||||
if (favouriteCategories.isNotEmpty()) {
|
||||
val ids = favouriteCategories.joinToString(",")
|
||||
|
||||
@@ -1,32 +1,5 @@
|
||||
package org.koitharu.kotatsu.suggestions.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.commit
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import org.koitharu.kotatsu.core.ui.FragmentContainerActivity
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SuggestionsActivity :
|
||||
BaseActivity<ActivityContainerBinding>(),
|
||||
AppBarOwner {
|
||||
|
||||
override val appBar: AppBarLayout
|
||||
get() = viewBinding.appbar
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityContainerBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
val fm = supportFragmentManager
|
||||
if (fm.findFragmentById(R.id.container) == null) {
|
||||
fm.commit {
|
||||
setReorderingAllowed(true)
|
||||
replace(R.id.container, SuggestionsFragment::class.java, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class SuggestionsActivity : FragmentContainerActivity(SuggestionsFragment::class.java)
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.accounts.AccountManager
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.Toast
|
||||
@@ -20,6 +21,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.consumeInsetsAsPadding
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.core.util.ext.getParcelableExtraCompat
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
@@ -40,6 +42,7 @@ class SyncAuthActivity : BaseActivity<ActivitySyncAuthBinding>(), View.OnClickLi
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivitySyncAuthBinding.inflate(layoutInflater))
|
||||
viewBinding.root.consumeInsetsAsPadding(Gravity.FILL)
|
||||
accountAuthenticatorResponse =
|
||||
intent.getParcelableExtraCompat(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE)
|
||||
accountAuthenticatorResponse?.onRequestContinued()
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.koitharu.kotatsu.core.ui.BaseFragment
|
||||
import org.koitharu.kotatsu.core.ui.list.PaginationScrollListener
|
||||
import org.koitharu.kotatsu.core.ui.list.RecyclerScrollKeeper
|
||||
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
|
||||
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
||||
import org.koitharu.kotatsu.core.ui.widgets.TipView
|
||||
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
|
||||
@@ -38,13 +39,18 @@ import javax.inject.Inject
|
||||
class FeedFragment :
|
||||
BaseFragment<FragmentListBinding>(),
|
||||
PaginationScrollListener.Callback,
|
||||
MangaListListener, SwipeRefreshLayout.OnRefreshListener {
|
||||
RecyclerViewOwner,
|
||||
MangaListListener,
|
||||
SwipeRefreshLayout.OnRefreshListener {
|
||||
|
||||
@Inject
|
||||
lateinit var coil: ImageLoader
|
||||
|
||||
private val viewModel by viewModels<FeedViewModel>()
|
||||
|
||||
override val recyclerView: RecyclerView?
|
||||
get() = viewBinding?.recyclerView
|
||||
|
||||
override fun onCreateViewBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
||||
@@ -1,33 +1,5 @@
|
||||
package org.koitharu.kotatsu.tracker.ui.updates
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.commit
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import org.koitharu.kotatsu.core.ui.FragmentContainerActivity
|
||||
|
||||
@AndroidEntryPoint
|
||||
class UpdatesActivity :
|
||||
BaseActivity<ActivityContainerBinding>(),
|
||||
AppBarOwner {
|
||||
|
||||
override val appBar: AppBarLayout
|
||||
get() = viewBinding.appbar
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityContainerBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
val fm = supportFragmentManager
|
||||
if (fm.findFragmentById(R.id.container) == null) {
|
||||
fm.commit {
|
||||
setReorderingAllowed(true)
|
||||
val fragment = UpdatesFragment.newInstance()
|
||||
replace(R.id.container, fragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class UpdatesActivity : FragmentContainerActivity(UpdatesFragment::class.java)
|
||||
|
||||
@@ -38,9 +38,4 @@ class UpdatesFragment : MangaListFragment() {
|
||||
else -> super.onActionItemClicked(controller, mode, item)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance() = UpdatesFragment()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="32dp">
|
||||
|
||||
<ImageView
|
||||
@@ -16,32 +16,38 @@
|
||||
android:scaleType="fitCenter"
|
||||
tools:src="@drawable/ic_empty_favourites" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textPrimary"
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:gravity="center"
|
||||
android:textAppearance="?attr/textAppearanceTitleLarge"
|
||||
tools:text="@tools:sample/lorem[3]" />
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textSecondary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:gravity="center"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
tools:text="@tools:sample/lorem[15]" />
|
||||
<TextView
|
||||
android:id="@+id/textPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:textAppearance="?attr/textAppearanceTitleLarge"
|
||||
tools:text="@tools:sample/lorem[3]" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_retry"
|
||||
style="@style/Widget.Material3.Button.TonalButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:visibility="gone"
|
||||
tools:text="@string/try_again"
|
||||
tools:visibility="visible" />
|
||||
<TextView
|
||||
android:id="@+id/textSecondary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:maxWidth="320dp"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
tools:text="@tools:sample/lorem[15]" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_retry"
|
||||
style="?materialButtonTonalStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:visibility="gone"
|
||||
tools:text="@string/try_again"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
115
app/src/main/res/layout-w600dp-land/activity_stats.xml
Normal file
115
app/src/main/res/layout-w600dp-land/activity_stats.xml
Normal file
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline_center"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_scrollFlags="noScroll"
|
||||
tools:title="@string/reading_stats" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/scrollView_chips"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:scrollIndicators="start"
|
||||
android:scrollbars="none"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_center"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/layout_chips"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_period"
|
||||
style="@style/Widget.Kotatsu.Chip.Dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/week"
|
||||
app:chipIcon="@drawable/ic_history" />
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<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_toBottomOf="@id/appbar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar"
|
||||
app:showAnimationBehavior="inward"
|
||||
app:trackCornerRadius="0dp"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<org.koitharu.kotatsu.stats.ui.views.PieChartView
|
||||
android:id="@+id/chart"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="24dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline_center"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:overScrollMode="ifContentScrolls"
|
||||
android:scrollbars="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_center"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar"
|
||||
tools:itemCount="4"
|
||||
tools:listitem="@layout/item_stats" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/stub_empty"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout="@layout/item_empty_state"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_percent="0.5" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -37,6 +37,7 @@
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_retry"
|
||||
style="?materialButtonTonalStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
|
||||
Reference in New Issue
Block a user