More floating action modes in details

Tags now use action mode, shows the option to search the manga's source, even in library
Description text now also adds the option for global/local searching

All of these no longer show a snackbar that the text is copied (since you'd be tapping the option to select the text anyway
This commit is contained in:
Jays2Kings 2022-05-16 16:32:46 -04:00
parent 955277c4ae
commit ef5a05050d
5 changed files with 134 additions and 23 deletions

View file

@ -1,6 +1,8 @@
package eu.kanade.tachiyomi.ui.manga
import android.view.ActionMode
import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.ItemTouchHelper
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
@ -133,11 +135,12 @@ class MangaDetailsAdapter(
fun topCoverHeight(): Int
fun localSearch(text: String)
fun globalSearch(text: String)
fun showFloatingActionMode(view: View, content: String, label: Int)
fun showFloatingActionMode(view: TextView, content: String? = null, searchSource: Boolean = false)
fun showChapterFilter()
fun favoriteManga(longPress: Boolean)
fun copyToClipboard(content: String, label: Int, useToast: Boolean = false)
fun copyToClipboard(content: String, label: String, useToast: Boolean = false)
fun customActionMode(view: TextView): ActionMode.Callback
fun copyToClipboard(content: String, label: String?, useToast: Boolean = false)
fun zoomImageFromThumb(thumbView: View)
fun showTrackingSheet()
fun updateScroll()

View file

@ -19,6 +19,7 @@ import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.FloatRange
import androidx.appcompat.app.AppCompatActivity
@ -39,6 +40,7 @@ import coil.imageLoader
import coil.request.ImageRequest
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType
import com.google.android.material.chip.Chip
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import eu.davidea.flexibleadapter.FlexibleAdapter
@ -54,8 +56,10 @@ import eu.kanade.tachiyomi.data.image.coil.getBestColor
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.databinding.MangaDetailsControllerBinding
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.icon
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.MaterialMenuSheet
import eu.kanade.tachiyomi.ui.base.SmallToolbarInterface
@ -1379,12 +1383,27 @@ class MangaDetailsController :
router.getControllerWithTag(R.id.nav_library.toString()) as LibraryController
controller.search(text)
}
}
}
fun sourceSearch(text: String) {
when (
val previousController =
router.backstack.getOrNull(router.backstackSize - 2)?.controller
) {
is BrowseSourceController -> {
if (presenter.source is HttpSource) {
router.handleBack()
previousController.searchWithGenre(text)
}
}
else -> {
if (presenter.source is CatalogueSource) {
val controller = BrowseSourceController(presenter.source)
router.pushController(controller.withFadeTransaction())
controller.searchWithGenre(text)
}
}
}
}
@ -1393,13 +1412,22 @@ class MangaDetailsController :
router.pushController(GlobalSearchController(text).withFadeTransaction())
}
override fun showFloatingActionMode(view: View, content: String, label: Int) {
if (content.isBlank()) return
override fun showFloatingActionMode(view: TextView, content: String?, searchSource: Boolean) {
finishFloatingActionMode()
floatingActionMode = view.startActionMode(
FloatingMangaDetailsActionModeCallback(content, label),
android.view.ActionMode.TYPE_FLOATING,
val actionModeCallback = if (content != null) FloatingMangaDetailsActionModeCallback(
content,
searchSource = searchSource,
)
else FloatingMangaDetailsActionModeCallback(view, searchSource = searchSource)
if (view is Chip) {
view.isActivated = true
}
floatingActionMode =
view.startActionMode(actionModeCallback, android.view.ActionMode.TYPE_FLOATING)
}
override fun customActionMode(view: TextView): android.view.ActionMode.Callback {
return FloatingMangaDetailsActionModeCallback(view, false)
}
override fun showChapterFilter() {
@ -1500,7 +1528,7 @@ class MangaDetailsController :
*/
override fun copyToClipboard(content: String, label: Int, useToast: Boolean) {
val view = view ?: return
val contentType = view.context.getString(label)
val contentType = if (label != 0) view.context.getString(label) else null
copyToClipboard(content, contentType, useToast)
}
@ -1510,7 +1538,7 @@ class MangaDetailsController :
* @param content the actual text to copy to the board
* @param label Label to show to the user describing the content
*/
override fun copyToClipboard(content: String, label: String, useToast: Boolean) {
override fun copyToClipboard(content: String, label: String?, useToast: Boolean) {
if (content.isBlank()) return
val activity = activity ?: return
@ -1519,6 +1547,7 @@ class MangaDetailsController :
val clipboard = activity.getSystemService(ClipboardManager::class.java)
clipboard.setPrimaryClip(ClipData.newPlainText(label, content))
label ?: return
if (useToast) {
activity.toast(view.context.getString(R.string._copied_to_clipboard, label))
} else {
@ -1681,20 +1710,49 @@ class MangaDetailsController :
}
inner class FloatingMangaDetailsActionModeCallback(
val text: String,
val label: Int,
private val textView: TextView?,
private val showCopy: Boolean = true,
private val searchSource: Boolean = false,
) : android.view.ActionMode.Callback {
constructor(
text: String,
showCopy: Boolean = true,
searchSource: Boolean = false,
) : this(null, showCopy, searchSource) {
customText = text
}
var customText: String? = null
val text: String
get() {
return customText ?: if (textView?.isTextSelectable == true) {
textView.text.subSequence(textView.selectionStart, textView.selectionEnd)
.toString()
} else {
textView?.text?.toString() ?: ""
}
}
override fun onCreateActionMode(mode: android.view.ActionMode?, menu: Menu?): Boolean {
mode?.menuInflater?.inflate(R.menu.manga_details_title, menu)
mode?.menuInflater?.inflate(
if (searchSource) R.menu.manga_details_tag else R.menu.manga_details_title,
menu,
)
menu?.findItem(R.id.action_copy)?.isVisible = showCopy
val sourceMenuItem = menu?.findItem(R.id.action_source_search)
sourceMenuItem?.isVisible = searchSource && presenter.source is CatalogueSource
val context = view?.context ?: return false
val prevController = router.backstack.getOrNull(router.backstackSize - 2)?.controller
val localItem = menu?.findItem(R.id.action_local_search) ?: return true
localItem.isVisible = when (prevController) {
is LibraryController, is BrowseController, is RecentsController -> true
is LibraryController, is RecentsController -> true
else -> false
}
val library = context.getString(R.string.library).lowercase(Locale.getDefault())
localItem.title = context.getString(R.string.search_, library)
sourceMenuItem?.title = context.getString(R.string.search_, presenter.source.name)
if (searchSource) {
sourceMenuItem?.icon = presenter.source.icon()
}
return true
}
@ -1706,18 +1764,25 @@ class MangaDetailsController :
mode: android.view.ActionMode?,
item: MenuItem?,
): Boolean {
mode?.finish()
val context = view?.context ?: return true
when (item?.itemId) {
R.id.action_copy -> copyToClipboard(text, context.getString(label))
R.id.action_copy -> copyToClipboard(text, null)
R.id.action_global_search -> globalSearch(text)
R.id.action_source_search -> sourceSearch(text)
R.id.action_local_search -> localSearch(text)
}
if (showCopy) {
mode?.finish()
}
return true
}
override fun onDestroyActionMode(mode: android.view.ActionMode?) {
floatingActionMode = null
if (showCopy) {
floatingActionMode = null
}
if (textView is Chip) {
textView.isActivated = false
}
}
}
}

View file

@ -12,6 +12,7 @@ import android.os.Build
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
@ -129,14 +130,15 @@ class MangaHeaderHolder(
}
title.setOnClickListener { view ->
title.text?.toString()?.toNormalized()?.let {
adapter.delegate.showFloatingActionMode(view, it, R.string.title)
adapter.delegate.showFloatingActionMode(view as TextView, it)
}
}
mangaAuthor.setOnClickListener { view ->
mangaAuthor.text?.toString()?.let {
adapter.delegate.showFloatingActionMode(view, it, R.string.title)
adapter.delegate.showFloatingActionMode(view as TextView, it)
}
}
mangaSummary.customSelectionActionModeCallback = adapter.delegate.customActionMode(mangaSummary)
applyBlur()
mangaCover.setOnClickListener { adapter.delegate.zoomImageFromThumb(coverCard) }
trackButton.setOnClickListener { adapter.delegate.showTrackingSheet() }
@ -469,6 +471,19 @@ class MangaHeaderHolder(
if (dark) 0.945f else 0.175f,
),
)
val states = arrayOf(
intArrayOf(-android.R.attr.state_activated),
intArrayOf(),
)
val colors = intArrayOf(
downloadedColor,
ColorUtils.blendARGB(
downloadedColor,
context.getResourceColor(R.attr.colorControlNormal),
0.25f,
),
)
val colorStateList = ColorStateList(states, colors)
if (manga.genre.isNullOrBlank().not()) {
(manga.getGenres() ?: emptyList()).map { genreText ->
val chip = LayoutInflater.from(binding.root.context).inflate(
@ -478,14 +493,14 @@ class MangaHeaderHolder(
) as Chip
val id = View.generateViewId()
chip.id = id
chip.chipBackgroundColor = ColorStateList.valueOf(downloadedColor)
chip.chipBackgroundColor = colorStateList
chip.setTextColor(textColor)
chip.text = genreText
chip.setOnClickListener {
adapter.delegate.localSearch(genreText)
adapter.delegate.showFloatingActionMode(chip, searchSource = true)
}
chip.setOnLongClickListener {
adapter.delegate.copyToClipboard(genreText, genreText)
adapter.delegate.showFloatingActionMode(chip, searchSource = true)
true
}
this.addView(chip)

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_copy"
android:icon="@drawable/ic_done_all_24dp"
android:title="@string/copy_value"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_local_search"
android:icon="@drawable/ic_search_24dp"
android:title="@string/search"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_source_search"
android:icon="@drawable/ic_extension_update_24dp"
android:title="@string/search"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_global_search"
android:icon="@drawable/ic_open_in_webview_24dp"
android:title="@string/label_global_search"
app:showAsAction="ifRoom" />
</menu>

View file

@ -8,7 +8,7 @@
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_global_search"
android:icon="@drawable/ic_search_24dp"
android:icon="@drawable/ic_open_in_webview_24dp"
android:title="@string/label_global_search"
app:showAsAction="ifRoom" />
<item