Refactor menu providers

This commit is contained in:
Koitharu
2022-05-30 15:45:29 +03:00
parent 5808e8f321
commit a74b623c10
24 changed files with 396 additions and 305 deletions

View File

@@ -45,7 +45,7 @@ abstract class BaseBottomSheet<B : ViewBinding> : BottomSheetDialogFragment() {
requireContext().displayCompat?.let {
val metrics = DisplayMetrics()
it.getRealMetrics(metrics)
behavior?.peekHeight = metrics.heightPixels / 2
behavior?.peekHeight = (metrics.heightPixels * 0.4).toInt()
}
return binding.root

View File

@@ -16,10 +16,7 @@ class WindowInsetsDelegate(
private var lastInsets: Insets? = null
override fun onApplyWindowInsets(v: View?, insets: WindowInsetsCompat?): WindowInsetsCompat? {
if (insets == null) {
return null
}
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
val handledInsets = interceptingWindowInsetsListener?.onApplyWindowInsets(v, insets) ?: insets
val newInsets = if (handleImeInsets) {
Insets.max(
@@ -49,7 +46,7 @@ class WindowInsetsDelegate(
) {
view.removeOnLayoutChangeListener(this)
if (lastInsets == null) { // Listener may not be called
onApplyWindowInsets(view, ViewCompat.getRootWindowInsets(view))
onApplyWindowInsets(view, ViewCompat.getRootWindowInsets(view) ?: return)
}
}

View File

@@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView
import androidx.core.graphics.Insets
import androidx.core.view.MenuProvider
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import com.google.android.material.snackbar.Snackbar
@@ -27,6 +28,7 @@ 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
import org.koitharu.kotatsu.utils.ext.addMenuProvider
import kotlin.math.roundToInt
class ChaptersFragment :
@@ -43,11 +45,6 @@ class ChaptersFragment :
private var actionMode: ActionMode? = null
private var selectionDecoration: ChaptersSelectionDecoration? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
@@ -72,6 +69,7 @@ class ChaptersFragment :
binding.textViewHolder.isVisible = it
activity?.invalidateOptionsMenu()
}
addMenuProvider(ChaptersMenuProvider())
}
override fun onDestroyView() {
@@ -81,31 +79,6 @@ class ChaptersFragment :
super.onDestroyView()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.opt_chapters, menu)
val searchMenuItem = menu.findItem(R.id.action_search)
searchMenuItem.setOnActionExpandListener(this)
val searchView = searchMenuItem.actionView as SearchView
searchView.setOnQueryTextListener(this)
searchView.setIconifiedByDefault(false)
searchView.queryHint = searchMenuItem.title
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
menu.findItem(R.id.action_reversed).isChecked = viewModel.isChaptersReversed.value == true
menu.findItem(R.id.action_search).isVisible = viewModel.isChaptersEmpty.value == false
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_reversed -> {
viewModel.setChaptersReversed(!item.isChecked)
true
}
else -> super.onOptionsItemSelected(item)
}
override fun onItemClick(item: ChapterListItem, view: View) {
if (selectionDecoration?.checkedItemsCount != 0) {
selectionDecoration?.toggleItemChecked(item.chapter.id)
@@ -268,4 +241,30 @@ class ChaptersFragment :
private fun onLoadingStateChanged(isLoading: Boolean) {
binding.progressBar.isVisible = isLoading
}
private inner class ChaptersMenuProvider : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_chapters, menu)
val searchMenuItem = menu.findItem(R.id.action_search)
searchMenuItem.setOnActionExpandListener(this@ChaptersFragment)
val searchView = searchMenuItem.actionView as SearchView
searchView.setOnQueryTextListener(this@ChaptersFragment)
searchView.setIconifiedByDefault(false)
searchView.queryHint = searchMenuItem.title
}
override fun onPrepareMenu(menu: Menu) {
menu.findItem(R.id.action_reversed).isChecked = viewModel.isChaptersReversed.value == true
menu.findItem(R.id.action_search).isVisible = viewModel.isChaptersEmpty.value == false
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.action_reversed -> {
viewModel.setChaptersReversed(!menuItem.isChecked)
true
}
else -> false
}
}
}

