From 50f8cb919373efdb0ef6e0fb3ff4f2af0aaf1cc6 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 11 May 2020 17:30:59 +0300 Subject: [PATCH] Action mode chapters selection --- .../kotatsu/domain/history/ChapterExtra.kt | 2 +- .../kotatsu/ui/details/ChapterHolder.kt | 7 ++ .../kotatsu/ui/details/ChaptersAdapter.kt | 34 ++++++++ .../kotatsu/ui/details/ChaptersFragment.kt | 82 ++++++++++++------- .../ui/details/MangaDetailsActivity.kt | 28 ++++++- .../kotatsu/ui/details/MangaDetailsAdapter.kt | 18 ++-- app/src/main/res/drawable/ic_check.xml | 5 ++ app/src/main/res/drawable/ic_save.xml | 11 +++ app/src/main/res/drawable/ic_select_all.xml | 11 +++ app/src/main/res/layout/activity_details.xml | 2 +- app/src/main/res/layout/item_chapter.xml | 28 ++++++- app/src/main/res/menu/mode_chapters.xml | 18 ++++ app/src/main/res/values-ru/plurals.xml | 5 ++ app/src/main/res/values/plurals.xml | 4 + 14 files changed, 204 insertions(+), 51 deletions(-) create mode 100644 app/src/main/res/drawable/ic_check.xml create mode 100644 app/src/main/res/drawable/ic_save.xml create mode 100644 app/src/main/res/drawable/ic_select_all.xml create mode 100644 app/src/main/res/menu/mode_chapters.xml diff --git a/app/src/main/java/org/koitharu/kotatsu/domain/history/ChapterExtra.kt b/app/src/main/java/org/koitharu/kotatsu/domain/history/ChapterExtra.kt index a9f2d4341..4d6d2b411 100644 --- a/app/src/main/java/org/koitharu/kotatsu/domain/history/ChapterExtra.kt +++ b/app/src/main/java/org/koitharu/kotatsu/domain/history/ChapterExtra.kt @@ -2,5 +2,5 @@ package org.koitharu.kotatsu.domain.history enum class ChapterExtra { - READ, CURRENT, UNREAD, NEW + READ, CURRENT, UNREAD, NEW, CHECKED } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/details/ChapterHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/details/ChapterHolder.kt index 7e91ff0e4..5c6b388a7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/details/ChapterHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/details/ChapterHolder.kt @@ -1,6 +1,8 @@ package org.koitharu.kotatsu.ui.details +import android.graphics.Color import android.view.ViewGroup +import androidx.core.view.isVisible import kotlinx.android.synthetic.main.item_chapter.* import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.MangaChapter @@ -14,6 +16,7 @@ class ChapterHolder(parent: ViewGroup) : override fun onBind(data: MangaChapter, extra: ChapterExtra) { textView_title.text = data.name textView_number.text = data.number.toString() + imageView_check.isVisible = extra == ChapterExtra.CHECKED when (extra) { ChapterExtra.UNREAD -> { textView_number.setBackgroundResource(R.drawable.bg_badge_default) @@ -31,6 +34,10 @@ class ChapterHolder(parent: ViewGroup) : textView_number.setBackgroundResource(R.drawable.bg_badge_accent) textView_number.setTextColor(context.getThemeColor(android.R.attr.textColorPrimaryInverse)) } + ChapterExtra.CHECKED -> { + textView_number.setBackgroundResource(R.drawable.bg_badge_accent) + textView_number.setTextColor(Color.TRANSPARENT) + } } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/details/ChaptersAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/details/ChaptersAdapter.kt index cc9ce1800..8127ed0e2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/details/ChaptersAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/details/ChaptersAdapter.kt @@ -10,6 +10,14 @@ import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener class ChaptersAdapter(onItemClickListener: OnRecyclerItemClickListener) : BaseRecyclerAdapter(onItemClickListener) { + private val checkedIds = HashSet() + + val checkedItemsCount: Int + get() = checkedIds.size + + val checkedItemsIds: Set + get() = checkedIds + var currentChapterId: Long? = null set(value) { field = value @@ -26,11 +34,37 @@ class ChaptersAdapter(onItemClickListener: OnRecyclerItemClickListener ChapterExtra.CHECKED currentChapterPosition == RecyclerView.NO_POSITION || currentChapterPosition < position -> if (position >= itemCount - newChaptersCount) { ChapterExtra.NEW diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/details/ChaptersFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/details/ChaptersFragment.kt index a4dd04424..c6370a276 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/details/ChaptersFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/details/ChaptersFragment.kt @@ -1,11 +1,12 @@ package org.koitharu.kotatsu.ui.details import android.app.ActivityOptions -import android.content.Context import android.os.Bundle +import android.view.Menu import android.view.MenuItem import android.view.View -import androidx.appcompat.widget.PopupMenu +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ActionMode import androidx.core.view.isVisible import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager @@ -21,7 +22,7 @@ import org.koitharu.kotatsu.ui.reader.ReaderActivity import org.koitharu.kotatsu.utils.ext.resolveDp class ChaptersFragment : BaseFragment(R.layout.fragment_chapters), MangaDetailsView, - OnRecyclerItemClickListener { + OnRecyclerItemClickListener, ActionMode.Callback { @Suppress("unused") private val presenter by moxyPresenter(factory = MangaDetailsPresenter.Companion::getInstance) @@ -29,6 +30,7 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters), MangaDetailsV private var manga: Manga? = null private lateinit var adapter: ChaptersAdapter + private var actionMode: ActionMode? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -69,6 +71,15 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters), MangaDetailsV override fun onFavouriteChanged(categories: List) = Unit override fun onItemClick(item: MangaChapter, position: Int, view: View) { + if (adapter.checkedItemsCount != 0) { + adapter.toggleItemChecked(item.id) + if (adapter.checkedItemsCount == 0) { + actionMode?.finish() + } else { + actionMode?.invalidate() + } + return + } val options = ActivityOptions.makeScaleUpAnimation( view, 0, @@ -86,16 +97,13 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters), MangaDetailsV } override fun onItemLongClick(item: MangaChapter, position: Int, view: View): Boolean { - if (item.source == MangaSource.LOCAL) { - return false + if (actionMode == null) { + actionMode = (activity as? AppCompatActivity)?.startSupportActionMode(this) } - return context?.run { - val menu = PopupMenu(this, view) - menu.inflate(R.menu.popup_chapter) - menu.setOnMenuItemClickListener(PopupMenuListener(this, manga ?: return false, item)) - menu.show() - true - } ?: false + return actionMode?.also { + adapter.setItemIsChecked(item.id, true) + it.invalidate() + } != null } private fun scrollToCurrent() { @@ -107,29 +115,45 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters), MangaDetailsV } } - private class PopupMenuListener( - private val context: Context, - private val manga: Manga, - private val chapter: MangaChapter - ) : PopupMenu.OnMenuItemClickListener { - - override fun onMenuItemClick(item: MenuItem?): Boolean = when (item?.itemId) { - R.id.action_save_this -> { - DownloadService.start(context, manga, setOf(chapter.id)) + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return when (item.itemId) { + R.id.action_save -> { + DownloadService.start( + context ?: return false, + manga ?: return false, + adapter.checkedItemsIds + ) true } - R.id.action_save_this_next -> { - DownloadService.start(context, manga, manga.chapters.orEmpty() - .filter { x -> x.number >= chapter.number }.map { x -> x.id }) - true - } - R.id.action_save_this_prev -> { - DownloadService.start(context, manga, manga.chapters.orEmpty() - .filter { x -> x.number <= chapter.number }.map { x -> x.id }) + R.id.action_select_all -> { + adapter.checkAll() + mode.invalidate() true } else -> false } + } + + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { + mode.menuInflater.inflate(R.menu.mode_chapters, menu) + menu.findItem(R.id.action_save).isVisible = manga?.source != MangaSource.LOCAL + mode.title = manga?.title + return true + } + + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { + val count = adapter.checkedItemsCount + mode.subtitle = resources.getQuantityString( + R.plurals.chapters_from_x, + count, + count, + adapter.itemCount + ) + return true + } + override fun onDestroyActionMode(mode: ActionMode?) { + adapter.clearChecked() + actionMode = null } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsActivity.kt index ec9623306..af4802030 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsActivity.kt @@ -8,10 +8,13 @@ import android.view.Menu import android.view.MenuItem import android.widget.Toast import androidx.appcompat.app.AlertDialog +import androidx.appcompat.view.ActionMode import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.net.toFile import androidx.lifecycle.lifecycleScope import com.google.android.material.snackbar.Snackbar +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator import kotlinx.android.synthetic.main.activity_details.* import kotlinx.coroutines.launch import moxy.MvpDelegate @@ -29,7 +32,8 @@ import org.koitharu.kotatsu.utils.MangaShortcut import org.koitharu.kotatsu.utils.ShareHelper import org.koitharu.kotatsu.utils.ext.getDisplayMessage -class MangaDetailsActivity : BaseActivity(), MangaDetailsView { +class MangaDetailsActivity : BaseActivity(), MangaDetailsView, + TabLayoutMediator.TabConfigurationStrategy { private val presenter by moxyPresenter(factory = MangaDetailsPresenter.Companion::getInstance) @@ -39,8 +43,8 @@ class MangaDetailsActivity : BaseActivity(), MangaDetailsView { super.onCreate(savedInstanceState) setContentView(R.layout.activity_details) supportActionBar?.setDisplayHomeAsUpEnabled(true) - pager.adapter = MangaDetailsAdapter(resources, supportFragmentManager) - tabs.setupWithViewPager(pager) + pager.adapter = MangaDetailsAdapter(this) + TabLayoutMediator(tabs, pager, this).attach() if (savedInstanceState?.containsKey(MvpDelegate.MOXY_DELEGATE_TAGS_KEY) != true) { intent?.getParcelableExtra(EXTRA_MANGA)?.let { presenter.loadDetails(it, true) @@ -169,6 +173,24 @@ class MangaDetailsActivity : BaseActivity(), MangaDetailsView { else -> super.onOptionsItemSelected(item) } + override fun onConfigureTab(tab: TabLayout.Tab, position: Int) { + tab.text = when(position) { + 0 -> getString(R.string.details) + 1 -> getString(R.string.chapters) + else -> null + } + } + + override fun onSupportActionModeStarted(mode: ActionMode) { + super.onSupportActionModeStarted(mode) + pager.isUserInputEnabled = false + } + + override fun onSupportActionModeFinished(mode: ActionMode) { + super.onSupportActionModeFinished(mode) + pager.isUserInputEnabled = true + } + companion object { private const val EXTRA_MANGA = "manga" diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsAdapter.kt index 94c018bc3..02a881ef6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsAdapter.kt @@ -1,24 +1,16 @@ package org.koitharu.kotatsu.ui.details -import android.content.res.Resources import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentPagerAdapter -import org.koitharu.kotatsu.R +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter -class MangaDetailsAdapter(private val resources: Resources, fm: FragmentManager) : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { +class MangaDetailsAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) { - override fun getCount() = 2 + override fun getItemCount() = 2 - override fun getItem(position: Int): Fragment = when(position) { + override fun createFragment(position: Int): Fragment = when(position) { 0 -> MangaDetailsFragment() 1 -> ChaptersFragment() else -> throw IndexOutOfBoundsException("No fragment for position $position") } - - override fun getPageTitle(position: Int): CharSequence? = when(position) { - 0 -> resources.getString(R.string.details) - 1 -> resources.getString(R.string.chapters) - else -> null - } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_check.xml b/app/src/main/res/drawable/ic_check.xml new file mode 100644 index 000000000..2501e9fd9 --- /dev/null +++ b/app/src/main/res/drawable/ic_check.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_save.xml b/app/src/main/res/drawable/ic_save.xml new file mode 100644 index 000000000..4d083ab12 --- /dev/null +++ b/app/src/main/res/drawable/ic_save.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_select_all.xml b/app/src/main/res/drawable/ic_select_all.xml new file mode 100644 index 000000000..59a0c0354 --- /dev/null +++ b/app/src/main/res/drawable/ic_select_all.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/layout/activity_details.xml b/app/src/main/res/layout/activity_details.xml index caa3f66b1..2680d6b34 100644 --- a/app/src/main/res/layout/activity_details.xml +++ b/app/src/main/res/layout/activity_details.xml @@ -27,7 +27,7 @@ - - @@ -13,21 +13,41 @@ android:id="@+id/textView_number" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_centerVertical="true" android:background="@drawable/bg_badge_default" android:gravity="center" android:minWidth="26dp" + android:textAlignment="center" android:textColor="?android:textColorSecondaryInverse" tools:text="13" /> + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/menu/mode_chapters.xml b/app/src/main/res/menu/mode_chapters.xml new file mode 100644 index 000000000..56853beb5 --- /dev/null +++ b/app/src/main/res/menu/mode_chapters.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ru/plurals.xml b/app/src/main/res/values-ru/plurals.xml index 1ca8d51bf..e429b244f 100644 --- a/app/src/main/res/values-ru/plurals.xml +++ b/app/src/main/res/values-ru/plurals.xml @@ -15,4 +15,9 @@ %1$d новых главы %1$d новых глав + + %1$d глава из %2$d + %1$d главы из %2$d + %1$d глав из %2$d + \ No newline at end of file diff --git a/app/src/main/res/values/plurals.xml b/app/src/main/res/values/plurals.xml index 521d57dfd..b8688fe55 100644 --- a/app/src/main/res/values/plurals.xml +++ b/app/src/main/res/values/plurals.xml @@ -12,4 +12,8 @@ %1$d new chapter %1$d new chapters + + %1$d chapter from %2$d + %1$d chapters from %2$d + \ No newline at end of file