Misc fixes
This commit is contained in:
@@ -16,7 +16,7 @@ android {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode gitCommits
|
||||
versionName '1.0-a1'
|
||||
versionName '1.0-b1'
|
||||
|
||||
kapt {
|
||||
arguments {
|
||||
|
||||
@@ -31,7 +31,6 @@ abstract class BaseFullscreenActivity<B : ViewBinding> : BaseActivity<B>() {
|
||||
|
||||
protected fun hideSystemUI() {
|
||||
insetsControllerCompat.hide(WindowInsetsCompat.Type.systemBars())
|
||||
|
||||
}
|
||||
|
||||
protected fun showSystemUI() {
|
||||
|
||||
@@ -27,6 +27,8 @@ import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.domain.MangaIntent
|
||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.browser.BrowserActivity
|
||||
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog
|
||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.databinding.ActivityDetailsBinding
|
||||
@@ -71,12 +73,19 @@ class DetailsActivity : BaseActivity<ActivityDetailsBinding>(),
|
||||
}
|
||||
|
||||
private fun onError(e: Throwable) {
|
||||
if (viewModel.manga.value == null) {
|
||||
Toast.makeText(this, e.getDisplayMessage(resources), Toast.LENGTH_LONG).show()
|
||||
finishAfterTransition()
|
||||
} else {
|
||||
Snackbar.make(binding.pager, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
when {
|
||||
e is CloudFlareProtectedException -> {
|
||||
CloudFlareDialog.newInstance(e.url)
|
||||
.show(supportFragmentManager, CloudFlareDialog.TAG)
|
||||
}
|
||||
viewModel.manga.value == null -> {
|
||||
Toast.makeText(this, e.getDisplayMessage(resources), Toast.LENGTH_LONG).show()
|
||||
finishAfterTransition()
|
||||
}
|
||||
else -> {
|
||||
Snackbar.make(binding.pager, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,9 +51,9 @@ class FavouritesContainerFragment : BaseFragment<FragmentFavouritesBinding>(),
|
||||
|
||||
override fun onViewStateRestored(savedInstanceState: Bundle?) {
|
||||
super.onViewStateRestored(savedInstanceState)
|
||||
(savedInstanceState?.getParcelable(KEY_ADAPTER_STATE) ?: adapterState)?.let {
|
||||
(binding.pager.adapter as FavouritesPagerAdapter).restoreState(it)
|
||||
}
|
||||
// (savedInstanceState?.getParcelable(KEY_ADAPTER_STATE) ?: adapterState)?.let {
|
||||
// (binding.pager.adapter as FavouritesPagerAdapter).restoreState(it)
|
||||
// }
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
||||
@@ -30,7 +30,7 @@ class CategoriesEditDelegate(
|
||||
.setHint(R.string.enter_category_name)
|
||||
.setInputType(InputType.TYPE_TEXT_VARIATION_PERSON_NAME or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES)
|
||||
.setNegativeButton(android.R.string.cancel)
|
||||
.setMaxLength(12, false)
|
||||
.setMaxLength(MAX_TITLE_LENGTH, false)
|
||||
.setPositiveButton(R.string.rename) { _, name ->
|
||||
callback.onRenameCategory(category, name)
|
||||
}.create()
|
||||
@@ -43,7 +43,7 @@ class CategoriesEditDelegate(
|
||||
.setHint(R.string.enter_category_name)
|
||||
.setInputType(InputType.TYPE_TEXT_VARIATION_PERSON_NAME or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES)
|
||||
.setNegativeButton(android.R.string.cancel)
|
||||
.setMaxLength(12, false)
|
||||
.setMaxLength(MAX_TITLE_LENGTH, false)
|
||||
.setPositiveButton(R.string.add) { _, name ->
|
||||
callback.onCreateCategory(name)
|
||||
}.create()
|
||||
@@ -58,4 +58,9 @@ class CategoriesEditDelegate(
|
||||
|
||||
fun onCreateCategory(name: String)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
const val MAX_TITLE_LENGTH = 24
|
||||
}
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
package org.koitharu.kotatsu.list.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseBottomSheet
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.base.ui.list.PaginationScrollListener
|
||||
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
|
||||
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog
|
||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.databinding.SheetListBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
|
||||
abstract class MangaListSheet : BaseBottomSheet<SheetListBinding>(),
|
||||
PaginationScrollListener.Callback, OnListItemClickListener<Manga>,
|
||||
Toolbar.OnMenuItemClickListener {
|
||||
|
||||
private var listAdapter: MangaListAdapter? = null
|
||||
private var paginationListener: PaginationScrollListener? = null
|
||||
private val spanResolver = MangaListSpanResolver()
|
||||
private val spanSizeLookup = SpanSizeLookup()
|
||||
open val isSwipeRefreshEnabled = true
|
||||
|
||||
protected abstract val viewModel: MangaListViewModel
|
||||
|
||||
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetListBinding {
|
||||
return SheetListBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
listAdapter = MangaListAdapter(get(), viewLifecycleOwner, this) {
|
||||
viewModel.onRetry()
|
||||
}
|
||||
paginationListener = PaginationScrollListener(4, this)
|
||||
with(binding.recyclerView) {
|
||||
setHasFixedSize(true)
|
||||
adapter = listAdapter
|
||||
addOnScrollListener(paginationListener!!)
|
||||
}
|
||||
with(binding.toolbar) {
|
||||
inflateMenu(R.menu.opt_list_sheet)
|
||||
setOnMenuItemClickListener(this@MangaListSheet)
|
||||
setNavigationOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
if (dialog !is BottomSheetDialog) {
|
||||
binding.toolbar.isVisible = true
|
||||
binding.textViewTitle.isVisible = false
|
||||
binding.appbar.elevation = resources.getDimension(R.dimen.elevation_large)
|
||||
}
|
||||
|
||||
viewModel.content.observe(viewLifecycleOwner, ::onListChanged)
|
||||
viewModel.onError.observe(viewLifecycleOwner, ::onError)
|
||||
viewModel.isLoading.observe(viewLifecycleOwner, ::onLoadingStateChanged)
|
||||
viewModel.listMode.observe(viewLifecycleOwner, ::onListModeChanged)
|
||||
viewModel.gridScale.observe(viewLifecycleOwner, ::onGridScaleChanged)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
listAdapter = null
|
||||
paginationListener = null
|
||||
spanSizeLookup.invalidateCache()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
protected fun setTitle(title: CharSequence) {
|
||||
binding.toolbar.title = title
|
||||
binding.textViewTitle.text = title
|
||||
}
|
||||
|
||||
protected fun setSubtitle(subtitle: CharSequence) {
|
||||
binding.toolbar.subtitle = subtitle
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?) =
|
||||
super.onCreateDialog(savedInstanceState).also {
|
||||
val behavior = (it as? BottomSheetDialog)?.behavior ?: return@also
|
||||
behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
private val elevation = resources.getDimension(R.dimen.elevation_large)
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
binding.toolbar.isVisible = true
|
||||
binding.textViewTitle.isVisible = false
|
||||
binding.appbar.elevation = elevation
|
||||
} else {
|
||||
binding.toolbar.isVisible = false
|
||||
binding.textViewTitle.isVisible = true
|
||||
binding.appbar.elevation = 0f
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem) = when (item.itemId) {
|
||||
R.id.action_list_mode -> {
|
||||
ListModeSelectDialog.show(childFragmentManager)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun onItemClick(item: Manga, view: View) {
|
||||
startActivity(DetailsActivity.newIntent(context ?: return, item))
|
||||
}
|
||||
|
||||
private fun onListChanged(list: List<ListModel>) {
|
||||
spanSizeLookup.invalidateCache()
|
||||
listAdapter?.items = list
|
||||
}
|
||||
|
||||
private fun onError(e: Throwable) {
|
||||
if (e is CloudFlareProtectedException) {
|
||||
CloudFlareDialog.newInstance(e.url).show(childFragmentManager, CloudFlareDialog.TAG)
|
||||
} else {
|
||||
Snackbar.make(
|
||||
binding.recyclerView,
|
||||
e.getDisplayMessage(resources),
|
||||
Snackbar.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onLoadingStateChanged(isLoading: Boolean) {
|
||||
binding.progressBar.isVisible =
|
||||
isLoading && !binding.recyclerView.hasItems
|
||||
}
|
||||
|
||||
private fun onGridScaleChanged(scale: Float) {
|
||||
spanSizeLookup.invalidateCache()
|
||||
spanResolver.setGridSize(scale, binding.recyclerView)
|
||||
}
|
||||
|
||||
private fun onListModeChanged(mode: ListMode) {
|
||||
spanSizeLookup.invalidateCache()
|
||||
with(binding.recyclerView) {
|
||||
clearItemDecorations()
|
||||
removeOnLayoutChangeListener(spanResolver)
|
||||
when (mode) {
|
||||
ListMode.LIST -> {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
context,
|
||||
RecyclerView.VERTICAL
|
||||
)
|
||||
)
|
||||
}
|
||||
ListMode.DETAILED_LIST -> {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
addItemDecoration(
|
||||
SpacingItemDecoration(
|
||||
resources.getDimensionPixelOffset(R.dimen.grid_spacing)
|
||||
)
|
||||
)
|
||||
}
|
||||
ListMode.GRID -> {
|
||||
layoutManager = GridLayoutManager(context, spanResolver.spanCount).also {
|
||||
it.spanSizeLookup = spanSizeLookup
|
||||
}
|
||||
addItemDecoration(
|
||||
SpacingItemDecoration(
|
||||
resources.getDimensionPixelOffset(R.dimen.grid_spacing)
|
||||
)
|
||||
)
|
||||
addOnLayoutChangeListener(spanResolver)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class SpanSizeLookup : GridLayoutManager.SpanSizeLookup() {
|
||||
|
||||
init {
|
||||
isSpanIndexCacheEnabled = true
|
||||
isSpanGroupIndexCacheEnabled = true
|
||||
}
|
||||
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
val total =
|
||||
(binding.recyclerView.layoutManager as? GridLayoutManager)?.spanCount ?: return 1
|
||||
return when (listAdapter?.getItemViewType(position)) {
|
||||
MangaListAdapter.ITEM_TYPE_MANGA_GRID -> 1
|
||||
else -> total
|
||||
}
|
||||
}
|
||||
|
||||
fun invalidateCache() {
|
||||
invalidateSpanGroupIndexCache()
|
||||
invalidateSpanIndexCache()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (protectHelper.check(this)) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
setContentView(ActivityMainBinding.inflate(layoutInflater))
|
||||
drawerToggle =
|
||||
ActionBarDrawerToggle(
|
||||
@@ -76,10 +80,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
|
||||
} ?: run {
|
||||
openDefaultSection()
|
||||
}
|
||||
if (protectHelper.check(this)) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
TrackWorker.setup(applicationContext)
|
||||
AppUpdateChecker(this).launchIfNeeded()
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ class PageLoader(
|
||||
"Null response"
|
||||
}
|
||||
cache.put(url) { out ->
|
||||
body.byteStream().copyTo(out)
|
||||
body.byteStream().use { it.copyTo(out) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.koitharu.kotatsu.core.model.MangaChapter
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.databinding.ActivityReaderBinding
|
||||
import org.koitharu.kotatsu.reader.ReaderControlDelegate
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReader
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderUiState
|
||||
import org.koitharu.kotatsu.reader.ui.pager.reversed.ReversedReaderFragment
|
||||
@@ -63,6 +62,10 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
private lateinit var touchHelper: GridTouchHelper
|
||||
private lateinit var orientationHelper: ScreenOrientationHelper
|
||||
private lateinit var controlDelegate: ReaderControlDelegate
|
||||
private val permissionsRequest = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission(),
|
||||
this
|
||||
)
|
||||
private var gestureInsets: Insets = Insets.NONE
|
||||
|
||||
private val reader
|
||||
@@ -184,10 +187,7 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
) {
|
||||
onActivityResult(true)
|
||||
} else {
|
||||
registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission(),
|
||||
this
|
||||
).launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
permissionsRequest.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
} else {
|
||||
showWaitWhileLoading()
|
||||
@@ -273,6 +273,7 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
}
|
||||
|
||||
override fun onReaderModeChanged(mode: ReaderMode) {
|
||||
viewModel.saveCurrentState(reader?.getCurrentState())
|
||||
viewModel.switchMode(mode)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.koitharu.kotatsu.reader
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import android.view.KeyEvent
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
@@ -39,7 +39,7 @@ class ReaderViewModel(
|
||||
) : BaseViewModel() {
|
||||
|
||||
private var loadingJob: Job? = null
|
||||
private val currentState = MutableStateFlow(state)
|
||||
private val currentState = MutableStateFlow<ReaderState?>(null)
|
||||
private val mangaData = MutableStateFlow<Manga?>(intent.manga)
|
||||
private val chapters = LongSparseArray<MangaChapter>()
|
||||
|
||||
@@ -98,14 +98,12 @@ class ReaderViewModel(
|
||||
newMode
|
||||
} ?: error("There are no chapters in this manga")
|
||||
// obtain state
|
||||
if (state == null) {
|
||||
currentState.value = historyRepository.getOne(manga)?.let {
|
||||
ReaderState.from(it)
|
||||
} ?: ReaderState.initial(manga)
|
||||
}
|
||||
currentState.value = state ?: historyRepository.getOne(manga)?.let {
|
||||
ReaderState.from(it)
|
||||
} ?: ReaderState.initial(manga)
|
||||
readerMode.postValue(mode)
|
||||
|
||||
val pages = loadChapter(checkNotNull(manga.chapters?.firstOrNull()).id)
|
||||
val pages = loadChapter(requireNotNull(currentState.value).chapterId)
|
||||
content.postValue(ReaderContent(pages, currentState.value))
|
||||
}
|
||||
}
|
||||
@@ -118,10 +116,18 @@ class ReaderViewModel(
|
||||
mode = newMode
|
||||
)
|
||||
readerMode.value = newMode
|
||||
content.value?.run {
|
||||
content.value = copy(
|
||||
state = getCurrentState()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun saveCurrentState(state: ReaderState? = null) {
|
||||
if (state != null) {
|
||||
currentState.value = state
|
||||
}
|
||||
saveState(
|
||||
mangaData.value ?: return,
|
||||
state ?: currentState.value ?: return
|
||||
@@ -184,17 +190,14 @@ class ReaderViewModel(
|
||||
currentState.value = currentValue.copy(chapterId = it.chapterId)
|
||||
}
|
||||
}
|
||||
when {
|
||||
loadingJob?.isActive == true -> return
|
||||
pages.isEmpty() -> return
|
||||
position <= BOUNDS_PAGE_OFFSET -> {
|
||||
val chapterId = pages.first().chapterId
|
||||
loadPrevNextChapter(chapterId, -1)
|
||||
}
|
||||
position >= pages.size - BOUNDS_PAGE_OFFSET -> {
|
||||
val chapterId = pages.last().chapterId
|
||||
loadPrevNextChapter(chapterId, 1)
|
||||
}
|
||||
if (pages.isEmpty() || loadingJob?.isActive == true) {
|
||||
return
|
||||
}
|
||||
if (position <= BOUNDS_PAGE_OFFSET) {
|
||||
loadPrevNextChapter(pages.first().chapterId, -1)
|
||||
}
|
||||
if (position >= pages.size - BOUNDS_PAGE_OFFSET) {
|
||||
loadPrevNextChapter(pages.last().chapterId, 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,17 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import kotlinx.coroutines.async
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReader
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.utils.ext.callOnPageChaneListeners
|
||||
import org.koitharu.kotatsu.utils.ext.doOnPageChanged
|
||||
import org.koitharu.kotatsu.utils.ext.swapAdapter
|
||||
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
|
||||
|
||||
class ReversedReaderFragment : BaseReader<FragmentReaderStandardBinding>() {
|
||||
|
||||
@@ -29,6 +33,15 @@ class ReversedReaderFragment : BaseReader<FragmentReaderStandardBinding>() {
|
||||
offscreenPageLimit = 2
|
||||
doOnPageChanged(::notifyPageChanged)
|
||||
}
|
||||
|
||||
viewModel.readerAnimation.observe(viewLifecycleOwner) {
|
||||
val transformer = if (it) ReversedPageAnimTransformer() else null
|
||||
binding.pager.setPageTransformer(transformer)
|
||||
}
|
||||
viewModel.onZoomChanged.observe(viewLifecycleOwner) {
|
||||
pagerAdapter = ReversedPagesAdapter(loader, get())
|
||||
binding.pager.swapAdapter(pagerAdapter)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
@@ -47,13 +60,24 @@ class ReversedReaderFragment : BaseReader<FragmentReaderStandardBinding>() {
|
||||
}
|
||||
|
||||
override fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) {
|
||||
pagerAdapter?.setItems(pages.asReversed()) {
|
||||
val reversedPages = pages.asReversed()
|
||||
viewLifecycleScope.launchWhenCreated {
|
||||
val items = async {
|
||||
pagerAdapter?.setItems(reversedPages)
|
||||
}
|
||||
if (pendingState != null) {
|
||||
val position = pages.indexOfFirst {
|
||||
val position = reversedPages.indexOfLast {
|
||||
it.chapterId == pendingState.chapterId && it.index == pendingState.page
|
||||
}
|
||||
if (position == -1) return@setItems
|
||||
binding.pager.setCurrentItem(position, false)
|
||||
items.await() ?: return@launchWhenCreated
|
||||
if (position != -1) {
|
||||
binding.pager.setCurrentItem(position, false)
|
||||
}
|
||||
} else {
|
||||
items.await()
|
||||
}
|
||||
binding.pager.post {
|
||||
bindingOrNull()?.pager?.callOnPageChaneListeners()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReader
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.utils.ext.callOnPageChaneListeners
|
||||
import org.koitharu.kotatsu.utils.ext.doOnPageChanged
|
||||
import org.koitharu.kotatsu.utils.ext.swapAdapter
|
||||
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
|
||||
@@ -64,6 +65,9 @@ class PagerReaderFragment : BaseReader<FragmentReaderStandardBinding>() {
|
||||
} else {
|
||||
items.await()
|
||||
}
|
||||
binding.pager.post {
|
||||
bindingOrNull()?.pager?.callOnPageChaneListeners()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,7 @@ import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReader
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.utils.ext.doOnCurrentItemChanged
|
||||
import org.koitharu.kotatsu.utils.ext.findCenterViewPosition
|
||||
import org.koitharu.kotatsu.utils.ext.firstItem
|
||||
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
|
||||
class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
|
||||
|
||||
@@ -51,14 +48,20 @@ class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
|
||||
}
|
||||
setItems.await() ?: return@launchWhenCreated
|
||||
if (position != -1) {
|
||||
binding.recyclerView.firstItem = position
|
||||
// TODO check
|
||||
(binding.recyclerView.findViewHolderForAdapterPosition(position) as? WebtoonHolder)
|
||||
?.restoreScroll(pendingState.scroll)
|
||||
with(binding.recyclerView) {
|
||||
firstItem = position
|
||||
post {
|
||||
(findViewHolderForAdapterPosition(position) as? WebtoonHolder)
|
||||
?.restoreScroll(pendingState.scroll)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setItems.await()
|
||||
}
|
||||
binding.recyclerView.post {
|
||||
binding.recyclerView.callOnScrollListeners()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,9 +96,12 @@ class RemoteListViewModel(
|
||||
|
||||
fun applyFilter(newFilter: MangaFilter) {
|
||||
appliedFilter = newFilter
|
||||
mangaList.value = emptyList()
|
||||
mangaList.value = null
|
||||
hasNextPage.value = false
|
||||
loadList(false)
|
||||
filter.value?.run {
|
||||
filter.value = copy(currentFilter = newFilter)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadFilter() {
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package org.koitharu.kotatsu.search.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.list.ui.MangaListSheet
|
||||
import org.koitharu.kotatsu.utils.ext.parcelableArgument
|
||||
import org.koitharu.kotatsu.utils.ext.stringArgument
|
||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||
|
||||
class MangaSearchSheet : MangaListSheet() {
|
||||
|
||||
override val viewModel by viewModel<SearchViewModel> {
|
||||
parametersOf(source, query)
|
||||
}
|
||||
|
||||
private val query by stringArgument(ARG_QUERY)
|
||||
private val source by parcelableArgument<MangaSource>(ARG_SOURCE)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setTitle(query.orEmpty())
|
||||
setSubtitle(getString(R.string.search_results_on_s, source.title))
|
||||
}
|
||||
|
||||
override fun onScrolledToEnd() {
|
||||
viewModel.loadNextPage()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val ARG_SOURCE = "source"
|
||||
private const val ARG_QUERY = "query"
|
||||
|
||||
private const val TAG = "MangaSearchSheet"
|
||||
|
||||
@Deprecated("Not ready for use")
|
||||
fun show(fm: FragmentManager, source: MangaSource, query: String) {
|
||||
MangaSearchSheet().withArgs(2) {
|
||||
putParcelable(ARG_SOURCE, source)
|
||||
putString(ARG_QUERY, query)
|
||||
}.show(fm, TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,8 +52,6 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
|
||||
MultiSummaryProvider(R.string.gestures_only)
|
||||
findPreference<MultiSelectListPreference>(AppSettings.KEY_TRACK_SOURCES)?.summaryProvider =
|
||||
MultiSummaryProvider(R.string.dont_check)
|
||||
findPreference<Preference>(AppSettings.KEY_GRID_SIZE)?.isEnabled =
|
||||
settings.listMode == ListMode.GRID
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@@ -93,10 +91,6 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
|
||||
?: getString(R.string.not_available)
|
||||
}
|
||||
}
|
||||
AppSettings.KEY_LIST_MODE -> {
|
||||
findPreference<Preference>(AppSettings.KEY_GRID_SIZE)?.isEnabled =
|
||||
settings.listMode == ListMode.GRID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,10 @@ class NotificationSettingsLegacyFragment : BasePreferenceFragment(R.string.notif
|
||||
private val ringtonePickContract = registerForActivityResult(
|
||||
RingtonePickContract(get<Context>().getString(R.string.notification_sound))
|
||||
) { uri ->
|
||||
settings.notificationSound = uri?.toString().orEmpty()
|
||||
settings.notificationSound = uri?.toString() ?: return@registerForActivityResult
|
||||
findPreference<Preference>(AppSettings.KEY_NOTIFICATIONS_SOUND)?.run {
|
||||
summary = RingtoneManager.getRingtone(context, uri).getTitle(context)
|
||||
summary = RingtoneManager.getRingtone(context, uri)?.getTitle(context)
|
||||
?: getString(R.string.silent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +32,8 @@ class NotificationSettingsLegacyFragment : BasePreferenceFragment(R.string.notif
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
findPreference<Preference>(AppSettings.KEY_NOTIFICATIONS_SOUND)?.run {
|
||||
val uri = settings.notificationSound.toUriOrNull()
|
||||
summary = RingtoneManager.getRingtone(context, uri).getTitle(context)
|
||||
summary = RingtoneManager.getRingtone(context, uri)?.getTitle(context)
|
||||
?: getString(R.string.silent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:actionBarSize"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/AppPopupTheme" />
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_height="wrap_content"
|
||||
app:popupTheme="@style/AppPopupTheme" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:actionBarSize"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/AppPopupTheme">
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:actionBarSize"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/AppPopupTheme" />
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:actionBarSize"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/AppPopupTheme" />
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
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="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?android:windowBackground"
|
||||
android:elevation="0dp"
|
||||
app:elevation="0dp">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
style="@style/Widget.MaterialComponents.Toolbar.Surface"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:elevation="0dp"
|
||||
android:outlineProvider="@null"
|
||||
android:visibility="gone"
|
||||
app:elevation="0dp"
|
||||
app:navigationIcon="@drawable/ic_cross"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
style="@style/MaterialAlertDialog.MaterialComponents.Title.Text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="16dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:visibility="gone" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:minHeight="120dp">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:scrollbars="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_manga_list" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_holder"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="@tools:sample/lorem[3]" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="blue_primary">#1565C0</color>
|
||||
<color name="blue_primary_dark">#283593</color>
|
||||
<color name="blue_primary">#1976D2</color>
|
||||
<color name="blue_primary_dark">#0D47A1</color>
|
||||
<color name="blue_primary_darker">#1A237E</color>
|
||||
<color name="red_accent">#FF8A65</color>
|
||||
<color name="dim">#99000000</color>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="AppTheme.Amoled">
|
||||
<item name="colorPrimary">@color/blue_primary_dark</item>
|
||||
<item name="colorPrimary">@color/blue_primary</item>
|
||||
<item name="colorPrimaryDark">@color/blue_primary_darker</item>
|
||||
<item name="android:windowBackground">@android:color/black</item>
|
||||
</style>
|
||||
|
||||
@@ -179,12 +179,13 @@
|
||||
<string name="file_not_found">Файл не найден</string>
|
||||
<string name="data_restored_success">Все данные успешно восстановлены</string>
|
||||
<string name="data_restored_with_errors">Данные восстановлены, но возникли некоторые ошибки</string>
|
||||
<string name="backup_information">You can create backup of your history and favourites and restore it</string>
|
||||
<string name="backup_information">Вы можете создать резервную копию избранного и истории и потом восстановить их</string>
|
||||
<string name="just_now">Только что</string>
|
||||
<string name="yesterday">Вчера</string>
|
||||
<string name="long_ago">Давно</string>
|
||||
<string name="group">Группировать</string>
|
||||
<string name="today">Сегодня</string>
|
||||
<string name="tap_to_try_again">Tap to try again</string>
|
||||
<string name="reader_mode_hint">Chosen configuration will be remembered for this manga</string>
|
||||
<string name="tap_to_try_again">Попробовать ещё раз</string>
|
||||
<string name="reader_mode_hint">Выбранный режим будет сохранён для текущей манги</string>
|
||||
<string name="silent">Без звука</string>
|
||||
</resources>
|
||||
5
app/src/main/res/values-w600dp/dimens.xml
Normal file
5
app/src/main/res/values-w600dp/dimens.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="grid_spacing">8dp</dimen>
|
||||
<dimen name="preferred_grid_width">140dp</dimen>
|
||||
</resources>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="grid_spacing">5dp</dimen>
|
||||
<dimen name="grid_spacing">6dp</dimen>
|
||||
<dimen name="manga_list_item_height">84dp</dimen>
|
||||
<dimen name="manga_list_details_item_height">120dp</dimen>
|
||||
<dimen name="chapter_list_item_height">46dp</dimen>
|
||||
|
||||
@@ -189,4 +189,5 @@
|
||||
<string name="today">Today</string>
|
||||
<string name="tap_to_try_again">Tap to try again</string>
|
||||
<string name="reader_mode_hint">Chosen configuration will be remembered for this manga</string>
|
||||
<string name="silent">Silent</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user