View File

@@ -15,8 +15,6 @@ import android.widget.Toast
import androidx.appcompat.view.ActionMode
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.Insets
import androidx.core.net.toFile
import androidx.core.net.toUri
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
@@ -45,7 +43,6 @@ import org.koitharu.kotatsu.parsers.util.mapNotNullToSet
import org.koitharu.kotatsu.reader.ui.ReaderActivity
import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.search.ui.multi.MultiSearchActivity
import org.koitharu.kotatsu.utils.ShareHelper
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
class DetailsActivity :
@@ -166,16 +163,6 @@ class DetailsActivity :
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_share -> {
viewModel.manga.value?.let {
if (it.source == MangaSource.LOCAL) {
ShareHelper(this).shareCbz(listOf(it.url.toUri().toFile()))
} else {
ShareHelper(this).shareMangaLink(it)
}
}
true
}
R.id.action_delete -> {
val title = viewModel.manga.value?.title.orEmpty()
MaterialAlertDialogBuilder(this)

View File

@@ -8,8 +8,10 @@ import android.view.*
import androidx.appcompat.widget.PopupMenu
import androidx.core.content.ContextCompat
import androidx.core.graphics.Insets
import androidx.core.net.toFile
import androidx.core.net.toUri
import androidx.core.text.parseAsHtml
import androidx.core.view.MenuProvider
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
@@ -40,6 +42,7 @@ import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.search.ui.MangaListActivity
import org.koitharu.kotatsu.search.ui.SearchActivity
import org.koitharu.kotatsu.utils.FileSize
import org.koitharu.kotatsu.utils.ShareHelper
import org.koitharu.kotatsu.utils.ext.*
class DetailsFragment :
@@ -52,11 +55,6 @@ class DetailsFragment :
private val viewModel by sharedViewModel<DetailsViewModel>()
private val coil by inject<ImageLoader>(mode = LazyThreadSafetyMode.NONE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -76,11 +74,7 @@ class DetailsFragment :
viewModel.favouriteCategories.observe(viewLifecycleOwner, ::onFavouriteChanged)
viewModel.readingHistory.observe(viewLifecycleOwner, ::onHistoryChanged)
viewModel.bookmarks.observe(viewLifecycleOwner, ::onBookmarksChanged)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.opt_details_info, menu)
addMenuProvider(DetailsMenuProvider())
}
override fun onItemClick(item: Bookmark, view: View) {
@@ -329,4 +323,26 @@ class DetailsFragment :
} ?: request.fallback(R.drawable.ic_placeholder)
request.enqueueWith(coil)
}
private inner class DetailsMenuProvider : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_details_info, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.action_share -> {
viewModel.manga.value?.let {
val context = requireContext()
if (it.source == MangaSource.LOCAL) {
ShareHelper(context).shareCbz(listOf(it.url.toUri().toFile()))
} else {
ShareHelper(context).shareMangaLink(it)
}
}
true
}
else -> false
}
}
}

View File

@@ -1,7 +1,9 @@
package org.koitharu.kotatsu.favourites.ui
import android.os.Bundle
import android.view.*
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.PopupMenu
import androidx.core.graphics.Insets
@@ -19,12 +21,12 @@ import org.koitharu.kotatsu.base.ui.util.ActionModeListener
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.databinding.FragmentFavouritesBinding
import org.koitharu.kotatsu.databinding.ItemEmptyStateBinding
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate
import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity
import org.koitharu.kotatsu.main.ui.AppBarOwner
import org.koitharu.kotatsu.utils.ext.addMenuProvider
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.measureHeight
import org.koitharu.kotatsu.utils.ext.resolveDp
@@ -43,11 +45,6 @@ class FavouritesContainerFragment :
private var pagerAdapter: FavouritesPagerAdapter? = null
private var stubBinding: ItemEmptyStateBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
@@ -61,6 +58,7 @@ class FavouritesContainerFragment :
pagerAdapter = adapter
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
actionModeDelegate.addListener(this, viewLifecycleOwner)
addMenuProvider(FavouritesContainerMenuProvider(view.context))
viewModel.visibleCategories.observe(viewLifecycleOwner, ::onCategoriesChanged)
viewModel.onError.observe(viewLifecycleOwner, ::onError)
@@ -115,21 +113,6 @@ class FavouritesContainerFragment :
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.opt_favourites, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_categories -> {
context?.let {
startActivity(CategoriesActivity.newIntent(it))
}
true
}
else -> super.onOptionsItemSelected(item)
}
private fun onError(e: Throwable) {
Snackbar.make(binding.pager, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG).show()
}

