Merge branch 'feature/desktop-ui' into devel
This commit is contained in:
@@ -31,7 +31,7 @@ abstract class BaseActivity<B : ViewBinding> : AppCompatActivity(), OnApplyWindo
|
||||
@Suppress("LeakingThis")
|
||||
protected val exceptionResolver = ExceptionResolver(this)
|
||||
|
||||
private var lastInsets: Insets = Insets.NONE
|
||||
private var lastInsets: Insets? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val settings = get<AppSettings>()
|
||||
|
||||
@@ -42,7 +42,7 @@ abstract class BaseBottomSheet<B : ViewBinding> : BottomSheetDialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return if (resources.getBoolean(R.bool.is_tablet)) {
|
||||
AppCompatDialog(context, theme)
|
||||
AppCompatDialog(context, R.style.Theme_Kotatsu_Dialog)
|
||||
} else super.onCreateDialog(savedInstanceState)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import androidx.core.graphics.Insets
|
||||
import androidx.core.view.OnApplyWindowInsetsListener
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
@@ -22,7 +23,7 @@ abstract class BaseFragment<B : ViewBinding> : Fragment(), OnApplyWindowInsetsLi
|
||||
@Suppress("LeakingThis")
|
||||
protected val exceptionResolver = ExceptionResolver(this)
|
||||
|
||||
private var lastInsets: Insets = Insets.NONE
|
||||
private var lastInsets: Insets? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
@@ -36,12 +37,18 @@ abstract class BaseFragment<B : ViewBinding> : Fragment(), OnApplyWindowInsetsLi
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
lastInsets = Insets.NONE
|
||||
ViewCompat.setOnApplyWindowInsetsListener(view, this)
|
||||
view.doOnNextLayout {
|
||||
// Listener may not be called
|
||||
if (lastInsets == null) {
|
||||
onWindowInsetsChanged(Insets.NONE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
viewBinding = null
|
||||
lastInsets = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.ActivityOptions
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import android.widget.AdapterView
|
||||
import android.widget.Spinner
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.core.graphics.Insets
|
||||
@@ -24,6 +25,7 @@ import org.koitharu.kotatsu.download.ui.service.DownloadService
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderActivity
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.utils.RecyclerViewScrollCallback
|
||||
|
||||
class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
|
||||
OnListItemClickListener<ChapterListItem>,
|
||||
@@ -56,21 +58,9 @@ class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
|
||||
setHasFixedSize(true)
|
||||
adapter = chaptersAdapter
|
||||
}
|
||||
val branchesAdapter = BranchesAdapter()
|
||||
binding.spinnerBranches.adapter = branchesAdapter
|
||||
binding.spinnerBranches.onItemSelectedListener = this
|
||||
|
||||
binding.spinnerBranches?.let(::initSpinner)
|
||||
viewModel.isLoading.observe(viewLifecycleOwner, this::onLoadingStateChanged)
|
||||
viewModel.chapters.observe(viewLifecycleOwner, this::onChaptersChanged)
|
||||
viewModel.branches.observe(viewLifecycleOwner) {
|
||||
branchesAdapter.setItems(it)
|
||||
binding.spinnerBranches.isVisible = it.size > 1
|
||||
}
|
||||
viewModel.selectedBranchIndex.observe(viewLifecycleOwner) {
|
||||
if (it != -1 && it != binding.spinnerBranches.selectedItemPosition) {
|
||||
binding.spinnerBranches.setSelection(it)
|
||||
}
|
||||
}
|
||||
viewModel.isChaptersReversed.observe(viewLifecycleOwner) {
|
||||
activity?.invalidateOptionsMenu()
|
||||
}
|
||||
@@ -79,7 +69,7 @@ class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
|
||||
override fun onDestroyView() {
|
||||
chaptersAdapter = null
|
||||
selectionDecoration = null
|
||||
binding.spinnerBranches.adapter = null
|
||||
binding.spinnerBranches?.adapter = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
@@ -166,7 +156,8 @@ class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
|
||||
}
|
||||
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
viewModel.setSelectedBranch(binding.spinnerBranches.selectedItem as String?)
|
||||
val spinner = binding.spinnerBranches ?: return
|
||||
viewModel.setSelectedBranch(spinner.selectedItem as String?)
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) = Unit
|
||||
@@ -201,14 +192,37 @@ class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
|
||||
|
||||
override fun onWindowInsetsChanged(insets: Insets) {
|
||||
binding.recyclerViewChapters.updatePadding(
|
||||
left = insets.left,
|
||||
right = insets.right,
|
||||
bottom = insets.bottom + binding.spinnerBranches.height
|
||||
bottom = insets.bottom + (binding.spinnerBranches?.height ?: 0),
|
||||
)
|
||||
}
|
||||
|
||||
private fun initSpinner(spinner: Spinner) {
|
||||
val branchesAdapter = BranchesAdapter()
|
||||
spinner.adapter = branchesAdapter
|
||||
spinner.onItemSelectedListener = this
|
||||
viewModel.branches.observe(viewLifecycleOwner) {
|
||||
branchesAdapter.setItems(it)
|
||||
spinner.isVisible = it.size > 1
|
||||
}
|
||||
viewModel.selectedBranchIndex.observe(viewLifecycleOwner) {
|
||||
if (it != -1 && it != spinner.selectedItemPosition) {
|
||||
spinner.setSelection(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onChaptersChanged(list: List<ChapterListItem>) {
|
||||
chaptersAdapter?.items = list
|
||||
val adapter = chaptersAdapter ?: return
|
||||
if (adapter.itemCount == 0) {
|
||||
val position = list.indexOfFirst { it.hasFlag(ChapterListItem.FLAG_CURRENT) } - 1
|
||||
if (position > 0) {
|
||||
adapter.setItems(list, RecyclerViewScrollCallback(binding.recyclerViewChapters, position))
|
||||
} else {
|
||||
adapter.items = list
|
||||
}
|
||||
} else {
|
||||
adapter.items = list
|
||||
}
|
||||
}
|
||||
|
||||
private fun onLoadingStateChanged(isLoading: Boolean) {
|
||||
|
||||
@@ -6,25 +6,26 @@ import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.Spinner
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.domain.MangaIntent
|
||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||
@@ -34,6 +35,7 @@ import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
||||
import org.koitharu.kotatsu.core.os.ShortcutsRepository
|
||||
import org.koitharu.kotatsu.databinding.ActivityDetailsBinding
|
||||
import org.koitharu.kotatsu.details.ui.adapter.BranchesAdapter
|
||||
import org.koitharu.kotatsu.download.ui.service.DownloadService
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
@@ -43,8 +45,8 @@ import org.koitharu.kotatsu.search.ui.global.GlobalSearchActivity
|
||||
import org.koitharu.kotatsu.utils.ShareHelper
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
|
||||
class DetailsActivity : BaseActivity<ActivityDetailsBinding>(),
|
||||
TabLayoutMediator.TabConfigurationStrategy {
|
||||
class DetailsActivity : BaseActivity<ActivityDetailsBinding>(), TabLayoutMediator.TabConfigurationStrategy,
|
||||
AdapterView.OnItemSelectedListener {
|
||||
|
||||
private val viewModel by viewModel<DetailsViewModel> {
|
||||
parametersOf(MangaIntent(intent))
|
||||
@@ -53,9 +55,16 @@ class DetailsActivity : BaseActivity<ActivityDetailsBinding>(),
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityDetailsBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
binding.pager.adapter = MangaDetailsAdapter(this)
|
||||
TabLayoutMediator(binding.tabs, binding.pager, this).attach()
|
||||
supportActionBar?.run {
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
setDisplayShowTitleEnabled(false)
|
||||
}
|
||||
val pager = binding.pager
|
||||
if (pager != null) {
|
||||
pager.adapter = MangaDetailsAdapter(this)
|
||||
TabLayoutMediator(checkNotNull(binding.tabs), pager, this).attach()
|
||||
}
|
||||
binding.spinnerBranches?.let(::initSpinner)
|
||||
|
||||
viewModel.manga.observe(this, ::onMangaUpdated)
|
||||
viewModel.newChaptersCount.observe(this, ::onNewChaptersChanged)
|
||||
@@ -96,25 +105,17 @@ class DetailsActivity : BaseActivity<ActivityDetailsBinding>(),
|
||||
binding.snackbar.updatePadding(
|
||||
bottom = insets.bottom
|
||||
)
|
||||
with(binding.toolbar) {
|
||||
updatePadding(
|
||||
left = insets.left,
|
||||
right = insets.right
|
||||
)
|
||||
updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = insets.top
|
||||
}
|
||||
}
|
||||
if (binding.tabs.parent !is Toolbar) {
|
||||
binding.tabs.updatePadding(
|
||||
left = insets.left,
|
||||
right = insets.right
|
||||
)
|
||||
binding.toolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = insets.top
|
||||
}
|
||||
binding.root.updatePadding(
|
||||
left = insets.left,
|
||||
right = insets.right
|
||||
)
|
||||
}
|
||||
|
||||
private fun onNewChaptersChanged(newChapters: Int) {
|
||||
val tab = binding.tabs.getTabAt(1) ?: return
|
||||
val tab = binding.tabs?.getTabAt(1) ?: return
|
||||
if (newChapters == 0) {
|
||||
tab.removeBadge()
|
||||
} else {
|
||||
@@ -208,11 +209,7 @@ class DetailsActivity : BaseActivity<ActivityDetailsBinding>(),
|
||||
viewModel.manga.value?.let {
|
||||
lifecycleScope.launch {
|
||||
if (!get<ShortcutsRepository>().requestPinShortcut(it)) {
|
||||
Snackbar.make(
|
||||
binding.pager,
|
||||
R.string.operation_not_supported,
|
||||
Snackbar.LENGTH_SHORT
|
||||
).show()
|
||||
binding.snackbar.show(getString(R.string.operation_not_supported))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,19 +228,25 @@ class DetailsActivity : BaseActivity<ActivityDetailsBinding>(),
|
||||
|
||||
override fun onSupportActionModeStarted(mode: ActionMode) {
|
||||
super.onSupportActionModeStarted(mode)
|
||||
binding.pager.isUserInputEnabled = false
|
||||
binding.pager?.isUserInputEnabled = false
|
||||
}
|
||||
|
||||
override fun onSupportActionModeFinished(mode: ActionMode) {
|
||||
super.onSupportActionModeFinished(mode)
|
||||
binding.pager.isUserInputEnabled = true
|
||||
binding.pager?.isUserInputEnabled = true
|
||||
}
|
||||
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
val spinner = binding.spinnerBranches ?: return
|
||||
viewModel.setSelectedBranch(spinner.selectedItem as String?)
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) = Unit
|
||||
|
||||
fun showChapterMissingDialog(chapterId: Long) {
|
||||
val remoteManga = viewModel.getRemoteManga()
|
||||
if (remoteManga == null) {
|
||||
Snackbar.make(binding.pager, R.string.chapter_is_missing, Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
binding.snackbar.show(getString( R.string.chapter_is_missing))
|
||||
return
|
||||
}
|
||||
MaterialAlertDialogBuilder(this).apply {
|
||||
@@ -266,9 +269,22 @@ class DetailsActivity : BaseActivity<ActivityDetailsBinding>(),
|
||||
}.show()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun initSpinner(spinner: Spinner) {
|
||||
val branchesAdapter = BranchesAdapter()
|
||||
spinner.adapter = branchesAdapter
|
||||
spinner.onItemSelectedListener = this
|
||||
viewModel.branches.observe(this) {
|
||||
branchesAdapter.setItems(it)
|
||||
spinner.isVisible = it.size > 1
|
||||
}
|
||||
viewModel.selectedBranchIndex.observe(this) {
|
||||
if (it != -1 && it != spinner.selectedItemPosition) {
|
||||
spinner.setSelection(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const val ACTION_MANGA_VIEW = "${BuildConfig.APPLICATION_ID}.action.VIEW_MANGA"
|
||||
companion object {
|
||||
|
||||
fun newIntent(context: Context, manga: Manga): Intent {
|
||||
return Intent(context, DetailsActivity::class.java)
|
||||
|
||||
@@ -243,9 +243,7 @@ class DetailsFragment : BaseFragment<FragmentDetailsBinding>(), View.OnClickList
|
||||
|
||||
override fun onWindowInsetsChanged(insets: Insets) {
|
||||
binding.root.updatePadding(
|
||||
left = insets.left,
|
||||
right = insets.right,
|
||||
bottom = insets.bottom
|
||||
bottom = insets.bottom,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -176,17 +176,19 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
||||
val headerHeight = (activity as? AppBarOwner)?.appBar?.measureHeight() ?: insets.top
|
||||
binding.root.updatePadding(
|
||||
left = insets.left,
|
||||
right = insets.right
|
||||
right = insets.right,
|
||||
)
|
||||
if (activity is MainActivity) {
|
||||
val topOffsetDiff = binding.recyclerView.paddingTop - headerHeight
|
||||
binding.recyclerView.updatePadding(
|
||||
top = headerHeight,
|
||||
bottom = insets.bottom
|
||||
bottom = insets.bottom,
|
||||
)
|
||||
binding.recyclerView.scrollBy(0, topOffsetDiff)
|
||||
binding.swipeRefreshLayout.setProgressViewOffset(
|
||||
true,
|
||||
headerHeight + resources.resolveDp(-72),
|
||||
headerHeight + resources.resolveDp(10)
|
||||
headerHeight + resources.resolveDp(10),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,15 @@ import android.view.ViewGroup.MarginLayoutParams
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.*
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
@@ -55,10 +57,7 @@ import org.koitharu.kotatsu.suggestions.ui.SuggestionsFragment
|
||||
import org.koitharu.kotatsu.suggestions.ui.SuggestionsWorker
|
||||
import org.koitharu.kotatsu.tracker.ui.FeedFragment
|
||||
import org.koitharu.kotatsu.tracker.work.TrackWorker
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.hideKeyboard
|
||||
import org.koitharu.kotatsu.utils.ext.measureHeight
|
||||
import org.koitharu.kotatsu.utils.ext.resolveDp
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
|
||||
private const val TAG_PRIMARY = "primary"
|
||||
private const val TAG_SEARCH = "search"
|
||||
@@ -71,7 +70,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
|
||||
private val searchSuggestionViewModel by viewModel<SearchSuggestionViewModel>()
|
||||
|
||||
private lateinit var navHeaderBinding: NavigationHeaderBinding
|
||||
private lateinit var drawerToggle: ActionBarDrawerToggle
|
||||
private var drawerToggle: ActionBarDrawerToggle? = null
|
||||
private var drawer: DrawerLayout? = null
|
||||
|
||||
override val appBar: AppBarLayout
|
||||
get() = binding.appbar
|
||||
@@ -80,36 +80,35 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityMainBinding.inflate(layoutInflater))
|
||||
navHeaderBinding = NavigationHeaderBinding.inflate(layoutInflater)
|
||||
drawerToggle = ActionBarDrawerToggle(
|
||||
this,
|
||||
binding.drawer,
|
||||
binding.toolbar,
|
||||
R.string.open_menu,
|
||||
R.string.close_menu
|
||||
)
|
||||
drawerToggle.setHomeAsUpIndicator(ContextCompat.getDrawable(this, R.drawable.ic_arrow_back))
|
||||
drawerToggle.setToolbarNavigationClickListener {
|
||||
binding.searchView.hideKeyboard()
|
||||
onBackPressed()
|
||||
drawer = binding.root as? DrawerLayout
|
||||
drawerToggle = drawer?.let {
|
||||
ActionBarDrawerToggle(
|
||||
this,
|
||||
it,
|
||||
binding.toolbar,
|
||||
R.string.open_menu,
|
||||
R.string.close_menu
|
||||
).apply {
|
||||
setHomeAsUpIndicator(ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_arrow_back))
|
||||
setToolbarNavigationClickListener {
|
||||
binding.searchView.hideKeyboard()
|
||||
onBackPressed()
|
||||
}
|
||||
it.addDrawerListener(this)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
}
|
||||
binding.drawer.addDrawerListener(drawerToggle)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
with(binding.searchView) {
|
||||
onFocusChangeListener = this@MainActivity
|
||||
searchSuggestionListener = this@MainActivity
|
||||
if (drawer == null) {
|
||||
drawableStart = ContextCompat.getDrawable(context, R.drawable.ic_search)
|
||||
}
|
||||
}
|
||||
|
||||
with(binding.navigationView) {
|
||||
val menuView =
|
||||
findViewById<RecyclerView>(com.google.android.material.R.id.design_navigation_view)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(navHeaderBinding.root) { v, insets ->
|
||||
val systemWindowInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.updatePadding(top = systemWindowInsets.top)
|
||||
// NavigationView doesn't dispatch insets to the menu view, so pad the bottom here.
|
||||
menuView.updatePadding(bottom = systemWindowInsets.bottom)
|
||||
insets
|
||||
}
|
||||
ViewCompat.setOnApplyWindowInsetsListener(this, NavigationViewInsetsListener())
|
||||
addHeaderView(navHeaderBinding.root)
|
||||
setNavigationItemSelectedListener(this@MainActivity)
|
||||
}
|
||||
@@ -134,26 +133,27 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
drawerToggle.isDrawerIndicatorEnabled =
|
||||
binding.drawer.getDrawerLockMode(GravityCompat.START) == DrawerLayout.LOCK_MODE_UNLOCKED
|
||||
drawerToggle?.isDrawerIndicatorEnabled =
|
||||
drawer?.getDrawerLockMode(GravityCompat.START) == DrawerLayout.LOCK_MODE_UNLOCKED
|
||||
}
|
||||
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
drawerToggle.syncState()
|
||||
drawerToggle?.syncState()
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
drawerToggle.onConfigurationChanged(newConfig)
|
||||
drawerToggle?.onConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
val fragment = supportFragmentManager.findFragmentByTag(TAG_SEARCH)
|
||||
binding.searchView.clearFocus()
|
||||
when {
|
||||
binding.drawer.isDrawerOpen(binding.navigationView) -> binding.drawer.closeDrawer(
|
||||
binding.navigationView)
|
||||
drawer?.isDrawerOpen(binding.navigationView) == true -> {
|
||||
drawer?.closeDrawer(binding.navigationView)
|
||||
}
|
||||
fragment != null -> supportFragmentManager.commit {
|
||||
remove(fragment)
|
||||
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
@@ -164,7 +164,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return drawerToggle.onOptionsItemSelected(item) || when (item.itemId) {
|
||||
return drawerToggle?.onOptionsItemSelected(item) == true || when (item.itemId) {
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
@@ -210,19 +210,22 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
binding.drawer.closeDrawers()
|
||||
drawer?.closeDrawers()
|
||||
appBar.setExpanded(true)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onWindowInsetsChanged(insets: Insets) {
|
||||
binding.toolbarCard.updateLayoutParams<MarginLayoutParams> {
|
||||
topMargin = insets.top + resources.resolveDp(8)
|
||||
}
|
||||
binding.fab.updateLayoutParams<MarginLayoutParams> {
|
||||
bottomMargin = insets.bottom + topMargin
|
||||
leftMargin = insets.left + topMargin
|
||||
rightMargin = insets.right + topMargin
|
||||
}
|
||||
binding.toolbarCard.updateLayoutParams<MarginLayoutParams> {
|
||||
topMargin = insets.top + bottomMargin
|
||||
}
|
||||
binding.root.updatePadding(
|
||||
left = insets.left,
|
||||
right = insets.right,
|
||||
)
|
||||
binding.container.updateLayoutParams<MarginLayoutParams> {
|
||||
topMargin = -(binding.appbar.measureHeight())
|
||||
}
|
||||
@@ -360,14 +363,14 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
|
||||
}
|
||||
|
||||
private fun onSearchOpened() {
|
||||
binding.drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||
drawerToggle.isDrawerIndicatorEnabled = false
|
||||
drawer?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||
drawerToggle?.isDrawerIndicatorEnabled = false
|
||||
adjustFabVisibility(isSearchOpened = true)
|
||||
}
|
||||
|
||||
private fun onSearchClosed() {
|
||||
binding.drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
|
||||
drawerToggle.isDrawerIndicatorEnabled = true
|
||||
drawer?.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
|
||||
drawerToggle?.isDrawerIndicatorEnabled = true
|
||||
adjustFabVisibility(isSearchOpened = false)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.koitharu.kotatsu.main.ui
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.OnApplyWindowInsetsListener
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import java.lang.ref.WeakReference
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
class NavigationViewInsetsListener : OnApplyWindowInsetsListener {
|
||||
|
||||
private var menuViewRef: WeakReference<View>? = null
|
||||
|
||||
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
|
||||
val menuView = menuViewRef?.get() ?: v.findViewById<View>(materialR.id.design_navigation_view).also {
|
||||
menuViewRef = WeakReference(it)
|
||||
}
|
||||
val systemWindowInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.updatePadding(top = systemWindowInsets.top)
|
||||
// NavigationView doesn't dispatch insets to the menu view, so pad the bottom here.
|
||||
menuView.updatePadding(bottom = systemWindowInsets.bottom)
|
||||
return WindowInsetsCompat.CONSUMED
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.divider.MaterialDividerItemDecoration
|
||||
import org.koin.android.ext.android.get
|
||||
@@ -20,6 +19,7 @@ import org.koitharu.kotatsu.details.ui.model.ChapterListItem
|
||||
import org.koitharu.kotatsu.details.ui.model.toListItem
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.utils.BottomSheetToolbarController
|
||||
import org.koitharu.kotatsu.utils.RecyclerViewScrollCallback
|
||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||
|
||||
class ChaptersBottomSheet : BaseBottomSheet<SheetChaptersBinding>(), OnListItemClickListener<ChapterListItem> {
|
||||
@@ -59,7 +59,7 @@ class ChaptersBottomSheet : BaseBottomSheet<SheetChaptersBinding>(), OnListItemC
|
||||
binding.recyclerView.adapter = ChaptersAdapter(this).also { adapter ->
|
||||
if (currentPosition >= 0) {
|
||||
val targetPosition = (currentPosition - 1).coerceAtLeast(0)
|
||||
adapter.setItems(items, Scroller(binding.recyclerView, targetPosition))
|
||||
adapter.setItems(items, RecyclerViewScrollCallback(binding.recyclerView, targetPosition))
|
||||
} else {
|
||||
adapter.items = items
|
||||
}
|
||||
@@ -78,13 +78,6 @@ class ChaptersBottomSheet : BaseBottomSheet<SheetChaptersBinding>(), OnListItemC
|
||||
fun onChapterChanged(chapter: MangaChapter)
|
||||
}
|
||||
|
||||
private class Scroller(private val recyclerView: RecyclerView, private val position: Int) : Runnable {
|
||||
override fun run() {
|
||||
val offset = recyclerView.resources.getDimensionPixelSize(R.dimen.chapter_list_item_height) / 2
|
||||
(recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(position, offset)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val ARG_CHAPTERS = "chapters"
|
||||
|
||||
@@ -243,7 +243,7 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
rawX >= binding.root.width - gestureInsets.right ||
|
||||
rawY >= binding.root.height - gestureInsets.bottom ||
|
||||
binding.appbarTop.hasGlobalPoint(rawX, rawY) ||
|
||||
binding.appbarBottom.hasGlobalPoint(rawX, rawY)
|
||||
binding.appbarBottom?.hasGlobalPoint(rawX, rawY) == true
|
||||
) {
|
||||
false
|
||||
} else {
|
||||
@@ -320,11 +320,13 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
if (binding.appbarTop.isVisible != isUiVisible) {
|
||||
val transition = TransitionSet()
|
||||
.setOrdering(TransitionSet.ORDERING_TOGETHER)
|
||||
.addTransition(Slide(Gravity.BOTTOM).addTarget(binding.appbarBottom))
|
||||
.addTransition(Slide(Gravity.TOP).addTarget(binding.appbarTop))
|
||||
binding.appbarBottom?.let { botomBar ->
|
||||
transition.addTransition(Slide(Gravity.BOTTOM).addTarget(botomBar))
|
||||
}
|
||||
TransitionManager.beginDelayedTransition(binding.root, transition)
|
||||
binding.appbarTop.isVisible = isUiVisible
|
||||
binding.appbarBottom.isVisible = isUiVisible
|
||||
binding.appbarBottom?.isVisible = isUiVisible
|
||||
if (isUiVisible) {
|
||||
showSystemUI()
|
||||
} else {
|
||||
@@ -341,7 +343,7 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
right = systemBars.right,
|
||||
left = systemBars.left
|
||||
)
|
||||
binding.appbarBottom.updatePadding(
|
||||
binding.appbarBottom?.updatePadding(
|
||||
bottom = systemBars.bottom,
|
||||
right = systemBars.right,
|
||||
left = systemBars.left
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -29,6 +29,7 @@ class PagesThumbnailsSheet : BaseBottomSheet<SheetPagesBinding>(),
|
||||
|
||||
private lateinit var thumbnails: List<PageThumbnail>
|
||||
private val spanResolver = MangaListSpanResolver()
|
||||
private var currentPageIndex = -1
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -37,12 +38,12 @@ class PagesThumbnailsSheet : BaseBottomSheet<SheetPagesBinding>(),
|
||||
dismissAllowingStateLoss()
|
||||
return
|
||||
}
|
||||
val current = arguments?.getInt(ARG_CURRENT, -1) ?: -1
|
||||
currentPageIndex = requireArguments().getInt(ARG_CURRENT, currentPageIndex)
|
||||
val repository = MangaRepository(pages.first().source)
|
||||
thumbnails = pages.mapIndexed { i, x ->
|
||||
PageThumbnail(
|
||||
number = i + 1,
|
||||
isCurrent = i == current,
|
||||
isCurrent = i == currentPageIndex,
|
||||
repository = repository,
|
||||
page = x
|
||||
)
|
||||
@@ -69,11 +70,9 @@ class PagesThumbnailsSheet : BaseBottomSheet<SheetPagesBinding>(),
|
||||
resources.getQuantityString(R.plurals.pages, thumbnails.size, thumbnails.size)
|
||||
}
|
||||
|
||||
val initialTopPosition = binding.recyclerView.top
|
||||
|
||||
with(binding.recyclerView) {
|
||||
addItemDecoration(
|
||||
SpacingItemDecoration(view.resources.getDimensionPixelOffset(R.dimen.grid_spacing))
|
||||
SpacingItemDecoration(resources.getDimensionPixelOffset(R.dimen.grid_spacing))
|
||||
)
|
||||
adapter = PageThumbnailAdapter(
|
||||
thumbnails,
|
||||
@@ -82,16 +81,12 @@ class PagesThumbnailsSheet : BaseBottomSheet<SheetPagesBinding>(),
|
||||
get(),
|
||||
this@PagesThumbnailsSheet
|
||||
)
|
||||
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) = Unit
|
||||
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
binding.appbar.isLifted = getChildAt(0).top < initialTopPosition
|
||||
}
|
||||
})
|
||||
addOnLayoutChangeListener(spanResolver)
|
||||
spanResolver.setGridSize(get<AppSettings>().gridSize / 100f, this)
|
||||
if (currentPageIndex > 0) {
|
||||
val offset = resources.getDimensionPixelOffset(R.dimen.preferred_grid_width)
|
||||
(layoutManager as GridLayoutManager).scrollToPositionWithOffset(currentPageIndex, offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.PageThumbnail
|
||||
import org.koitharu.kotatsu.utils.ext.IgnoreErrors
|
||||
import org.koitharu.kotatsu.utils.ext.referer
|
||||
import org.koitharu.kotatsu.utils.ext.setTextColorAttr
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
fun pageThumbnailAD(
|
||||
coil: ImageLoader,
|
||||
@@ -31,7 +33,7 @@ fun pageThumbnailAD(
|
||||
height = (gridWidth * 13f / 18f).toInt()
|
||||
)
|
||||
|
||||
binding.handle.setOnClickListener {
|
||||
binding.root.setOnClickListener {
|
||||
clickListener.onItemClick(item.page, itemView)
|
||||
}
|
||||
|
||||
@@ -39,7 +41,8 @@ fun pageThumbnailAD(
|
||||
job?.cancel()
|
||||
binding.imageViewThumb.setImageDrawable(null)
|
||||
with(binding.textViewNumber) {
|
||||
setBackgroundResource(if (item.isCurrent) R.drawable.bg_badge_accent else R.drawable.bg_badge_default)
|
||||
setBackgroundResource(if (item.isCurrent) R.drawable.bg_badge_accent else R.drawable.bg_badge_empty)
|
||||
setTextColorAttr(if (item.isCurrent) materialR.attr.colorOnTertiary else android.R.attr.textColorPrimary)
|
||||
text = (item.number).toString()
|
||||
}
|
||||
job = scope.launch(Dispatchers.Default + IgnoreErrors) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import org.koitharu.kotatsu.databinding.ItemSearchSuggestionMangaGridBinding
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener
|
||||
import org.koitharu.kotatsu.search.ui.suggestion.model.SearchSuggestionItem
|
||||
import org.koitharu.kotatsu.utils.ScrollResetCallback
|
||||
import org.koitharu.kotatsu.utils.RecyclerViewScrollCallback
|
||||
import org.koitharu.kotatsu.utils.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.utils.ext.newImageRequest
|
||||
|
||||
@@ -37,7 +37,7 @@ fun searchSuggestionMangaListAD(
|
||||
right = recyclerView.paddingRight - spacing,
|
||||
)
|
||||
recyclerView.addItemDecoration(SpacingItemDecoration(spacing))
|
||||
val scrollResetCallback = ScrollResetCallback(recyclerView)
|
||||
val scrollResetCallback = RecyclerViewScrollCallback(recyclerView, 0)
|
||||
|
||||
bind {
|
||||
adapter.setItems(item.items, scrollResetCallback)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.koitharu.kotatsu.utils
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class RecyclerViewScrollCallback(recyclerView: RecyclerView, private val position: Int) : Runnable {
|
||||
|
||||
private val recyclerViewRef = WeakReference(recyclerView)
|
||||
|
||||
override fun run() {
|
||||
val rv = recyclerViewRef.get() ?: return
|
||||
val lm = rv.layoutManager ?: return
|
||||
rv.stopScroll()
|
||||
if (lm is LinearLayoutManager) {
|
||||
lm.scrollToPositionWithOffset(position, 0)
|
||||
} else {
|
||||
lm.scrollToPosition(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package org.koitharu.kotatsu.utils
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class ScrollResetCallback(recyclerView: RecyclerView) : Runnable {
|
||||
|
||||
private val recyclerViewRef = WeakReference(recyclerView)
|
||||
|
||||
override fun run() {
|
||||
recyclerViewRef.get()?.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.koitharu.kotatsu.utils.ext
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.graphics.Insets
|
||||
|
||||
fun Insets.getStart(view: View): Int {
|
||||
return if (view.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
|
||||
right
|
||||
} else {
|
||||
left
|
||||
}
|
||||
}
|
||||
|
||||
fun Insets.getEnd(view: View): Int {
|
||||
return if (view.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
|
||||
left
|
||||
} else {
|
||||
right
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,9 @@ package org.koitharu.kotatsu.utils.ext
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.res.use
|
||||
import androidx.core.view.isGone
|
||||
|
||||
var TextView.textAndVisible: CharSequence?
|
||||
@@ -35,4 +37,11 @@ fun TextView.setTextAndVisible(@StringRes textResId: Int) {
|
||||
setText(textResId)
|
||||
isGone = text.isNullOrEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
fun TextView.setTextColorAttr(@AttrRes attrResId: Int) {
|
||||
val colors = context.obtainStyledAttributes(intArrayOf(attrResId)).use {
|
||||
it.getColorStateList(0)
|
||||
}
|
||||
setTextColor(colors)
|
||||
}
|
||||
Reference in New Issue
Block a user