From 2d520026a13d9182b589b31900eb71c95219721d Mon Sep 17 00:00:00 2001 From: Jays2Kings Date: Sat, 24 Apr 2021 14:52:05 -0400 Subject: [PATCH] Search bar toolbar in some screens + many search fixes This would be a yoink if this wasn't from scratch, so let's call it a spiritual yoink Floating search bar as a toolbar for the following screens: Library, Recents, Browse, Settings, Browse Source, Global search Fixes include: Searching now saves when pausing/resuming tachi activity Other changes: Tapping on the search bar in library also shows backdrop now (which will collapse on close/starting to type Also using a subtitle in the library to show the current category *good luck TF* --- .../kanade/tachiyomi/ui/base/BaseToolbar.kt | 93 +++++++++++++++ .../tachiyomi/ui/base/CenteredToolbar.kt | 84 +------------ .../tachiyomi/ui/base/FloatingToolbar.kt | 77 ++++++++++++ .../tachiyomi/ui/base/MiniSearchView.kt | 24 ++++ .../ui/base/controller/BaseController.kt | 13 ++ .../tachiyomi/ui/library/LibraryController.kt | 108 +++++++++++++---- .../kanade/tachiyomi/ui/main/MainActivity.kt | 77 +++++++++++- .../tachiyomi/ui/main/OverflowDialog.kt | 3 +- .../tachiyomi/ui/main/SearchActivity.kt | 8 +- .../ui/manga/MangaDetailsController.kt | 6 +- .../tachiyomi/ui/recents/RecentsController.kt | 37 ++++-- .../ui/setting/SettingsController.kt | 4 + .../ui/setting/SettingsMainController.kt | 3 +- .../search/SettingsSearchController.kt | 4 +- .../tachiyomi/ui/source/BrowseController.kt | 71 +++++------ .../source/browse/BrowseSourceController.kt | 4 +- .../global_search/GlobalSearchController.kt | 2 + .../util/view/ControllerExtensions.kt | 86 ++++++++++++-- .../main/res/layout/library_controller.xml | 51 ++++---- app/src/main/res/layout/main_activity.xml | 112 +++++++++++++++--- .../main/res/layout/tachi_overflow_layout.xml | 2 +- app/src/main/res/menu/browse_source.xml | 2 +- app/src/main/res/menu/catalogue_main.xml | 2 +- app/src/main/res/menu/catalogue_new_list.xml | 2 +- app/src/main/res/menu/library.xml | 2 +- app/src/main/res/menu/recents.xml | 2 +- app/src/main/res/menu/settings_main.xml | 2 +- app/src/main/res/values/strings.xml | 1 + 28 files changed, 666 insertions(+), 216 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/base/BaseToolbar.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/base/FloatingToolbar.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/base/MiniSearchView.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/BaseToolbar.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/BaseToolbar.kt new file mode 100644 index 0000000000..42a633901b --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/BaseToolbar.kt @@ -0,0 +1,93 @@ +package eu.kanade.tachiyomi.ui.base + +import android.content.Context +import android.util.AttributeSet +import android.widget.TextView +import androidx.annotation.DrawableRes +import androidx.appcompat.graphics.drawable.DrawerArrowDrawable +import androidx.core.view.isVisible +import com.google.android.material.appbar.MaterialToolbar +import eu.kanade.tachiyomi.R + +open class BaseToolbar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + MaterialToolbar(context, attrs) { + + protected lateinit var toolbarTitle: TextView + private val defStyleRes = com.google.android.material.R.style.Widget_MaterialComponents_Toolbar + + protected val titleTextAppeance: Int + + var incognito = false + var hasDropdown: Boolean? = null + init { + val a = context.obtainStyledAttributes( + attrs, + R.styleable.Toolbar, + 0, + defStyleRes + ) + titleTextAppeance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0) + a.recycle() + } + + override fun setTitle(resId: Int) { + setCustomTitle(context.getString(resId)) + } + + override fun setTitle(title: CharSequence?) { + setCustomTitle(title) + } + + protected open fun setCustomTitle(title: CharSequence?) { + toolbarTitle.isVisible = true + toolbarTitle.text = title + super.setTitle(null) + if (navigationIcon is DrawerArrowDrawable) { + hideDropdown() + } + setIncognitoMode(incognito) + } + + fun hideDropdown() { + hasDropdown = null + setIcons() + } + + fun showDropdown(down: Boolean = true) { + hasDropdown = down + setIcons() + } + + fun setIncognitoMode(enabled: Boolean) { + incognito = enabled + setIcons() + } + + open fun setIcons() { + toolbarTitle.setCompoundDrawablesRelativeWithIntrinsicBounds( + getIncogRes(), + 0, + getDropdownRes(), + 0 + ) + } + + @DrawableRes + private fun getIncogRes(): Int { + return when { + incognito -> R.drawable.ic_incognito_circle_24dp + hasDropdown != null -> R.drawable.ic_blank_24dp + else -> 0 + } + } + + @DrawableRes + private fun getDropdownRes(): Int { + return when { + hasDropdown == true -> R.drawable.ic_arrow_drop_down_24dp + hasDropdown == false -> R.drawable.ic_arrow_drop_up_24dp + incognito && navigationIcon !is DrawerArrowDrawable -> R.drawable.ic_blank_28dp + else -> 0 + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/CenteredToolbar.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/CenteredToolbar.kt index 7c2a28d96c..9310c5eb16 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/CenteredToolbar.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/CenteredToolbar.kt @@ -4,12 +4,8 @@ import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.view.Gravity -import android.widget.TextView -import androidx.annotation.DrawableRes import androidx.appcompat.graphics.drawable.DrawerArrowDrawable -import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams -import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.textview.MaterialTextView import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.dpToPx @@ -17,25 +13,8 @@ import eu.kanade.tachiyomi.util.system.getResourceColor @SuppressLint("CustomViewStyleable") class CenteredToolbar@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : - MaterialToolbar(context, attrs) { + BaseToolbar(context, attrs) { - private lateinit var toolbarTitle: TextView - private val defStyleRes = com.google.android.material.R.style.Widget_MaterialComponents_Toolbar - - private val titleTextAppeance: Int - - var incognito = false - var hasDropdown: Boolean? = null - init { - val a = context.obtainStyledAttributes( - attrs, - R.styleable.Toolbar, - 0, - defStyleRes - ) - titleTextAppeance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0) - a.recycle() - } override fun onFinishInflate() { super.onFinishInflate() toolbarTitle = findViewById(R.id.toolbar_title) @@ -43,68 +22,11 @@ class CenteredToolbar@JvmOverloads constructor(context: Context, attrs: Attribut toolbarTitle.setTextColor(context.getResourceColor(R.attr.actionBarTintColor)) } - override fun setTitle(resId: Int) { - setCustomTitle(context.getString(resId)) - } - - override fun setTitle(title: CharSequence?) { - setCustomTitle(title) - } - - private fun setCustomTitle(title: CharSequence?) { - toolbarTitle.isVisible = true - toolbarTitle.text = title - super.setTitle(null) + override fun setCustomTitle(title: CharSequence?) { + super.setCustomTitle(title) toolbarTitle.updateLayoutParams { gravity = if (navigationIcon is DrawerArrowDrawable) Gravity.START else Gravity.CENTER } toolbarTitle.compoundDrawablePadding = if (navigationIcon is DrawerArrowDrawable) 6.dpToPx else 0 - if (navigationIcon is DrawerArrowDrawable) { - hideDropdown() - } - setIncognitoMode(incognito) - } - - fun hideDropdown() { - hasDropdown = null - setIcons() - } - - fun showDropdown(down: Boolean = true) { - hasDropdown = down - setIcons() - } - - fun setIncognitoMode(enabled: Boolean) { - incognito = enabled - setIcons() - } - - private fun setIcons() { - toolbarTitle.setCompoundDrawablesRelativeWithIntrinsicBounds( - getIncogRes(), - 0, - getDropdownRes(), - 0 - ) - } - - @DrawableRes - private fun getIncogRes(): Int { - return when { - incognito -> R.drawable.ic_incognito_circle_24dp - hasDropdown != null -> R.drawable.ic_blank_24dp - else -> 0 - } - } - - @DrawableRes - private fun getDropdownRes(): Int { - return when { - hasDropdown == true -> R.drawable.ic_arrow_drop_down_24dp - hasDropdown == false -> R.drawable.ic_arrow_drop_up_24dp - incognito && navigationIcon !is DrawerArrowDrawable -> R.drawable.ic_blank_28dp - else -> 0 - } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/FloatingToolbar.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/FloatingToolbar.kt new file mode 100644 index 0000000000..d45c69f4dd --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/FloatingToolbar.kt @@ -0,0 +1,77 @@ +package eu.kanade.tachiyomi.ui.base + +import android.content.Context +import android.util.AttributeSet +import android.view.Gravity +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.graphics.ColorUtils +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import com.google.android.material.textview.MaterialTextView +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.util.system.getResourceColor + +class FloatingToolbar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + BaseToolbar(context, attrs) { + + private val actionColorAlpha = ColorUtils.setAlphaComponent(context.getResourceColor(R.attr.actionBarTintColor), 200) + private val actionColorAlphaSecondary = ColorUtils.setAlphaComponent(context.getResourceColor(R.attr.actionBarTintColor), 150) + + private lateinit var toolbarsubTitle: TextView + private lateinit var cardIncogImage: ImageView + private val defStyleRes = com.google.android.material.R.style.Widget_MaterialComponents_Toolbar + private val subtitleTextAppeance: Int + + init { + val a = context.obtainStyledAttributes( + attrs, + R.styleable.Toolbar, + 0, + defStyleRes + ) + subtitleTextAppeance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0) + a.recycle() + } + override fun onFinishInflate() { + super.onFinishInflate() + toolbarTitle = findViewById(R.id.card_title) + toolbarTitle.setTextAppearance(titleTextAppeance) + toolbarTitle.setTextColor(actionColorAlpha) + + toolbarsubTitle = findViewById(R.id.card_subtitle) + toolbarsubTitle.setTextAppearance(subtitleTextAppeance) + toolbarsubTitle.setTextColor(actionColorAlphaSecondary) + toolbarsubTitle.isVisible = false + + cardIncogImage = findViewById(R.id.card_incog_image) + + setNavigationIconTint(actionColorAlpha) + } + + override fun setSubtitle(resId: Int) { + setCustomSubtitle(context.getString(resId)) + } + + override fun setSubtitle(subtitle: CharSequence?) { + setCustomSubtitle(subtitle) + } + + override fun setIcons() { + cardIncogImage.isVisible = incognito + } + + private fun setCustomSubtitle(title: CharSequence?) { + toolbarsubTitle.isVisible = !title.isNullOrBlank() + toolbarsubTitle.text = title + super.setSubtitle(null) + } + + override fun setCustomTitle(title: CharSequence?) { + super.setCustomTitle(title) + toolbarTitle.updateLayoutParams { + gravity = Gravity.START + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/MiniSearchView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/MiniSearchView.kt new file mode 100644 index 0000000000..7a24e013e9 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/MiniSearchView.kt @@ -0,0 +1,24 @@ +package eu.kanade.tachiyomi.ui.base + +import android.content.Context +import android.util.AttributeSet +import android.util.TypedValue +import androidx.appcompat.widget.SearchView +import androidx.core.graphics.ColorUtils +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.util.system.getResourceColor + +class MiniSearchView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + SearchView(context, attrs) { + + init { + val searchTextView = + findViewById(androidx.appcompat.R.id.search_src_text) + searchTextView?.setTextAppearance(android.R.style.TextAppearance_Material_Body1) + val actionColorAlpha = + ColorUtils.setAlphaComponent(context.getResourceColor(R.attr.actionBarTintColor), 200) + searchTextView?.setTextColor(actionColorAlpha) + searchTextView?.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f) + searchTextView?.setHintTextColor(actionColorAlpha) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt index c38933f17a..0de8ed8336 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.ui.base.controller +import android.app.Activity import android.os.Bundle import android.view.LayoutInflater import android.view.MenuItem @@ -11,6 +12,7 @@ import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.RestoreViewOnCreateController +import eu.kanade.tachiyomi.util.view.removeQueryListener import timber.log.Timber abstract class BaseController(bundle: Bundle? = null) : @@ -57,6 +59,8 @@ abstract class BaseController(bundle: Bundle? = null) : override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { if (type.isEnter) { setTitle() + } else { + removeQueryListener() } setHasOptionsMenu(type.isEnter) super.onChangeStarted(handler, type) @@ -69,6 +73,11 @@ abstract class BaseController(bundle: Bundle? = null) : return null } + override fun onActivityPaused(activity: Activity) { + super.onActivityPaused(activity) + removeQueryListener() + } + fun setTitle() { var parentController = parentController while (parentController != null) { @@ -114,6 +123,10 @@ abstract class BaseController(bundle: Bundle? = null) : } } + fun MenuItem.fixExpandInvalidate() { + fixExpand { invalidateMenuOnExpand() } + } + /** * Workaround for menu items not disappearing when expanding an expandable item like a SearchView. * [expandActionViewFromInteraction] should be set to true in [onOptionsItemSelected] when the expandable item is selected diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index b56a109e9a..9f2a4ffb8b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -54,6 +54,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.databinding.LibraryControllerBinding import eu.kanade.tachiyomi.source.LocalSource +import eu.kanade.tachiyomi.ui.base.MaterialFastScroll import eu.kanade.tachiyomi.ui.base.MaterialMenuSheet import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.category.CategoryController @@ -67,6 +68,7 @@ import eu.kanade.tachiyomi.ui.library.LibraryGroup.UNGROUPED import eu.kanade.tachiyomi.ui.library.display.TabbedLibraryDisplaySheet import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet import eu.kanade.tachiyomi.ui.main.BottomSheetController +import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.RootSearchInterface import eu.kanade.tachiyomi.ui.manga.MangaDetailsController @@ -126,6 +128,7 @@ class LibraryController( LibraryCategoryAdapter.LibraryListener, BottomSheetController, RootSearchInterface, + FloatingSearchInterface, LibraryServiceListener { init { @@ -209,11 +212,12 @@ class LibraryController( private var hopperOffset = 0f override fun getTitle(): String? { - return if (!showCategoryInTitle || binding.headerTitle.text.isNullOrBlank() || binding.recyclerCover.isClickable) { - view?.context?.getString(R.string.library) - } else { - binding.headerTitle.text.toString() - } + setSubtitle() +// return if (!showCategoryInTitle || binding.headerTitle.text.isNullOrBlank() || binding.recyclerCover.isClickable) { + return searchTitle(view?.context?.getString(R.string.library)) +// } else { +// binding.headerTitle.text.toString() +// } } private var scrollListener = object : RecyclerView.OnScrollListener() { @@ -338,18 +342,30 @@ class LibraryController( if (currentCategory > -1) { binding.categoryRecycler.setCategories(currentCategory) binding.headerTitle.text = presenter.categories[currentCategory].name - setTitle() + setSubtitle() } } fun showMiniBar() { - binding.headerTitle.visibleIf(showCategoryInTitle) - setTitle() + binding.headerCard.visibleIf(showCategoryInTitle) + setSubtitle() + } + + private fun setSubtitle() { + if (!singleCategory && presenter.showAllCategories && + !binding.headerTitle.text.isNullOrBlank() && !binding.recyclerCover.isClickable + ) { + activityBinding?.cardToolbar?.subtitle = binding.headerTitle.text.toString() + } else { + activityBinding?.cardToolbar?.subtitle = null + } } fun showCategoryText(name: String) { textAnim?.cancel() - textAnim = binding.jumperCategoryText.animate().alpha(0f).setDuration(250L).setStartDelay(2000) + textAnim = binding.jumperCategoryText.animate().alpha(0f).setDuration(250L).setStartDelay( + 2000 + ) textAnim?.start() binding.jumperCategoryText.alpha = 1f binding.jumperCategoryText.text = name @@ -432,6 +448,7 @@ class LibraryController( override fun createBinding(inflater: LayoutInflater) = LibraryControllerBinding.inflate(inflater) + @SuppressLint("ClickableViewAccessibility") override fun onViewCreated(view: View) { super.onViewCreated(view) if (!::presenter.isInitialized) presenter = LibraryPresenter(this) @@ -463,9 +480,15 @@ class LibraryController( showCategories(false) } binding.categoryRecycler.onCategoryClicked = { - binding.libraryGridRecycler.recycler.itemAnimator = null scrollToHeader(it) - showCategories(show = false) + showCategories(show = false, closeSearch = true) + } + binding.categoryRecycler.setOnTouchListener { _, _ -> + val searchView = activityBinding?.cardToolbar?.menu?.findItem(R.id.action_search)?.actionView + ?: return@setOnTouchListener false + val imm = activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager + imm!!.hideSoftInputFromWindow(searchView.windowToken, 0) + false } binding.categoryRecycler.onShowAllClicked = { isChecked -> preferences.showAllCategories().set(isChecked) @@ -486,10 +509,12 @@ class LibraryController( binding.fastScroller.updateLayoutParams { topMargin = binding.libraryGridRecycler.recycler.paddingTop } - binding.headerTitle.updatePaddingRelative(top = insets.systemWindowInsetTop + 2.dpToPx) + binding.headerCard.updateLayoutParams { + topMargin = insets.systemWindowInsetTop + 4.dpToPx + } }, onLeavingController = { - binding.headerTitle.gone() + binding.headerCard.gone() }, onBottomNavUpdate = { updateFilterSheetY() @@ -616,7 +641,7 @@ class LibraryController( 3 -> showGroupOptions() 2 -> showDisplayOptions() 1 -> if (canCollapseOrExpandCategory() != null) presenter.toggleAllCategoryVisibility() - else -> activityBinding?.toolbar?.menu?.performIdentifierAction( + else -> activityBinding?.cardToolbar?.menu?.performIdentifierAction( R.id.action_search, 0 ) @@ -779,7 +804,12 @@ class LibraryController( view.elevation = 15f.dpToPx setAction(R.string.cancel) { LibraryUpdateService.stop(context) - Handler().post { NotificationReceiver.dismissNotification(context, Notifications.ID_LIBRARY_PROGRESS) } + Handler().post { + NotificationReceiver.dismissNotification( + context, + Notifications.ID_LIBRARY_PROGRESS + ) + } } } } @@ -906,7 +936,9 @@ class LibraryController( binding.progress.gone() if (!freshStart) { justStarted = false - if (binding.recyclerLayout.alpha == 0f) binding.recyclerLayout.animate().alpha(1f).setDuration(500) + if (binding.recyclerLayout.alpha == 0f) binding.recyclerLayout.animate().alpha(1f).setDuration( + 500 + ) .start() } else binding.recyclerLayout.alpha = 1f if (justStarted && freshStart) { @@ -942,6 +974,7 @@ class LibraryController( showSlideAnimation() } } + setSubtitle() showMiniBar() } } @@ -985,9 +1018,12 @@ class LibraryController( .setDuration(duration) } - private fun showCategories(show: Boolean) { + private fun showCategories(show: Boolean, closeSearch: Boolean = false) { binding.recyclerCover.isClickable = show binding.recyclerCover.isFocusable = show + if (closeSearch) { + activityBinding?.cardToolbar?.menu?.findItem(R.id.action_search)?.collapseActionView() + } val full = binding.categoryRecycler.height.toFloat() + binding.libraryGridRecycler.recycler.paddingTop val translateY = if (show) full else 0f binding.libraryGridRecycler.recycler.animate().translationY(translateY).apply { @@ -1002,7 +1038,7 @@ class LibraryController( binding.libraryGridRecycler.recycler.suppressLayout(show) activityBinding?.toolbar?.showDropdown(!show) binding.swipeRefresh.isEnabled = !show - setTitle() + setSubtitle() if (show) { binding.categoryRecycler.scrollToCategory(activeCategory) binding.fastScroller.hideScrollbar() @@ -1075,7 +1111,10 @@ class LibraryController( (binding.libraryGridRecycler.recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() binding.libraryGridRecycler.recycler.adapter = adapter - (binding.libraryGridRecycler.recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(position, 0) + (binding.libraryGridRecycler.recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset( + position, + 0 + ) } fun search(query: String?): Boolean { @@ -1085,7 +1124,9 @@ class LibraryController( adapter.addScrollableHeader(searchItem) } else if (this.query.isNotBlank()) { searchItem.string = this.query - (binding.libraryGridRecycler.recycler.findViewHolderForAdapterPosition(0) as? SearchGlobalItem.Holder)?.bind(this.query) + (binding.libraryGridRecycler.recycler.findViewHolderForAdapterPosition(0) as? SearchGlobalItem.Holder)?.bind( + this.query + ) } else if (this.query.isBlank() && adapter.scrollableHeaders.isNotEmpty()) { adapter.removeAllScrollableHeaders() } @@ -1264,7 +1305,10 @@ class LibraryController( ) || adapter.getItem(fromPosition) == null ) { - binding.libraryGridRecycler.recycler.scrollBy(0, binding.libraryGridRecycler.recycler.paddingTop) + binding.libraryGridRecycler.recycler.scrollBy( + 0, + binding.libraryGridRecycler.recycler.paddingTop + ) } if (lastItemPosition == toPosition) lastItemPosition = null else if (lastItemPosition == null) lastItemPosition = fromPosition @@ -1481,8 +1525,26 @@ class LibraryController( searchView.clearFocus() } - setOnQueryTextChangeListener(searchView) { search(it) } - searchItem.fixExpand(onExpand = { invalidateMenuOnExpand() }) + setOnQueryTextChangeListener(searchView) { + if (!it.isNullOrEmpty() && binding.recyclerCover.isClickable) { + showCategories(false) + } + search(it) + } + searchItem.fixExpand( + onExpand = { + if (!binding.recyclerCover.isClickable && query.isBlank()) { + showCategories(true) + } + invalidateMenuOnExpand() + }, + onCollapse = { + if (binding.recyclerCover.isClickable) { + showCategories(false) + } + true + } + ) } override fun onOptionsItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 0f310531dc..52a6e8bd5e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -12,6 +12,7 @@ import android.os.Bundle import android.os.Handler import android.provider.Settings import android.view.GestureDetector +import android.view.Menu import android.view.MenuItem import android.view.MotionEvent import android.view.View @@ -21,6 +22,7 @@ import android.view.WindowManager import android.webkit.WebView import androidx.annotation.IdRes import androidx.appcompat.graphics.drawable.DrawerArrowDrawable +import androidx.appcompat.widget.Toolbar import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils import androidx.core.view.GestureDetectorCompat @@ -112,6 +114,7 @@ open class MainActivity : BaseActivity(), DownloadServiceLi private val isUpdaterEnabled = BuildConfig.INCLUDE_UPDATER var tabAnimation: ValueAnimator? = null var overflowDialog: Dialog? = null + var currentToolbar: Toolbar? = null fun setUndoSnackBar(snackBar: Snackbar?, extraViewToCheck: View? = null) { this.snackBar = snackBar @@ -145,7 +148,7 @@ open class MainActivity : BaseActivity(), DownloadServiceLi binding = MainActivityBinding.inflate(layoutInflater) setContentView(binding.root) - setSupportActionBar(binding.toolbar) + setFloatingToolbar(true) drawerArrow = DrawerArrowDrawable(this) drawerArrow?.color = getResourceColor(R.attr.actionBarTintColor) @@ -270,6 +273,17 @@ open class MainActivity : BaseActivity(), DownloadServiceLi } else onBackPressed() } + binding.cardToolbar.setNavigationOnClickListener { + val rootSearchController = router.backstack.lastOrNull()?.controller() + if (rootSearchController is RootSearchInterface) { + rootSearchController.expandSearch() + } else onBackPressed() + } + + binding.cardToolbar.setOnClickListener { + binding.cardToolbar.menu.findItem(R.id.action_search)?.expandActionView() + } + binding.bottomNav.visibleIf(!hideBottomNav) binding.bottomView.visibility = if (hideBottomNav) View.GONE else binding.bottomView.visibility binding.bottomNav.alpha = if (hideBottomNav) 0f else 1f @@ -325,12 +339,42 @@ open class MainActivity : BaseActivity(), DownloadServiceLi } preferences.incognitoMode() - .asImmediateFlow { binding.toolbar.setIncognitoMode(it) } + .asImmediateFlow { + binding.toolbar.setIncognitoMode(it) + binding.cardToolbar.setIncognitoMode(it) + } .launchIn(lifecycleScope) setExtensionsBadge() } + fun setFloatingToolbar(show: Boolean, solidBG: Boolean = false) { + val oldTB = currentToolbar + currentToolbar = if (show) { + binding.cardToolbar + } else { + binding.toolbar + } + if (oldTB != currentToolbar) { + setSupportActionBar(currentToolbar) + } + binding.toolbar.isVisible = !show + binding.cardFrame.isVisible = show + binding.appBar.setBackgroundColor( + if (show && !solidBG) Color.TRANSPARENT else getResourceColor(R.attr.colorSecondary) + ) + currentToolbar?.setNavigationOnClickListener { + val rootSearchController = router.backstack.lastOrNull()?.controller() + if (rootSearchController is RootSearchInterface) { + rootSearchController.expandSearch() + } else onBackPressed() + } + if (oldTB != currentToolbar) { + invalidateOptionsMenu() + } + } + fun setDismissIcon(enabled: Boolean) { + binding.cardToolbar.navigationIcon = if (enabled) dismissDrawable else searchDrawable binding.toolbar.navigationIcon = if (enabled) dismissDrawable else searchDrawable } @@ -553,6 +597,7 @@ open class MainActivity : BaseActivity(), DownloadServiceLi DownloadService.removeListener(this) if (isBindingInitialized) { binding.toolbar.setNavigationOnClickListener(null) + binding.cardToolbar.setNavigationOnClickListener(null) } } @@ -608,6 +653,12 @@ open class MainActivity : BaseActivity(), DownloadServiceLi router.setRoot(controller.withFadeTransaction().tag(id.toString())) } + override fun onPrepareOptionsMenu(menu: Menu?): Boolean { + val searchItem = menu?.findItem(R.id.action_search) + searchItem?.isVisible = currentToolbar != binding.cardToolbar + return super.onPrepareOptionsMenu(menu) + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { // Initialize option to open catalogue settings. @@ -660,6 +711,9 @@ open class MainActivity : BaseActivity(), DownloadServiceLi return super.dispatchTouchEvent(ev) } + protected fun canShowFloatingToolbar(controller: Controller?) = + controller is FloatingSearchInterface + protected open fun syncActivityViewWithController( to: Controller?, from: Controller? = null, @@ -668,14 +722,18 @@ open class MainActivity : BaseActivity(), DownloadServiceLi if (from is DialogController || to is DialogController) { return } + setFloatingToolbar(canShowFloatingToolbar(to)) val onRoot = router.backstackSize == 1 if (onRoot) { window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) binding.toolbar.navigationIcon = searchDrawable + binding.cardToolbar.navigationIcon = searchDrawable } else { window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) binding.toolbar.navigationIcon = drawerArrow + binding.cardToolbar.navigationIcon = drawerArrow } + binding.cardToolbar.subtitle = null drawerArrow?.progress = 1f binding.bottomNav.visibility = if (!hideBottomNav) View.VISIBLE else binding.bottomNav.visibility @@ -803,8 +861,19 @@ interface BottomNavBarInterface { interface RootSearchInterface { fun expandSearch() { - if (this is Controller) (activity as? MainActivity)?.binding?.toolbar?.menu?.findItem(R.id.action_search) - ?.expandActionView() + if (this is Controller) { + val mainActivity = activity as? MainActivity ?: return + mainActivity.binding.cardToolbar.menu.findItem(R.id.action_search)?.expandActionView() + } + } +} + +interface FloatingSearchInterface { + fun searchTitle(title: String?): String? { + if (this is Controller) { + return activity?.getString(R.string.search_, title) + } + return title } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/OverflowDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/OverflowDialog.kt index ff6a1768b4..a0ad5025b4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/OverflowDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/OverflowDialog.kt @@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.view.updateLayoutParams import uy.kohesive.injekt.injectLazy +import kotlin.math.max class OverflowDialog(activity: MainActivity) : Dialog(activity, R.style.OverflowDialogTheme) { @@ -76,7 +77,7 @@ class OverflowDialog(activity: MainActivity) : Dialog(activity, R.style.Overflow } binding.overflowCardView.updateLayoutParams { - topMargin = activity.binding.toolbar.height - 2.dpToPx + topMargin = max(activity.binding.toolbar.height, activity.binding.cardFrame.height) - 2.dpToPx } window?.let { window -> window.navigationBarColor = Color.TRANSPARENT diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/SearchActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/SearchActivity.kt index 97892cf2f2..482e042073 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/SearchActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/SearchActivity.kt @@ -25,9 +25,9 @@ class SearchActivity : MainActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding.toolbar.navigationIcon = drawerArrow - binding.toolbar.setNavigationOnClickListener { - popToRoot() - } + binding.cardToolbar.navigationIcon = drawerArrow + binding.toolbar.setNavigationOnClickListener { popToRoot() } + binding.cardToolbar.setNavigationOnClickListener { popToRoot() } (router.backstack.lastOrNull()?.controller() as? BaseController<*>)?.setTitle() (router.backstack.lastOrNull()?.controller() as? SettingsController)?.setTitle() } @@ -63,6 +63,8 @@ class SearchActivity : MainActivity() { if (from is DialogController || to is DialogController) { return } + setFloatingToolbar(canShowFloatingToolbar(to)) + binding.cardToolbar.navigationIcon = drawerArrow binding.toolbar.navigationIcon = drawerArrow drawerArrow?.progress = 1f diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt index f7061a7cbc..00d66dd667 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt @@ -69,6 +69,7 @@ import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.ui.library.LibraryController +import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.SearchActivity import eu.kanade.tachiyomi.ui.manga.chapter.ChapterHolder @@ -445,9 +446,10 @@ class MangaDetailsController : if (router.backstackSize > 0 && router.backstack.last().controller() !is MangaDetailsController ) { - activityBinding?.appBar?.setBackgroundColor(colorSecondary) + if (router.backstack.last().controller() !is FloatingSearchInterface) { + activityBinding?.appBar?.setBackgroundColor(colorSecondary) + } activityBinding?.toolbar?.setBackgroundColor(colorSecondary) - activity?.window?.statusBarColor = activity?.getResourceColor( android.R.attr.statusBarColor ) ?: colorSecondary diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt index 9dea13befc..514483c63d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt @@ -32,6 +32,7 @@ import eu.kanade.tachiyomi.databinding.RecentsControllerBinding import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.main.BottomSheetController +import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.RootSearchInterface import eu.kanade.tachiyomi.ui.manga.MangaDetailsController @@ -74,6 +75,7 @@ class RecentsController(bundle: Bundle? = null) : FlexibleAdapter.OnItemMoveListener, FlexibleAdapter.EndlessScrollListener, RootSearchInterface, + FloatingSearchInterface, BottomSheetController, RemoveHistoryDialog.Listener { @@ -100,11 +102,16 @@ class RecentsController(bundle: Bundle? = null) : private var lastChapterId: Long? = null private var showingDownloads = false var headerHeight = 0 + private var query = "" + set(value) { + field = value + presenter.query = value + } override fun getTitle(): String? { return if (showingDownloads) { resources?.getString(R.string.download_queue) - } else resources?.getString(R.string.recents) + } else searchTitle(resources?.getString(R.string.recents)) } override fun createBinding(inflater: LayoutInflater) = RecentsControllerBinding.inflate(inflater) @@ -230,7 +237,7 @@ class RecentsController(bundle: Bundle? = null) : val oldShow = showingDownloads showingDownloads = progress > 0.92f if (oldShow != showingDownloads) { - setTitle() + updateTitleAndMenu() activity?.invalidateOptionsMenu() } } @@ -242,7 +249,7 @@ class RecentsController(bundle: Bundle? = null) : binding.downloadBottomSheet.sheetLayout.alpha = if (state == BottomSheetBehavior.STATE_COLLAPSED) 1f else 0f showingDownloads = state == BottomSheetBehavior.STATE_EXPANDED - setTitle() + updateTitleAndMenu() activity?.invalidateOptionsMenu() } @@ -299,6 +306,11 @@ class RecentsController(bundle: Bundle? = null) : requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301) } + fun updateTitleAndMenu() { + (activity as? MainActivity)?.setFloatingToolbar(!showingDownloads, true) + setTitle() + } + private fun setBottomPadding() { val bottomBar = activityBinding?.bottomNav ?: return val pad = bottomBar.translationY - bottomBar.height @@ -554,7 +566,7 @@ class RecentsController(bundle: Bundle? = null) : (activity as? MainActivity)?.setUndoSnackBar(snack) } - override fun isSearching() = presenter.query.isNotEmpty() + override fun isSearching() = query.isNotEmpty() override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { if (onRoot) (activity as? MainActivity)?.setDismissIcon(showingDownloads) @@ -566,20 +578,22 @@ class RecentsController(bundle: Bundle? = null) : val searchItem = menu.findItem(R.id.action_search) val searchView = searchItem.actionView as SearchView searchView.queryHint = view?.context?.getString(R.string.search_recents) + searchItem.collapseActionView() if (isSearching()) { searchItem.expandActionView() - searchView.setQuery(presenter.query, true) + searchView.setQuery(query, true) searchView.clearFocus() } setOnQueryTextChangeListener(searchView) { - if (presenter.query != it) { - presenter.query = it ?: return@setOnQueryTextChangeListener false + if (query != it) { + query = it ?: return@setOnQueryTextChangeListener false // loadNoMore() resetProgressItem() refresh() } true } + searchItem.fixExpandInvalidate() } } @@ -636,7 +650,12 @@ class RecentsController(bundle: Bundle? = null) : override fun onChangeEnded(handler: ControllerChangeHandler, type: ControllerChangeType) { super.onChangeEnded(handler, type) - if (type == ControllerChangeType.POP_ENTER) setBottomPadding() + if (type == ControllerChangeType.POP_ENTER) { + setBottomPadding() + } + if (type.isEnter) { + updateTitleAndMenu() + } } fun hasQueue() = presenter.downloadManager.hasQueue() @@ -659,7 +678,7 @@ class RecentsController(bundle: Bundle? = null) : if (showingDownloads) { binding.downloadBottomSheet.dlBottomSheet.dismiss() } else { - activityBinding?.toolbar?.menu?.findItem(R.id.action_search)?.expandActionView() + activityBinding?.cardToolbar?.menu?.findItem(R.id.action_search)?.expandActionView() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt index d6c22a2d55..6c593c0582 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt @@ -20,6 +20,7 @@ import com.bluelinelabs.conductor.ControllerChangeType import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.base.controller.BaseController +import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface import eu.kanade.tachiyomi.util.view.scrollViewWith import kotlinx.coroutines.MainScope import rx.Observable @@ -96,6 +97,9 @@ abstract class SettingsController : PreferenceController() { } open fun getTitle(): String? { + if (this is FloatingSearchInterface) { + return searchTitle(preferenceScreen?.title?.toString()) + } return preferenceScreen?.title?.toString() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt index 78d72c49dd..cdb4c5e9a8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt @@ -8,11 +8,12 @@ import androidx.preference.PreferenceScreen import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.RouterTransaction import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface import eu.kanade.tachiyomi.ui.setting.search.SettingsSearchController import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.view.withFadeTransaction -class SettingsMainController : SettingsController() { +class SettingsMainController : SettingsController(), FloatingSearchInterface { init { setHasOptionsMenu(true) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/search/SettingsSearchController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/search/SettingsSearchController.kt index 362b929742..1708e42072 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/search/SettingsSearchController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/search/SettingsSearchController.kt @@ -11,6 +11,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.databinding.SettingsSearchControllerBinding import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface import eu.kanade.tachiyomi.ui.setting.SettingsController import eu.kanade.tachiyomi.util.view.liftAppbarWith import eu.kanade.tachiyomi.util.view.withFadeTransaction @@ -21,6 +22,7 @@ import eu.kanade.tachiyomi.util.view.withFadeTransaction */ class SettingsSearchController : NucleusController(), + FloatingSearchInterface, SettingsSearchAdapter.OnTitleClickListener { /** @@ -107,7 +109,7 @@ class SettingsSearchController : adapter = SettingsSearchAdapter(this) - liftAppbarWith(binding.recycler) + liftAppbarWith(binding.recycler, true) // Create recycler and set adapter. binding.recycler.layoutManager = LinearLayoutManager(view.context) binding.recycler.adapter = adapter diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt index c1561c611a..0e60f31db3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt @@ -32,6 +32,7 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.extension.SettingsExtensionsController import eu.kanade.tachiyomi.ui.main.BottomSheetController +import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.RootSearchInterface import eu.kanade.tachiyomi.ui.setting.SettingsBrowseController @@ -59,8 +60,8 @@ import kotlinx.android.parcel.Parcelize import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.Date +import kotlin.math.abs import kotlin.math.max -import kotlin.math.min /** * This controller shows and manages the different catalogues enabled by the user. @@ -73,6 +74,7 @@ class BrowseController : FlexibleAdapter.OnItemClickListener, SourceAdapter.SourceListener, RootSearchInterface, + FloatingSearchInterface, BottomSheetController { /** @@ -109,7 +111,7 @@ class BrowseController : else -> R.string.source_migration } ) - } else view?.context?.getString(R.string.browse) + } else searchTitle(view?.context?.getString(R.string.sources)) } val presenter = SourcePresenter(this) @@ -142,19 +144,6 @@ class BrowseController : bottom = (activityBinding?.bottomNav?.height ?: 0) + 58.spToPx ) }, - liftOnScroll = { el -> - if (!binding.bottomSheet.root.sheetBehavior.isExpanded()) { - elevationAnim?.cancel() - elevationAnim = ValueAnimator.ofFloat( - activityBinding?.appBar?.elevation ?: 0f, - if (el) 15f else 0f - ) - elevationAnim?.addUpdateListener { valueAnimator -> - activityBinding?.appBar?.elevation = valueAnimator.animatedValue as Float - } - elevationAnim?.start() - } - }, onBottomNavUpdate = { setBottomPadding() } @@ -162,33 +151,31 @@ class BrowseController : binding.sourceRecycler.post { setBottomSheetTabs(if (binding.bottomSheet.root.sheetBehavior.isCollapsed()) 0f else 1f) - activityBinding?.appBar?.elevation = min( - if (binding.bottomSheet.root.sheetBehavior.isCollapsed()) 0f else 1f * 15f, - if (binding.sourceRecycler.canScrollVertically(-1)) 15f else 0f - ) binding.sourceRecycler.updatePaddingRelative( bottom = (activityBinding?.bottomNav?.height ?: 0) + 58.spToPx ) + val isCollapsed = binding.bottomSheet.root.sheetBehavior.isCollapsed() + binding.shadow.alpha = if (isCollapsed) 0.5f else 0f + updateTitleAndMenu() } requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301) binding.bottomSheet.root.onCreate(this) + binding.shadow.alpha = + if (binding.bottomSheet.root.sheetBehavior?.state == BottomSheetBehavior.STATE_COLLAPSED) 0.5f else 0f + binding.bottomSheet.root.sheetBehavior?.addBottomSheetCallback( object : BottomSheetBehavior .BottomSheetCallback() { override fun onSlide(bottomSheet: View, progress: Float) { binding.shadow2.alpha = (1 - max(0f, progress)) * 0.25f - activityBinding?.appBar?.elevation = min( - (1f - progress) * 15f, - if (binding.sourceRecycler.canScrollVertically(-1)) 15f else 0f - ) + binding.shadow.alpha = (1 - abs(progress)) * 0.5f activityBinding?.appBar?.y = max(activityBinding!!.appBar.y, -headerHeight * (1 - progress)) val oldShow = showingExtensions showingExtensions = progress > 0.92f if (oldShow != showingExtensions) { - setTitle() - activity?.invalidateOptionsMenu() + updateTitleAndMenu() } setBottomSheetTabs(max(0f, progress)) } @@ -209,11 +196,15 @@ class BrowseController : ) { binding.bottomSheet.root.sheetBehavior?.isDraggable = true showingExtensions = state == BottomSheetBehavior.STATE_EXPANDED - setTitle() + updateTitleAndMenu() if (state == BottomSheetBehavior.STATE_EXPANDED) { extBottomSheet.fetchOnlineExtensionsIfNeeded() } else extBottomSheet.shouldCallApi = true - activity?.invalidateOptionsMenu() + } + + if (state == BottomSheetBehavior.STATE_EXPANDED || state == BottomSheetBehavior.STATE_COLLAPSED) { + binding.shadow.alpha = + if (state == BottomSheetBehavior.STATE_COLLAPSED) 0.5f else 0f } retainViewMode = if (state == BottomSheetBehavior.STATE_EXPANDED) { @@ -238,8 +229,9 @@ class BrowseController : } fun updateTitleAndMenu() { - setTitle() + (activity as? MainActivity)?.setFloatingToolbar(!showingExtensions) activity?.invalidateOptionsMenu() + setTitle() } fun setBottomSheetTabs(progress: Float) { @@ -348,20 +340,9 @@ class BrowseController : } if (!type.isEnter) { binding.bottomSheet.root.canExpand = false - activityBinding?.appBar?.elevation = - when { - binding.bottomSheet.root.sheetBehavior.isExpanded() -> 0f - binding.sourceRecycler.canScrollVertically(-1) -> 15f - else -> 0f - } } else { binding.bottomSheet.root.presenter.refreshMigrations() - activityBinding?.appBar?.elevation = - when { - binding.bottomSheet.root.sheetBehavior.isExpanded() -> 0f - binding.sourceRecycler.canScrollVertically(-1) -> 15f - else -> 0f - } + updateTitleAndMenu() } setBottomPadding() } @@ -458,7 +439,7 @@ class BrowseController : override fun expandSearch() { if (showingExtensions) binding.bottomSheet.root.sheetBehavior?.collapse() - else activityBinding?.toolbar?.menu?.findItem(R.id.action_search)?.expandActionView() + else activityBinding?.cardToolbar?.menu?.findItem(R.id.action_search)?.expandActionView() } /** @@ -480,13 +461,19 @@ class BrowseController : // Change hint to show global search. searchView.queryHint = view?.context?.getString(R.string.search_extensions) - + searchItem.collapseActionView() + if (extQuery.isNotEmpty()) { + searchItem.expandActionView() + searchView.setQuery(extQuery, true) + searchView.clearFocus() + } // Create query listener which opens the global search view. setOnQueryTextChangeListener(searchView) { extQuery = it ?: "" binding.bottomSheet.root.drawExtensions() true } + searchItem.fixExpandInvalidate() } else { inflater.inflate(R.menu.migration_main, menu) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt index 5d43242746..ca17c12137 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt @@ -25,6 +25,7 @@ import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.ui.source.BrowseController @@ -57,6 +58,7 @@ open class BrowseSourceController(bundle: Bundle) : NucleusController(bundle), FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener, + FloatingSearchInterface, FlexibleAdapter.EndlessScrollListener { constructor( @@ -118,7 +120,7 @@ open class BrowseSourceController(bundle: Bundle) : } override fun getTitle(): String? { - return presenter.source.name + return searchTitle(presenter.source.name) } override fun createPresenter(): BrowseSourcePresenter { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/global_search/GlobalSearchController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/global_search/GlobalSearchController.kt index 7297a95cfd..4eae8b19b3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/global_search/GlobalSearchController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/global_search/GlobalSearchController.kt @@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.databinding.SourceGlobalSearchControllerBinding import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.util.addOrRemoveToFavorites @@ -34,6 +35,7 @@ open class GlobalSearchController( protected val initialQuery: String? = null, protected val extensionFilter: String? = null ) : NucleusController(), + FloatingSearchInterface, GlobalSearchCardAdapter.OnMangaClickListener { /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/ControllerExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/ControllerExtensions.kt index dd41a06f38..0e7c81a9a8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/view/ControllerExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/ControllerExtensions.kt @@ -26,7 +26,9 @@ import com.bluelinelabs.conductor.changehandler.FadeChangeHandler import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.databinding.MainActivityBinding +import eu.kanade.tachiyomi.ui.base.MaterialFastScroll import eu.kanade.tachiyomi.ui.main.BottomSheetController +import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.util.system.dpToPx @@ -67,15 +69,60 @@ fun Controller.setOnQueryTextChangeListener( ) } -fun Controller.liftAppbarWith(recycler: RecyclerView) { - view?.applyWindowInsetsForController() - recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener) +fun Controller.removeQueryListener() { + val searchView = activityBinding?.cardToolbar?.menu?.findItem(R.id.action_search)?.actionView as? SearchView + val searchView2 = activityBinding?.toolbar?.menu?.findItem(R.id.action_search)?.actionView as? SearchView + searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String?) = true + override fun onQueryTextChange(newText: String?) = true + }) + searchView2?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String?) = true + override fun onQueryTextChange(newText: String?) = true + }) +} + +fun Controller.liftAppbarWith(recycler: RecyclerView, padView: Boolean = false) { + if (padView) { + val attrsArray = intArrayOf(R.attr.actionBarSize) + val array = recycler.context.obtainStyledAttributes(attrsArray) + var appBarHeight = ( + if (activityBinding?.toolbar?.height ?: 0 > 0) activityBinding!!.toolbar.height + else array.getDimensionPixelSize(0, 0) + ) + array.recycle() + activityBinding!!.toolbar.post { + if (activityBinding!!.toolbar.height > 0) { + appBarHeight = activityBinding!!.toolbar.height + recycler.requestApplyInsets() + } + } + recycler.updatePaddingRelative( + top = activityBinding!!.toolbar.y.toInt() + appBarHeight + ) + recycler.doOnApplyWindowInsets { view, insets, _ -> + val headerHeight = insets.systemWindowInsetTop + appBarHeight + view.updatePaddingRelative( + top = headerHeight, + bottom = insets.systemWindowInsetBottom + ) + } + } else { + view?.applyWindowInsetsForController() + recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener) + } var elevationAnim: ValueAnimator? = null var elevate = false - val elevateFunc: (Boolean) -> Unit = { el -> + val elevateFunc: (Boolean) -> Unit = f@{ el -> elevate = el elevationAnim?.cancel() + val floatingBar = + !(activityBinding?.toolbar?.isVisible == true || activityBinding?.tabsFrameLayout?.isVisible == true) + if (floatingBar) { + activityBinding?.appBar?.elevation = 0f + return@f + } elevationAnim = ValueAnimator.ofFloat( activityBinding?.appBar?.elevation ?: 0f, if (el) 15f else 0f @@ -85,6 +132,12 @@ fun Controller.liftAppbarWith(recycler: RecyclerView) { } elevationAnim?.start() } + + val floatingBar = + !(activityBinding?.toolbar?.isVisible == true || activityBinding?.tabsFrameLayout?.isVisible == true) + if (floatingBar) { + activityBinding?.appBar?.elevation = 0f + } elevateFunc(recycler.canScrollVertically(-1)) recycler.addOnScrollListener( object : RecyclerView.OnScrollListener() { @@ -163,12 +216,21 @@ fun Controller.scrollViewWith( var elevate = false var isInView = true val preferences: PreferencesHelper by injectLazy() - val elevateFunc: (Boolean) -> Unit = { el -> + val elevateFunc: (Boolean) -> Unit = f@{ el -> elevate = el if (liftOnScroll != null) { liftOnScroll.invoke(el) } else { elevationAnim?.cancel() + val floatingBar = + !( + activityBinding?.toolbar?.isVisible == true || + (activityBinding?.tabsFrameLayout?.isVisible == true && includeTabView) + ) + if (floatingBar) { + activityBinding?.appBar?.elevation = 0f + return@f + } elevationAnim = ValueAnimator.ofFloat( activityBinding?.appBar?.elevation ?: 0f, if (el) 15f else 0f @@ -179,6 +241,14 @@ fun Controller.scrollViewWith( elevationAnim?.start() } } + val floatingBar = + !( + activityBinding?.toolbar?.isVisible == true || + (activityBinding?.tabsFrameLayout?.isVisible == true && includeTabView) + ) + if (floatingBar) { + activityBinding?.appBar?.elevation = 0f + } addLifecycleListener( object : Controller.LifecycleListener() { override fun onChangeStart( @@ -211,8 +281,10 @@ fun Controller.scrollViewWith( } } else { if (!customPadding && lastY == 0f && ( - router.backstack.lastOrNull() - ?.controller() is MangaDetailsController || includeTabView + ( + this@scrollViewWith !is FloatingSearchInterface && router.backstack.lastOrNull() + ?.controller() is MangaDetailsController + ) || includeTabView ) ) { val parent = recycler.parent as? ViewGroup ?: return diff --git a/app/src/main/res/layout/library_controller.xml b/app/src/main/res/layout/library_controller.xml index 5b2d8d27fd..46b08fe304 100644 --- a/app/src/main/res/layout/library_controller.xml +++ b/app/src/main/res/layout/library_controller.xml @@ -62,27 +62,37 @@ android:layout_height="match_parent" app:fastScrollerBubbleEnabled="true" /> - + android:layout_gravity="center|top"> + + + diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml index a141753850..81fda3c012 100644 --- a/app/src/main/res/layout/main_activity.xml +++ b/app/src/main/res/layout/main_activity.xml @@ -38,22 +38,106 @@ android:layout_height="?attr/actionBarSize" android:background="?attr/colorSecondary"> - + + + + + + + + + + + + + + + + + + + + + + app:actionViewClass="eu.kanade.tachiyomi.ui.base.MiniSearchView"/> + app:actionViewClass="eu.kanade.tachiyomi.ui.base.MiniSearchView"/> diff --git a/app/src/main/res/menu/library.xml b/app/src/main/res/menu/library.xml index 0649b82d11..44ba1213bc 100644 --- a/app/src/main/res/menu/library.xml +++ b/app/src/main/res/menu/library.xml @@ -9,7 +9,7 @@ android:icon="@drawable/ic_search_24dp" android:title="@string/search" android:visible="false" - app:actionViewClass="androidx.appcompat.widget.SearchView" + app:actionViewClass="eu.kanade.tachiyomi.ui.base.MiniSearchView" app:showAsAction="collapseActionView|ifRoom" /> diff --git a/app/src/main/res/menu/recents.xml b/app/src/main/res/menu/recents.xml index 9f85104c84..f789b01e1c 100644 --- a/app/src/main/res/menu/recents.xml +++ b/app/src/main/res/menu/recents.xml @@ -7,7 +7,7 @@ android:icon="@drawable/ic_search_24dp" android:title="@string/search" android:visible="false" - app:actionViewClass="androidx.appcompat.widget.SearchView" + app:actionViewClass="eu.kanade.tachiyomi.ui.base.MiniSearchView" app:showAsAction="ifRoom|collapseActionView" /> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 76fe6ae3f7..666fa655b5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -902,6 +902,7 @@ Rotation Save Search + Search %1$s Select all Select none Selection