View File

@@ -0,0 +1,28 @@
package org.koitharu.kotatsu.favourites.ui
import android.content.Context
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.core.view.MenuProvider
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
class FavouritesContainerMenuProvider(
private val context: Context,
) : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_favourites, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.action_categories -> {
context.startActivity(CategoriesActivity.newIntent(context))
true
}
else -> false
}
}
}

View File

@@ -2,18 +2,15 @@ package org.koitharu.kotatsu.favourites.ui.list
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.appcompat.view.ActionMode
import androidx.core.view.iterator
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.titleRes
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.utils.ext.addMenuProvider
import org.koitharu.kotatsu.utils.ext.withArgs
class FavouritesListFragment : MangaListFragment() {
@@ -30,47 +27,14 @@ class FavouritesListFragment : MangaListFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.sortOrder.observe(viewLifecycleOwner) { activity?.invalidateOptionsMenu() }
if (categoryId != NO_ID) {
addMenuProvider(FavouritesListMenuProvider(viewModel))
}
}
override fun onScrolledToEnd() = Unit
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
if (categoryId != NO_ID) {
inflater.inflate(R.menu.opt_favourites_list, menu)
menu.findItem(R.id.action_order)?.subMenu?.let { submenu ->
for ((i, item) in CategoriesActivity.SORT_ORDERS.withIndex()) {
val menuItem = submenu.add(R.id.group_order, Menu.NONE, i, item.titleRes)
menuItem.isCheckable = true
}
submenu.setGroupCheckable(R.id.group_order, true, true)
}
}
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
menu.findItem(R.id.action_order)?.subMenu?.let { submenu ->
val selectedOrder = viewModel.sortOrder.value
for (item in submenu) {
val order = CategoriesActivity.SORT_ORDERS.getOrNull(item.order)
item.isChecked = order == selectedOrder
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when {
item.itemId == R.id.action_order -> false
item.groupId == R.id.group_order -> {
val order = CategoriesActivity.SORT_ORDERS.getOrNull(item.order) ?: return false
viewModel.setSortOrder(order)
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
mode.menuInflater.inflate(R.menu.mode_favourites, menu)
return super.onCreateActionMode(mode, menu)

View File

@@ -0,0 +1,48 @@
package org.koitharu.kotatsu.favourites.ui.list
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.core.view.MenuProvider
import androidx.core.view.iterator
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.titleRes
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
class FavouritesListMenuProvider(
private val viewModel: FavouritesListViewModel,
) : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_favourites_list, menu)
menu.findItem(R.id.action_order)?.subMenu?.let { submenu ->
for ((i, item) in CategoriesActivity.SORT_ORDERS.withIndex()) {
val menuItem = submenu.add(R.id.group_order, Menu.NONE, i, item.titleRes)
menuItem.isCheckable = true
}
submenu.setGroupCheckable(R.id.group_order, true, true)
}
}
override fun onPrepareMenu(menu: Menu) {
menu.findItem(R.id.action_order)?.subMenu?.let { submenu ->
val selectedOrder = viewModel.sortOrder.value
for (item in submenu) {
val order = CategoriesActivity.SORT_ORDERS.getOrNull(item.order)
item.isChecked = order == selectedOrder
}
}
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when {
menuItem.itemId == R.id.action_order -> false
menuItem.groupId == R.id.group_order -> {
val order = CategoriesActivity.SORT_ORDERS.getOrNull(menuItem.order) ?: return false
viewModel.setSortOrder(order)
true
}
else -> false
}
}
}

View File

@@ -2,11 +2,9 @@ package org.koitharu.kotatsu.history.ui
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.appcompat.view.ActionMode
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R
@@ -14,6 +12,7 @@ import org.koitharu.kotatsu.base.domain.ReversibleHandle
import org.koitharu.kotatsu.base.domain.reverseAsync
import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.utils.ext.addMenuProvider
class HistoryListFragment : MangaListFragment() {
@@ -22,6 +21,7 @@ class HistoryListFragment : MangaListFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
addMenuProvider(HistoryListMenuProvider(view.context, viewModel))
viewModel.isGroupingEnabled.observe(viewLifecycleOwner) {
activity?.invalidateOptionsMenu()
}
@@ -30,37 +30,6 @@ class HistoryListFragment : MangaListFragment() {
override fun onScrolledToEnd() = Unit
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.opt_history, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
menu.findItem(R.id.action_history_grouping)?.isChecked =
viewModel.isGroupingEnabled.value == true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_clear_history -> {
MaterialAlertDialogBuilder(context ?: return false)
.setTitle(R.string.clear_history)
.setMessage(R.string.text_clear_history_prompt)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.clear) { _, _ ->
viewModel.clearHistory()
}.show()
true
}
R.id.action_history_grouping -> {
viewModel.setGrouping(!item.isChecked)
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
mode.menuInflater.inflate(R.menu.mode_history, menu)
return super.onCreateActionMode(mode, menu)

View File

@@ -0,0 +1,41 @@
package org.koitharu.kotatsu.history.ui
import android.content.Context
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.core.view.MenuProvider
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.koitharu.kotatsu.R
class HistoryListMenuProvider(
private val context: Context,
private val viewModel: HistoryListViewModel,
) : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_history, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.action_clear_history -> {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.clear_history)
.setMessage(R.string.text_clear_history_prompt)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.clear) { _, _ ->
viewModel.clearHistory()
}.show()
true
}
R.id.action_history_grouping -> {
viewModel.setGrouping(!menuItem.isChecked)
true
}
else -> false
}
override fun onPrepareMenu(menu: Menu) {
menu.findItem(R.id.action_history_grouping).isChecked = viewModel.isGroupingEnabled.value == true
}
}

View File

@@ -9,6 +9,7 @@ import androidx.collection.ArraySet
import androidx.core.graphics.Insets
import androidx.core.view.isNotEmpty
import androidx.core.view.updatePadding
import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.GridLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.snackbar.Snackbar
@@ -67,11 +68,6 @@ abstract class MangaListFragment :
protected val selectedItems: Set<Manga>
get() = collectSelectedItems()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
@@ -98,6 +94,7 @@ abstract class MangaListFragment :
setOnRefreshListener(this@MangaListFragment)
isEnabled = isSwipeRefreshEnabled
}
addMenuProvider(MangaListMenuProvider(childFragmentManager))
viewModel.listMode.observe(viewLifecycleOwner, ::onListModeChanged)
viewModel.gridScale.observe(viewLifecycleOwner, ::onGridScaleChanged)
@@ -114,19 +111,6 @@ abstract class MangaListFragment :
super.onDestroyView()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.opt_list, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_list_mode -> {
ListModeSelectDialog.show(childFragmentManager)
true
}
else -> super.onOptionsItemSelected(item)
}
override fun onItemClick(item: Manga, view: View) {
if (selectionDecoration?.checkedItemsCount != 0) {
selectionDecoration?.toggleItemChecked(item.id)

View File

@@ -0,0 +1,25 @@
package org.koitharu.kotatsu.list.ui
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.core.view.MenuProvider
import androidx.fragment.app.FragmentManager
import org.koitharu.kotatsu.R
class MangaListMenuProvider(
private val fragmentManager: FragmentManager,
) : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_list, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.action_list_mode -> {
ListModeSelectDialog.show(fragmentManager)
true
}
else -> false
}
}

View File

@@ -4,7 +4,6 @@ import android.content.*
import android.net.Uri
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.activity.result.ActivityResultCallback
@@ -19,6 +18,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.download.ui.service.DownloadService
import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.utils.ShareHelper
import org.koitharu.kotatsu.utils.ext.addMenuProvider
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
import org.koitharu.kotatsu.utils.progress.Progress
@@ -48,6 +48,7 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback<List<@JvmS
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
addMenuProvider(LocalListMenuProvider(this::onEmptyActionClick))
viewModel.onMangaRemoved.observe(viewLifecycleOwner) { onItemRemoved() }
viewModel.importProgress.observe(viewLifecycleOwner, ::onImportProgressChanged)
}
@@ -77,21 +78,6 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback<List<@JvmS
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.opt_local, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_import -> {
onEmptyActionClick()
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onActivityResult(result: List<@JvmSuppressWildcards Uri>) {
if (result.isEmpty()) return
viewModel.importFiles(result)

View File

@@ -0,0 +1,26 @@
package org.koitharu.kotatsu.local.ui
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.core.view.MenuProvider
import org.koitharu.kotatsu.R
class LocalListMenuProvider(
private val onImportClick: Function0<Unit>,
) : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_local, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.action_import -> {
onImportClick()
true
}
else -> false
}
}
}

View File

@@ -9,7 +9,7 @@ import android.webkit.MimeTypeMap
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.net.toUri
class PageSaveContract : ActivityResultContracts.CreateDocument() {
class PageSaveContract : ActivityResultContracts.CreateDocument("image/*") {
override fun createIntent(context: Context, input: String): Intent {
val intent = super.createIntent(context, input)

View File

@@ -1,9 +1,12 @@
package org.koitharu.kotatsu.remotelist.ui
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.appcompat.view.ActionMode
import androidx.core.view.MenuProvider
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.R
@@ -11,6 +14,7 @@ import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.list.ui.filter.FilterBottomSheet
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.settings.SettingsActivity
import org.koitharu.kotatsu.utils.ext.addMenuProvider
import org.koitharu.kotatsu.utils.ext.serializableArgument
import org.koitharu.kotatsu.utils.ext.withArgs
@@ -22,31 +26,15 @@ class RemoteListFragment : MangaListFragment() {
private val source by serializableArgument<MangaSource>(ARG_SOURCE)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
addMenuProvider(RemoteListMenuProvider())
}
override fun onScrolledToEnd() {
viewModel.loadNextPage()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.opt_list_remote, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_source_settings -> {
startActivity(
SettingsActivity.newSourceSettingsIntent(context ?: return false, source)
)
true
}
R.id.action_filter -> {
onFilterClick()
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
mode.menuInflater.inflate(R.menu.mode_remote, menu)
return super.onCreateActionMode(mode, menu)
@@ -60,6 +48,25 @@ class RemoteListFragment : MangaListFragment() {
viewModel.resetFilter()
}
private inner class RemoteListMenuProvider: MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_list_remote, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.action_source_settings -> {
startActivity(SettingsActivity.newSourceSettingsIntent(requireContext(), source))
true
}
R.id.action_filter -> {
onFilterClick()
true
}
else -> false
}
}
companion object {
private const val ARG_SOURCE = "provider"

View File

@@ -23,15 +23,16 @@ class BackupDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
private val viewModel by viewModel<BackupViewModel>()
private var backup: File? = null
private val saveFileContract =
registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri ->
val file = backup
if (uri != null && file != null) {
saveBackup(file, uri)
} else {
dismiss()
}
private val saveFileContract = registerForActivityResult(
ActivityResultContracts.CreateDocument("*/*")
) { uri ->
val file = backup
if (uri != null && file != null) {
saveBackup(file, uri)
} else {
dismiss()
}
}
override fun onInflateView(
inflater: LayoutInflater,

View File

@@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.*
import androidx.appcompat.widget.SearchView
import androidx.core.graphics.Insets
import androidx.core.view.MenuProvider
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
@@ -20,12 +21,11 @@ import org.koitharu.kotatsu.settings.SourceSettingsFragment
import org.koitharu.kotatsu.settings.sources.adapter.SourceConfigAdapter
import org.koitharu.kotatsu.settings.sources.adapter.SourceConfigListener
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
import org.koitharu.kotatsu.utils.ext.addMenuProvider
class SourcesSettingsFragment :
BaseFragment<FragmentSettingsSourcesBinding>(),
SourceConfigListener,
SearchView.OnQueryTextListener,
MenuItem.OnActionExpandListener,
RecyclerViewOwner {
private var reorderHelper: ItemTouchHelper? = null
@@ -34,11 +34,6 @@ class SourcesSettingsFragment :
override val recyclerView: RecyclerView
get() = binding.recyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
@@ -62,6 +57,7 @@ class SourcesSettingsFragment :
viewModel.items.observe(viewLifecycleOwner) {
sourcesAdapter.items = it
}
addMenuProvider(SourcesMenuProvider())
}
override fun onDestroyView() {
@@ -69,17 +65,6 @@ class SourcesSettingsFragment :
super.onDestroyView()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.opt_sources, menu)
val searchMenuItem = menu.findItem(R.id.action_search)
searchMenuItem.setOnActionExpandListener(this)
val searchView = searchMenuItem.actionView as SearchView
searchView.setOnQueryTextListener(this)
searchView.setIconifiedByDefault(false)
searchView.queryHint = searchMenuItem.title
}
override fun onWindowInsetsChanged(insets: Insets) {
binding.recyclerView.updatePadding(
bottom = insets.bottom,
@@ -106,21 +91,39 @@ class SourcesSettingsFragment :
viewModel.expandOrCollapse(header.localeId)
}
override fun onQueryTextSubmit(query: String?): Boolean = false
private inner class SourcesMenuProvider :
MenuProvider,
MenuItem.OnActionExpandListener,
SearchView.OnQueryTextListener {
override fun onQueryTextChange(newText: String?): Boolean {
viewModel.performSearch(newText)
return true
}
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_sources, menu)
val searchMenuItem = menu.findItem(R.id.action_search)
searchMenuItem.setOnActionExpandListener(this)
val searchView = searchMenuItem.actionView as SearchView
searchView.setOnQueryTextListener(this)
searchView.setIconifiedByDefault(false)
searchView.queryHint = searchMenuItem.title
}
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
(activity as? AppBarOwner)?.appBar?.setExpanded(false, true)
return true
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = false
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
(item.actionView as SearchView).setQuery("", false)
return true
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
(activity as? AppBarOwner)?.appBar?.setExpanded(false, true)
return true
}
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
(item.actionView as SearchView).setQuery("", false)
return true
}
override fun onQueryTextSubmit(query: String?): Boolean = false
override fun onQueryTextChange(newText: String?): Boolean {
viewModel.performSearch(newText)
return true
}
}
private inner class SourcesReorderCallback : ItemTouchHelper.SimpleCallback(

View File

@@ -4,30 +4,40 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.appcompat.view.ActionMode
import androidx.core.view.MenuProvider
import com.google.android.material.snackbar.Snackbar
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.settings.SettingsActivity
import org.koitharu.kotatsu.utils.ext.addMenuProvider
class SuggestionsFragment : MangaListFragment() {
override val viewModel by viewModel<SuggestionsViewModel>()
override val isSwipeRefreshEnabled = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
addMenuProvider(SuggestionMenuProvider())
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.opt_suggestions, menu)
override fun onScrolledToEnd() = Unit
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
mode.menuInflater.inflate(R.menu.mode_remote, menu)
return super.onCreateActionMode(mode, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
private inner class SuggestionMenuProvider : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_suggestions, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.action_update -> {
SuggestionsWorker.startNow(requireContext())
Snackbar.make(
@@ -41,17 +51,10 @@ class SuggestionsFragment : MangaListFragment() {
startActivity(SettingsActivity.newSuggestionsSettingsIntent(requireContext()))
true
}
else -> super.onOptionsItemSelected(item)
else -> false
}
}
override fun onScrolledToEnd() = Unit
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
mode.menuInflater.inflate(R.menu.mode_remote, menu)
return super.onCreateActionMode(mode, menu)
}
companion object {
fun newInstance() = SuggestionsFragment()

View File

@@ -1,10 +1,11 @@
package org.koitharu.kotatsu.tracker.ui
import android.os.Bundle
import android.view.*
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import org.koin.android.ext.android.get
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -21,6 +22,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.tracker.ui.adapter.FeedAdapter
import org.koitharu.kotatsu.tracker.work.TrackWorker
import org.koitharu.kotatsu.utils.ext.addMenuProvider
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.measureHeight
import org.koitharu.kotatsu.utils.progress.Progress
@@ -37,11 +39,6 @@ class FeedFragment :
private var paddingVertical = 0
private var paddingHorizontal = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
@@ -63,6 +60,7 @@ class FeedFragment :
)
addItemDecoration(decoration)
}
addMenuProvider(FeedMenuProvider(binding.recyclerView, viewModel))
viewModel.content.observe(viewLifecycleOwner, this::onListChanged)
viewModel.onError.observe(viewLifecycleOwner, this::onError)
@@ -73,36 +71,6 @@ class FeedFragment :
.observe(viewLifecycleOwner, this::onUpdateProgressChanged)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.opt_feed, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_update -> {
TrackWorker.startNow(requireContext())
Snackbar.make(
binding.recyclerView,
R.string.feed_will_update_soon,
Snackbar.LENGTH_LONG,
).show()
true
}
R.id.action_clear_feed -> {
MaterialAlertDialogBuilder(context ?: return false)
.setTitle(R.string.clear_updates_feed)
.setMessage(R.string.text_clear_updates_feed_prompt)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.clear) { _, _ ->
viewModel.clearFeed()
}.show()
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onDestroyView() {
feedAdapter = null
updateStatusSnackbar = null

View File

@@ -0,0 +1,48 @@
package org.koitharu.kotatsu.tracker.ui
import android.content.Context
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.core.view.MenuProvider
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.tracker.work.TrackWorker
class FeedMenuProvider(
private val snackbarHost: View,
private val viewModel: FeedViewModel,
) : MenuProvider {
private val context: Context
get() = snackbarHost.context
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_feed, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.action_update -> {
TrackWorker.startNow(context)
Snackbar.make(
snackbarHost,
R.string.feed_will_update_soon,
Snackbar.LENGTH_LONG,
).show()
true
}
R.id.action_clear_feed -> {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.clear_updates_feed)
.setMessage(R.string.text_clear_updates_feed_prompt)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.clear) { _, _ ->
viewModel.clearFeed()
}.show()
true
}
else -> false
}
}

View File

@@ -2,9 +2,11 @@ package org.koitharu.kotatsu.utils.ext
import android.os.Bundle
import android.os.Parcelable
import androidx.core.view.MenuProvider
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import java.io.Serializable
@@ -43,4 +45,8 @@ fun DialogFragment.showAllowStateLoss(manager: FragmentManager, tag: String?) {
if (!manager.isStateSaved) {
show(manager, tag)
}
}
fun Fragment.addMenuProvider(provider: MenuProvider) {
requireActivity().addMenuProvider(provider, viewLifecycleOwner, Lifecycle.State.RESUMED)
}

View File

@@ -3,12 +3,14 @@ package org.koitharu.kotatsu.utils.ext
import androidx.core.os.LocaleListCompat
import java.util.*
fun LocaleListCompat.toList(): List<Locale> = createList(size()) { i -> get(i) }
fun LocaleListCompat.getOrThrow(index: Int) = get(index) ?: throw kotlin.NoSuchElementException()
fun LocaleListCompat.toList(): List<Locale> = createList(size()) { i -> getOrThrow(i) }
operator fun LocaleListCompat.iterator() = object : Iterator<Locale> {
private var index = 0
override fun hasNext(): Boolean = index < size()
override fun next(): Locale = get(index++)
override fun next(): Locale = getOrThrow(index++)
}
inline fun <R, C : MutableCollection<in R>> LocaleListCompat.mapTo(
@@ -17,7 +19,7 @@ inline fun <R, C : MutableCollection<in R>> LocaleListCompat.mapTo(
): C {
val len = size()
for (i in 0 until len) {
val item = get(i)
val item = get(i) ?: continue
destination.add(block(item))
}
return destination