mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
Expanded toolbar (#1197)
* Most of the work for big toolbar * minor fixes to library * Using accurate offset for library * nav icon fixes * support tabs scrolling properly * fix toolbar snap * Add big icon next to large title * Update MainActivity.kt * Update browse sheet toolbar to be its own * Update recent sheet toolbar to be its own * fix pop from manga details * cleanup of BigAppBarLayout * Use LinearLayoutManagerAccurateOffset for recents * Updates to searchview * fix recents scrolling up on device config change * Move search view up to top on tap * Fix to recent and library searches * Fix multi line big titles * Fix config change resetting recycler scroll * add end padding to big title * More fixes * Fixes to non scrollable appbar controllers * Move swipe refresh circle with view * Fix manga details toolbar * Updates to recents and browse source when config changes * Fixes to global search * Fixes to popping from manga details * clear search when switching controllers + fixes to global and source search * Update search.xml * update backgroundColor * fixes to big appbar background on push * use canShowFloatingToolbar where possible * fix collapse/hide of dl bottom sheet * persist search on config change * Fix library menu on config change * Fix recents toolbar placement while searching * adjust calc of grid layout manager offset * Fixes to range calculation in library * More accurate scrollbar in manga details * use item animator for moving appbar * alpha on main tb * accurate offset for settings * updates to search activity * Fixes to back button and up button * Option to go back to smaller toolbar * Optimization to fast computing offset * moving scrollbar in library as app bar moves * fixes to search title in recents * fix popping into browse after 2 line big titles * Updates to landscape and tablets Tablets now always have a sticky header, just shrinks down Phones in landscape will have a smaller big title * fixes to main toolbar text alpha * Fixes to device config changes * remove updateOnRefresh preference big toolbar now, you can just reach the first refresh button * Fix category hopper * Fix recents in small toolbar mode * clean up and fixes * Fixes to back press + search activity * Fixes to going in and back out of manga details * Optimizations to grid layout calculations * adjustments to library fast scroller margins * Fixes to download queue overflow menu * Fixes to library search when popping * Fixes to d/l sheet on launch * expanding library search * remove includeTabView as parameter * remove sheetIsFullscreen * fixes to bigToolbarHeight * add onDetachedFromWindow to LinearLayoutManagerAccurateOffset * add scrollUpAnyway to moveRecyclerViewUp * snap swipe refresh circle with appbar * Fixes to extension searching * accurate offset use in ExtensionDetailsController * alpha big view adjustments * Fixes to MigrationController * Dont change toolbar menus while scrolling down * fix disappearing appbar in some cases * Cleanup GlobalSearchController searchItem * Cleanup SettingsMainController searchItem * Update LibraryController.kt * change onRoot to isControllerVisible and add to controller extensions * lock appbar y when switching tabs * Dont use large toolbar on extremely small devices Basically devices in landscape that cant display nav rails * Fixes to progress view and empty view in landscape new layout for empty view for short devices * move manager offsets to new files * fix for ungrouped library mode * clean up search items in BrowseSourceController * dont show incog icon on main toolbar when search bar is showing * fix browse source having the grid be offset when popping * Fixes to estimated heights of viewholders * rename large toolbar setting to expanded also update setting copy to mention small devices will never show it * BigAppBarLayout -> ExpandedAppBarLayout * save a var for computedRange to ease the calls a bit * fix crash * Cleanup BaseToolbar * Refactoring ExpandedAppBarLayout * misc cleanup * Rename ToolbarStates * refactor toolbar height variable names * More refactoring to MainActivity * helper method for setTextColorAlpha * Refactor MangaDetailsController * Refactor Recents * Refactoring settings controllers * Cleanup Browse controller * Remove Float.spToPx its not used * Clean up ControllerExtensions * clean up import in BrowseController * move itemanimator to a different method same change was already made in staggered branch * Fixes to browse source toolbar not being expanded * cleanup ControllerExtensions.setAppBarBG * Mainactivity cleanup * Optimizations + fixes to setting menu items in search toolbar * Fixes to switching between grid/list in browse source with correct offsetting and all * Fix findFirstVisibleItemPosition for offset layoutmanagers * remove unneeded invalidateOptionsMenu calls * More fixes to search toolbar menu in browse source * filter sources controller now uses search toolbar too when using expanded mode sticks back to main toolbar when collapsed (ie like older version of j2k) * Fix compact -> expanded setting change when starting app in compact mode * More fixes to BrowseSourceController * Fixes to LibraryController search menu * Filter sources search title is now just "search" because not gonna add a new string for this * Fix statusbar color when popping back to ext sheet * Make adding submenu items recursive * Fix appbar locking up in recents * Fix download/ext sheet in landscape 3 nav devices * Fixing to snapping on compact toolbars * Fix search toolbar menu flashing so much on change * Fix animation time of snapping compact appbar * Fixes to toolbar for tablets * Cleanup * better handling of menu item in search toolbar * minimize use of obtainStyledAttributes of mainActionBarSize
This commit is contained in:
parent
f372a4b81f
commit
92b98cef5c
53 changed files with 2333 additions and 750 deletions
|
@ -205,8 +205,6 @@ object PreferenceKeys {
|
|||
|
||||
const val refreshCoversToo = "refresh_covers_too"
|
||||
|
||||
const val updateOnRefresh = "update_on_refresh"
|
||||
|
||||
const val showDLsInRecents = "show_dls_in_recents"
|
||||
const val showRemHistoryInRecents = "show_rem_history_in_recents"
|
||||
const val showReadInAllRecents = "show_read_in_all_recents"
|
||||
|
@ -237,6 +235,8 @@ object PreferenceKeys {
|
|||
|
||||
const val themeMangaDetails = "theme_manga_details"
|
||||
|
||||
const val useLargeToolbar = "use_large_toolbar"
|
||||
|
||||
const val incognitoMode = "incognito_mode"
|
||||
|
||||
const val sideNavMode = "side_nav_mode"
|
||||
|
|
|
@ -337,8 +337,6 @@ class PreferencesHelper(val context: Context) {
|
|||
|
||||
fun refreshCoversToo() = flowPrefs.getBoolean(Keys.refreshCoversToo, true)
|
||||
|
||||
fun updateOnRefresh() = flowPrefs.getInt(Keys.updateOnRefresh, -1)
|
||||
|
||||
fun extensionUpdatesCount() = flowPrefs.getInt("ext_updates_count", 0)
|
||||
|
||||
fun recentsViewType() = flowPrefs.getInt("recents_view_type", 0)
|
||||
|
@ -410,6 +408,8 @@ class PreferencesHelper(val context: Context) {
|
|||
|
||||
fun themeMangaDetails() = prefs.getBoolean(Keys.themeMangaDetails, true)
|
||||
|
||||
fun useLargeToolbar() = prefs.getBoolean(Keys.useLargeToolbar, true)
|
||||
|
||||
fun dohProvider() = prefs.getInt(Keys.dohProvider, -1)
|
||||
|
||||
fun showSeriesInShortcuts() = prefs.getBoolean(Keys.showSeriesInShortcuts, true)
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.core.view.isVisible
|
|||
import com.bluelinelabs.conductor.Router
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface
|
||||
import eu.kanade.tachiyomi.ui.main.SearchActivity
|
||||
|
||||
open class BaseToolbar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
|
@ -17,6 +18,10 @@ open class BaseToolbar @JvmOverloads constructor(context: Context, attrs: Attrib
|
|||
val onRoot: Boolean
|
||||
get() = router?.backstackSize ?: 1 <= 1 && context !is SearchActivity
|
||||
|
||||
val canShowIncogOnMain: Boolean
|
||||
get() = router?.backstack?.lastOrNull()?.controller !is FloatingSearchInterface ||
|
||||
this !is CenteredToolbar
|
||||
|
||||
lateinit var toolbarTitle: TextView
|
||||
protected set
|
||||
private val defStyleRes = com.google.android.material.R.style.Widget_Material3_Toolbar
|
||||
|
@ -72,7 +77,7 @@ open class BaseToolbar @JvmOverloads constructor(context: Context, attrs: Attrib
|
|||
@DrawableRes
|
||||
private fun getIncogRes(): Int {
|
||||
return when {
|
||||
incognito -> R.drawable.ic_incognito_circle_24dp
|
||||
incognito && canShowIncogOnMain -> R.drawable.ic_incognito_circle_24dp
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +85,7 @@ open class BaseToolbar @JvmOverloads constructor(context: Context, attrs: Attrib
|
|||
@DrawableRes
|
||||
private fun getDropdownRes(): Int {
|
||||
return when {
|
||||
incognito && onRoot -> R.drawable.ic_blank_28dp
|
||||
incognito && onRoot && canShowIncogOnMain -> R.drawable.ic_blank_28dp
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,375 @@
|
|||
package eu.kanade.tachiyomi.ui.base
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewPropertyAnimator
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.math.MathUtils
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.marginTop
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.isTablet
|
||||
import eu.kanade.tachiyomi.util.view.backgroundColor
|
||||
import eu.kanade.tachiyomi.util.view.setTextColorAlpha
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class ExpandedAppBarLayout@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
AppBarLayout(context, attrs) {
|
||||
|
||||
var cardToolbar: FloatingToolbar? = null
|
||||
var cardFrame: FrameLayout? = null
|
||||
var mainToolbar: CenteredToolbar? = null
|
||||
var bigTitleView: TextView? = null
|
||||
val preferences: PreferencesHelper by injectLazy()
|
||||
var bigView: View? = null
|
||||
var imageView: ImageView? = null
|
||||
private var tabsFrameLayout: FrameLayout? = null
|
||||
var mainActivity: MainActivity? = null
|
||||
private var isExtraSmall = false
|
||||
val useLargeToolbar: Boolean
|
||||
get() = preferences.useLargeToolbar() && !isExtraSmall
|
||||
|
||||
/** Defines how the toolbar layout should be */
|
||||
private var toolbarMode = ToolbarState.EXPANDED
|
||||
set(value) {
|
||||
field = value
|
||||
if (value == ToolbarState.SEARCH_ONLY) {
|
||||
mainToolbar?.isGone = true
|
||||
} else if (value == ToolbarState.COMPACT) {
|
||||
mainToolbar?.alpha = 1f
|
||||
mainToolbar?.isVisible = true
|
||||
}
|
||||
if (value != ToolbarState.EXPANDED) {
|
||||
mainToolbar?.translationY = 0f
|
||||
y = 0f
|
||||
}
|
||||
}
|
||||
var useTabsInPreLayout = false
|
||||
var yAnimator: ViewPropertyAnimator? = null
|
||||
|
||||
/**
|
||||
* used to ignore updates to y
|
||||
*
|
||||
* use only on controller.onViewCreated that asynchronously loads the first set of items
|
||||
* and make false once the recycler has items
|
||||
*/
|
||||
var lockYPos = false
|
||||
|
||||
/** A value used to determine the offset needed for a recycler to land just under the smaller toolbar */
|
||||
val toolbarDistanceToTop: Int
|
||||
get() {
|
||||
val tabHeight = if (tabsFrameLayout?.isVisible == true) 48.dpToPx else 0
|
||||
return paddingTop - (mainToolbar?.height ?: 0) - tabHeight
|
||||
}
|
||||
|
||||
/** A value used to determine the offset needed for a appbar's y to show only the smaller toolbar */
|
||||
val yNeededForSmallToolbar: Int
|
||||
get() {
|
||||
val tabHeight = if (tabsFrameLayout?.isVisible == true) 48.dpToPx else 0
|
||||
return -preLayoutHeight + (mainToolbar?.height ?: 0) + tabHeight
|
||||
}
|
||||
|
||||
val attrToolbarHeight: Int = let {
|
||||
val attrsArray = intArrayOf(R.attr.mainActionBarSize)
|
||||
val array = it.context.obtainStyledAttributes(attrsArray)
|
||||
val height = array.getDimensionPixelSize(0, 0)
|
||||
array.recycle()
|
||||
height
|
||||
}
|
||||
|
||||
val preLayoutHeight: Int
|
||||
get() = getEstimatedLayout(
|
||||
cardFrame?.isVisible == true && toolbarMode == ToolbarState.EXPANDED,
|
||||
useTabsInPreLayout,
|
||||
toolbarMode == ToolbarState.EXPANDED
|
||||
)
|
||||
|
||||
/** Small toolbar height + top system insets, same size as a collapsed appbar */
|
||||
private val compactAppBarHeight: Float
|
||||
get() {
|
||||
val appBarHeight = if (mainToolbar?.height ?: 0 > 0) {
|
||||
mainToolbar?.height ?: 0
|
||||
} else {
|
||||
attrToolbarHeight
|
||||
}
|
||||
return (appBarHeight + paddingTop).toFloat()
|
||||
}
|
||||
|
||||
/** Used to restrain how far up the app bar can go up. Tablets stop at the smaller toolbar */
|
||||
private val minTabletHeight: Int
|
||||
get() {
|
||||
val tabHeight = if (tabsFrameLayout?.isVisible == true) 48.dpToPx else 0
|
||||
return if (context.isTablet()) {
|
||||
(mainToolbar?.height ?: 0) + paddingTop + tabHeight
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
enum class ToolbarState {
|
||||
EXPANDED,
|
||||
COMPACT,
|
||||
SEARCH_ONLY,
|
||||
}
|
||||
|
||||
fun setToolbarModeBy(controller: Controller?, useSmall: Boolean? = null) {
|
||||
toolbarMode = if (useSmall ?: !useLargeToolbar) {
|
||||
when {
|
||||
controller is FloatingSearchInterface && controller.showFloatingBar() -> {
|
||||
ToolbarState.SEARCH_ONLY
|
||||
}
|
||||
else -> ToolbarState.COMPACT
|
||||
}
|
||||
} else {
|
||||
when (controller) {
|
||||
is SmallToolbarInterface -> {
|
||||
if (controller is FloatingSearchInterface && controller.showFloatingBar()) {
|
||||
ToolbarState.SEARCH_ONLY
|
||||
} else {
|
||||
ToolbarState.COMPACT
|
||||
}
|
||||
}
|
||||
else -> ToolbarState.EXPANDED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun hideBigView(useSmall: Boolean, force: Boolean? = null, setTitleAlpha: Boolean = true) {
|
||||
val useSmallAnyway = force ?: (useSmall || !useLargeToolbar)
|
||||
bigView?.isGone = useSmallAnyway
|
||||
if (useSmallAnyway) {
|
||||
mainToolbar?.backgroundColor = null
|
||||
if (!setTitleAlpha) return
|
||||
mainToolbar?.toolbarTitle?.setTextColorAlpha(255)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFinishInflate() {
|
||||
super.onFinishInflate()
|
||||
bigTitleView = findViewById(R.id.big_title)
|
||||
cardToolbar = findViewById(R.id.card_toolbar)
|
||||
mainToolbar = findViewById(R.id.toolbar)
|
||||
bigView = findViewById(R.id.big_toolbar)
|
||||
cardFrame = findViewById(R.id.card_frame)
|
||||
tabsFrameLayout = findViewById(R.id.tabs_frame_layout)
|
||||
imageView = findViewById(R.id.big_icon)
|
||||
shrinkAppBarIfNeeded(resources.configuration)
|
||||
}
|
||||
|
||||
fun setTitle(title: CharSequence?, setBigTitle: Boolean) {
|
||||
if (setBigTitle) {
|
||||
bigTitleView?.text = title
|
||||
}
|
||||
mainToolbar?.title = title
|
||||
}
|
||||
|
||||
override fun setTranslationY(translationY: Float) {
|
||||
if (lockYPos) return
|
||||
val realHeight = (preLayoutHeight + paddingTop).toFloat()
|
||||
val newY = MathUtils.clamp(translationY, -realHeight + minTabletHeight, 0f)
|
||||
super.setTranslationY(newY)
|
||||
}
|
||||
|
||||
fun getEstimatedLayout(includeSearchToolbar: Boolean, includeTabs: Boolean, includeLargeToolbar: Boolean): Int {
|
||||
val hasLargeToolbar = includeLargeToolbar && useLargeToolbar
|
||||
val appBarHeight = attrToolbarHeight * (if (includeSearchToolbar && hasLargeToolbar) 2 else 1)
|
||||
val widthMeasureSpec = MeasureSpec.makeMeasureSpec(resources.displayMetrics.widthPixels, MeasureSpec.AT_MOST)
|
||||
val heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
|
||||
bigTitleView?.measure(widthMeasureSpec, heightMeasureSpec)
|
||||
val textHeight = max(bigTitleView?.height ?: 0, bigTitleView?.measuredHeight ?: 0) +
|
||||
(bigTitleView?.marginTop?.plus(bigView?.paddingBottom ?: 0) ?: 0)
|
||||
return appBarHeight + (if (hasLargeToolbar) textHeight else 0) +
|
||||
if (includeTabs) 48.dpToPx else 0
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration?) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
shrinkAppBarIfNeeded(newConfig)
|
||||
}
|
||||
|
||||
private fun shrinkAppBarIfNeeded(config: Configuration?) {
|
||||
config ?: return
|
||||
isExtraSmall = false
|
||||
if (config.screenHeightDp < 600) {
|
||||
val bigTitleView = bigTitleView ?: return
|
||||
isExtraSmall = config.screenWidthDp < 720
|
||||
if (isExtraSmall) {
|
||||
setToolbarModeBy(null, true)
|
||||
return
|
||||
}
|
||||
val attrs = intArrayOf(R.attr.textAppearanceHeadlineMedium)
|
||||
val ta = context.obtainStyledAttributes(attrs)
|
||||
val resId = ta.getResourceId(0, 0)
|
||||
ta.recycle()
|
||||
TextViewCompat.setTextAppearance(bigTitleView, resId)
|
||||
bigTitleView.setTextColor(context.getResourceColor(R.attr.actionBarTintColor))
|
||||
bigTitleView.updateLayoutParams<MarginLayoutParams> {
|
||||
topMargin = 12.dpToPx
|
||||
}
|
||||
imageView?.updateLayoutParams<MarginLayoutParams> {
|
||||
height = 48.dpToPx
|
||||
width = 48.dpToPx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateAppBarAfterY(recyclerView: RecyclerView?, cancelAnim: Boolean = true) {
|
||||
if (cancelAnim) {
|
||||
yAnimator?.cancel()
|
||||
}
|
||||
if (lockYPos) return
|
||||
val offset = recyclerView?.computeVerticalScrollOffset() ?: 0
|
||||
val bigHeight = bigView?.height ?: 0
|
||||
val realHeight = preLayoutHeight + paddingTop
|
||||
val tabHeight = if (tabsFrameLayout?.isVisible == true) 48.dpToPx else 0
|
||||
val shortH = if (toolbarMode != ToolbarState.EXPANDED) 0f else compactAppBarHeight
|
||||
val smallHeight = -realHeight + shortH + tabHeight
|
||||
val newY = when {
|
||||
toolbarMode != ToolbarState.EXPANDED -> {
|
||||
translationY
|
||||
}
|
||||
offset < realHeight - shortH - tabHeight -> {
|
||||
-offset.toFloat()
|
||||
}
|
||||
else -> {
|
||||
MathUtils.clamp(
|
||||
translationY,
|
||||
-realHeight.toFloat() + top + minTabletHeight,
|
||||
max(
|
||||
smallHeight,
|
||||
if (offset > realHeight - shortH - tabHeight) smallHeight else min(
|
||||
-offset.toFloat(),
|
||||
0f
|
||||
)
|
||||
) + top.toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
translationY = newY
|
||||
mainToolbar?.let { mainToolbar ->
|
||||
mainToolbar.translationY = when {
|
||||
toolbarMode != ToolbarState.EXPANDED -> 0f
|
||||
-newY <= bigHeight -> max(-newY, 0f)
|
||||
else -> bigHeight.toFloat()
|
||||
}
|
||||
}
|
||||
if (toolbarMode != ToolbarState.EXPANDED) {
|
||||
useSearchToolbarForMenu(offset > realHeight - shortH - tabHeight)
|
||||
return
|
||||
}
|
||||
val alpha =
|
||||
(bigHeight + newY * 2) / (bigHeight) + 0.45f // (realHeight.toFloat() + newY * 5) / realHeight.toFloat() + .33f
|
||||
bigView?.alpha = MathUtils.clamp(if (alpha.isNaN()) 1f else alpha, 0f, 1f)
|
||||
val toolbarTextView = mainToolbar?.toolbarTitle ?: return
|
||||
toolbarTextView.setTextColorAlpha(
|
||||
(
|
||||
MathUtils.clamp(
|
||||
(1 - ((if (alpha.isNaN()) 1f else alpha) + 0.95f)) * 2, 0f, 1f
|
||||
) * 255
|
||||
).roundToInt()
|
||||
)
|
||||
val mainToolbar = mainToolbar ?: return
|
||||
mainToolbar.alpha = MathUtils.clamp(
|
||||
(mainToolbar.bottom + mainToolbar.translationY + y - paddingTop) / mainToolbar.height,
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
val mainActivity = mainActivity ?: return
|
||||
val useSearchToolbar = mainToolbar.alpha <= 0.025f
|
||||
val idle = RecyclerView.SCROLL_STATE_IDLE
|
||||
if (if (useSearchToolbar) -y >= height || recyclerView?.scrollState ?: idle <= idle || context.isTablet()
|
||||
else mainActivity.currentToolbar == cardToolbar
|
||||
) {
|
||||
useSearchToolbarForMenu(useSearchToolbar)
|
||||
}
|
||||
}
|
||||
|
||||
fun snapAppBarY(recyclerView: RecyclerView, callback: (() -> Unit)?): Float {
|
||||
yAnimator?.cancel()
|
||||
val halfWay = compactAppBarHeight / 2
|
||||
val shortAnimationDuration = resources?.getInteger(
|
||||
if (toolbarMode != ToolbarState.EXPANDED) android.R.integer.config_shortAnimTime
|
||||
else android.R.integer.config_longAnimTime
|
||||
) ?: 0
|
||||
val realHeight = preLayoutHeight + paddingTop
|
||||
val closerToTop = abs(y) > realHeight - halfWay
|
||||
val atTop = !recyclerView.canScrollVertically(-1)
|
||||
val shortH = if (toolbarMode != ToolbarState.EXPANDED) 0f else compactAppBarHeight
|
||||
val lastY = if (closerToTop && !atTop) {
|
||||
-height.toFloat()
|
||||
} else {
|
||||
shortH
|
||||
}
|
||||
|
||||
val onFirstItem = recyclerView.computeVerticalScrollOffset() < realHeight - shortH
|
||||
|
||||
return if (!onFirstItem) {
|
||||
yAnimator = animate().y(lastY)
|
||||
.setDuration(shortAnimationDuration.toLong())
|
||||
yAnimator?.setUpdateListener {
|
||||
updateAppBarAfterY(recyclerView, false)
|
||||
callback?.invoke()
|
||||
}
|
||||
yAnimator?.start()
|
||||
useSearchToolbarForMenu(true)
|
||||
lastY
|
||||
} else {
|
||||
useSearchToolbarForMenu(mainToolbar?.alpha ?: 0f <= 0f)
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
fun useSearchToolbarForMenu(showCardTB: Boolean) {
|
||||
val mainActivity = mainActivity ?: return
|
||||
if (lockYPos) return
|
||||
if ((showCardTB || toolbarMode == ToolbarState.SEARCH_ONLY) && cardFrame?.isVisible == true) {
|
||||
if (mainActivity.currentToolbar != cardToolbar) {
|
||||
mainActivity.setFloatingToolbar(true, showSearchAnyway = true)
|
||||
} else {
|
||||
mainActivity.setSearchTBMenuIfInvalid()
|
||||
}
|
||||
if (mainActivity.currentToolbar == cardToolbar) {
|
||||
if (toolbarMode == ToolbarState.EXPANDED) {
|
||||
mainToolbar?.isInvisible = true
|
||||
}
|
||||
mainToolbar?.backgroundColor = null
|
||||
cardFrame?.backgroundColor = null
|
||||
}
|
||||
} else {
|
||||
if (mainActivity.currentToolbar != mainToolbar) {
|
||||
mainActivity.setFloatingToolbar(false, showSearchAnyway = true)
|
||||
}
|
||||
if (toolbarMode == ToolbarState.EXPANDED) {
|
||||
mainToolbar?.isInvisible = false
|
||||
}
|
||||
if (tabsFrameLayout?.isVisible == false) {
|
||||
cardFrame?.backgroundColor = mainActivity.getResourceColor(R.attr.colorSurface)
|
||||
} else {
|
||||
cardFrame?.backgroundColor = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface SmallToolbarInterface
|
|
@ -3,9 +3,11 @@ package eu.kanade.tachiyomi.ui.base
|
|||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.MenuItem
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
|
@ -25,6 +27,40 @@ class FloatingToolbar @JvmOverloads constructor(context: Context, attrs: Attribu
|
|||
private val defStyleRes = com.google.android.material.R.style.Widget_Material3_Toolbar
|
||||
private val subtitleTextAppearance: Int
|
||||
|
||||
val isSearchExpanded: Boolean
|
||||
get() {
|
||||
return searchItem?.isActionViewExpanded == true
|
||||
}
|
||||
|
||||
var searchQueryHint: CharSequence?
|
||||
get() {
|
||||
val searchView = searchItem?.actionView as? SearchView ?: return null
|
||||
return searchView.queryHint
|
||||
}
|
||||
set(value) {
|
||||
setQueryHint(value)
|
||||
}
|
||||
|
||||
fun setQueryHint(query: CharSequence?, collapseSearch: Boolean = true) {
|
||||
val searchView = searchItem?.actionView as? SearchView ?: return
|
||||
val oldV = searchView.queryHint
|
||||
searchView.queryHint = query
|
||||
if (oldV != query && collapseSearch) {
|
||||
searchView.setQuery("", false)
|
||||
searchItem?.collapseActionView()
|
||||
}
|
||||
}
|
||||
|
||||
val searchView: SearchView?
|
||||
get() {
|
||||
return searchItem?.actionView as? SearchView
|
||||
}
|
||||
|
||||
val searchItem: MenuItem?
|
||||
get() {
|
||||
return menu.findItem(R.id.action_search)
|
||||
}
|
||||
|
||||
init {
|
||||
val a = context.obtainStyledAttributes(
|
||||
attrs,
|
||||
|
@ -52,6 +88,7 @@ class FloatingToolbar @JvmOverloads constructor(context: Context, attrs: Attribu
|
|||
collapseIcon = context.contextCompatDrawable(R.drawable.ic_arrow_back_24dp)?.apply {
|
||||
setTint(actionColorAlpha)
|
||||
}
|
||||
inflateMenu(R.menu.search)
|
||||
}
|
||||
|
||||
override fun setSubtitle(resId: Int) {
|
||||
|
|
|
@ -32,6 +32,9 @@ class MaterialFastScroll @JvmOverloads constructor(context: Context, attrs: Attr
|
|||
updateScrollListener()
|
||||
}
|
||||
|
||||
val isFastScrolling: Boolean
|
||||
get() = handle.isSelected
|
||||
|
||||
// Overriding to force a distance moved before scrolling
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
if (recyclerView.computeVerticalScrollRange() <= recyclerView.computeVerticalScrollExtent()) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package eu.kanade.tachiyomi.ui.base.controller
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
|
@ -9,11 +10,15 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.isControllerVisible
|
||||
import eu.kanade.tachiyomi.util.view.removeQueryListener
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.MainScope
|
||||
|
@ -75,13 +80,21 @@ abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) :
|
|||
super.onChangeStarted(handler, type)
|
||||
}
|
||||
|
||||
val onRoot: Boolean
|
||||
get() = router.backstack.lastOrNull()?.controller == this
|
||||
|
||||
open fun getTitle(): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
open fun getSearchTitle(): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
open fun getBigIcon(): Drawable? {
|
||||
return null
|
||||
}
|
||||
|
||||
open val mainRecycler: RecyclerView?
|
||||
get() = null
|
||||
|
||||
override fun onActivityPaused(activity: Activity) {
|
||||
super.onActivityPaused(activity)
|
||||
removeQueryListener()
|
||||
|
@ -96,8 +109,16 @@ abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) :
|
|||
parentController = parentController.parentController
|
||||
}
|
||||
|
||||
if (router.backstack.lastOrNull()?.controller == this) {
|
||||
(activity as? AppCompatActivity)?.supportActionBar?.title = getTitle()
|
||||
if (isControllerVisible) {
|
||||
(activity as? AppCompatActivity)?.title = getTitle()
|
||||
(activity as? MainActivity)?.searchTitle = getSearchTitle()
|
||||
val icon = getBigIcon()
|
||||
activityBinding?.bigIcon?.isVisible = icon != null
|
||||
if (icon != null) {
|
||||
activityBinding?.bigIcon?.setImageDrawable(getBigIcon())
|
||||
} else {
|
||||
activityBinding?.bigIcon?.setImageDrawable(getBigIcon())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,6 +154,9 @@ abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) :
|
|||
}
|
||||
}
|
||||
|
||||
open fun onActionViewExpand(item: MenuItem?) { }
|
||||
open fun onActionViewCollapse(item: MenuItem?) { }
|
||||
|
||||
fun hideItemsIfExpanded(searchItem: MenuItem?, menu: Menu?, isExpanded: Boolean = false) {
|
||||
menu ?: return
|
||||
searchItem ?: return
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.google.android.material.snackbar.Snackbar
|
|||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.CategoriesControllerBinding
|
||||
import eu.kanade.tachiyomi.ui.base.SmallToolbarInterface
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryPresenter.Companion.CREATE_CATEGORY_ORDER
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
|
@ -25,6 +26,7 @@ class CategoryController(bundle: Bundle? = null) :
|
|||
BaseController<CategoriesControllerBinding>(bundle),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemMoveListener,
|
||||
SmallToolbarInterface,
|
||||
CategoryAdapter.CategoryItemListener {
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
|||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
import eu.kanade.tachiyomi.databinding.SetCategoriesSheetBinding
|
||||
import eu.kanade.tachiyomi.ui.category.ManageCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
|
||||
import eu.kanade.tachiyomi.util.view.checkHeightThen
|
||||
|
@ -218,9 +219,7 @@ class SetCategoriesSheet(
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val attrsArray = intArrayOf(R.attr.mainActionBarSize)
|
||||
val array = context.obtainStyledAttributes(attrsArray)
|
||||
val headerHeight = array.getDimensionPixelSize(0, 0)
|
||||
val headerHeight = (activity as? MainActivity)?.toolbarHeight ?: 0
|
||||
binding.buttonLayout.updatePaddingRelative(
|
||||
bottom = activity.window.decorView.rootWindowInsetsCompat
|
||||
?.getInsets(systemBars())?.bottom ?: 0
|
||||
|
@ -229,7 +228,6 @@ class SetCategoriesSheet(
|
|||
binding.buttonLayout.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
height = headerHeight + binding.buttonLayout.paddingBottom
|
||||
}
|
||||
array.recycle()
|
||||
|
||||
binding.cancelButton.setOnClickListener { dismiss() }
|
||||
binding.newCategoryButton.setOnClickListener {
|
||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.ui.download
|
|||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
|
@ -15,6 +14,7 @@ import eu.kanade.tachiyomi.data.download.DownloadService
|
|||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.databinding.DownloadBottomSheetBinding
|
||||
import eu.kanade.tachiyomi.ui.extension.ExtensionDividerItemDecoration
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.recents.RecentsController
|
||||
import eu.kanade.tachiyomi.util.view.collapse
|
||||
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsetsCompat
|
||||
|
@ -69,10 +69,7 @@ class DownloadBottomSheet @JvmOverloads constructor(
|
|||
this.controller = controller
|
||||
updateDLTitle()
|
||||
|
||||
val attrsArray = intArrayOf(android.R.attr.actionBarSize)
|
||||
val array = context.obtainStyledAttributes(attrsArray)
|
||||
val headerHeight = array.getDimensionPixelSize(0, 0)
|
||||
array.recycle()
|
||||
val headerHeight = (activity as? MainActivity)?.toolbarHeight ?: 0
|
||||
binding.recyclerLayout.doOnApplyWindowInsetsCompat { v, windowInsets, _ ->
|
||||
v.updateLayoutParams<MarginLayoutParams> {
|
||||
topMargin = windowInsets.getInsets(systemBars()).top +
|
||||
|
@ -108,6 +105,7 @@ class DownloadBottomSheet @JvmOverloads constructor(
|
|||
presenter.getItems()
|
||||
onQueueStatusChange(!presenter.downloadManager.isPaused())
|
||||
binding.downloadFab.isInvisible = presenter.downloadQueue.isEmpty()
|
||||
prepareMenu()
|
||||
}
|
||||
|
||||
private fun updateDLTitle() {
|
||||
|
@ -130,7 +128,7 @@ class DownloadBottomSheet @JvmOverloads constructor(
|
|||
binding.downloadFab.isInvisible = presenter.downloadQueue.isEmpty()
|
||||
updateFab()
|
||||
if (oldRunning != running) {
|
||||
activity?.invalidateOptionsMenu()
|
||||
prepareMenu()
|
||||
|
||||
// Check if download queue is empty and update information accordingly.
|
||||
setInformationView()
|
||||
|
@ -143,7 +141,7 @@ class DownloadBottomSheet @JvmOverloads constructor(
|
|||
* @param downloads the downloads from the queue.
|
||||
*/
|
||||
fun onNextDownloads(downloads: List<DownloadItem>) {
|
||||
activity?.invalidateOptionsMenu()
|
||||
prepareMenu()
|
||||
setInformationView()
|
||||
adapter?.updateDataSet(downloads)
|
||||
setBottomSheet()
|
||||
|
@ -193,7 +191,8 @@ class DownloadBottomSheet @JvmOverloads constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun prepareMenu(menu: Menu) {
|
||||
private fun prepareMenu() {
|
||||
val menu = binding.sheetToolbar.menu
|
||||
updateFab()
|
||||
// Set clear button visibility.
|
||||
menu.findItem(R.id.clear_queue)?.isVisible = !presenter.downloadQueue.isEmpty()
|
||||
|
|
|
@ -25,6 +25,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -72,7 +73,7 @@ class ExtensionBottomPresenter(
|
|||
extensionManager.availableExtensions
|
||||
)
|
||||
)
|
||||
withContext(Dispatchers.Main) { bottomSheet.setExtensions(extensions) }
|
||||
withContext(Dispatchers.Main) { bottomSheet.setExtensions(extensions, false) }
|
||||
}
|
||||
val migrationJob = async {
|
||||
val favs = db.getFavoriteMangas().executeOnIO()
|
||||
|
@ -97,6 +98,7 @@ class ExtensionBottomPresenter(
|
|||
}
|
||||
presenterScope.launch {
|
||||
extensionManager.downloadRelay
|
||||
.drop(3)
|
||||
.collect {
|
||||
if (it.first.startsWith("Finished")) {
|
||||
firstLoad = true
|
||||
|
@ -151,7 +153,7 @@ class ExtensionBottomPresenter(
|
|||
extensionManager.availableExtensions
|
||||
)
|
||||
)
|
||||
withContext(Dispatchers.Main) { bottomSheet.setExtensions(extensions) }
|
||||
withContext(Dispatchers.Main) { bottomSheet.setExtensions(extensions, false) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper
|
|||
import eu.kanade.tachiyomi.util.view.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import eu.kanade.tachiyomi.widget.LinearLayoutManagerAccurateOffset
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
@ -109,8 +110,7 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
|
|||
|
||||
manager.setPreferences(screen)
|
||||
|
||||
binding.extensionPrefsRecycler.layoutManager =
|
||||
androidx.recyclerview.widget.LinearLayoutManager(context)
|
||||
binding.extensionPrefsRecycler.layoutManager = LinearLayoutManagerAccurateOffset(context)
|
||||
val concatAdapterConfig = ConcatAdapter.Config.Builder()
|
||||
.setStableIdMode(ConcatAdapter.Config.StableIdMode.ISOLATED_STABLE_IDS)
|
||||
.build()
|
||||
|
|
|
@ -67,10 +67,10 @@ class LibraryCategoryAdapter(val controller: LibraryController) :
|
|||
}
|
||||
|
||||
private fun setItemsPerCategoryMap() {
|
||||
itemsPerCategory = headerItems.map { header ->
|
||||
itemsPerCategory = headerItems.associate { header ->
|
||||
(header as LibraryHeaderItem).catId to
|
||||
controller.presenter.getItemCountInCategories(header.catId)
|
||||
}.toMap()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.animation.ValueAnimator
|
|||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
|
@ -21,10 +20,8 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.view.ViewPropertyAnimator
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
|
@ -34,7 +31,9 @@ import androidx.core.view.WindowInsetsCompat.Type.ime
|
|||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.marginTop
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.core.view.updatePaddingRelative
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
|
@ -94,10 +93,13 @@ import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
|
|||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.collapse
|
||||
import eu.kanade.tachiyomi.util.view.expand
|
||||
import eu.kanade.tachiyomi.util.view.fullAppBarHeight
|
||||
import eu.kanade.tachiyomi.util.view.getItemView
|
||||
import eu.kanade.tachiyomi.util.view.hide
|
||||
import eu.kanade.tachiyomi.util.view.isControllerVisible
|
||||
import eu.kanade.tachiyomi.util.view.isExpanded
|
||||
import eu.kanade.tachiyomi.util.view.isHidden
|
||||
import eu.kanade.tachiyomi.util.view.moveRecyclerViewUp
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||
import eu.kanade.tachiyomi.util.view.setStyle
|
||||
|
@ -222,7 +224,15 @@ class LibraryController(
|
|||
) + 55f.dpToPx
|
||||
}
|
||||
|
||||
override val mainRecycler: RecyclerView
|
||||
get() = binding.libraryGridRecycler.recycler
|
||||
|
||||
override fun getTitle(): String? {
|
||||
setSubtitle()
|
||||
return view?.context?.getString(R.string.library)
|
||||
}
|
||||
|
||||
override fun getSearchTitle(): String? {
|
||||
setSubtitle()
|
||||
return searchTitle(
|
||||
if (preferences.showLibrarySearchSuggestions().get() &&
|
||||
|
@ -270,6 +280,9 @@ class LibraryController(
|
|||
if (!preferences.hideBottomNavOnScroll().get() || activityBinding?.bottomNav == null) {
|
||||
updateFilterSheetY()
|
||||
}
|
||||
if (!binding.fastScroller.isFastScrolling) {
|
||||
updateSmallerViewsTopMargins()
|
||||
}
|
||||
binding.roundedCategoryHopper.upCategory.alpha = if (isAtTop()) 0.25f else 1f
|
||||
binding.roundedCategoryHopper.downCategory.alpha = if (isAtBottom()) 0.25f else 1f
|
||||
}
|
||||
|
@ -390,7 +403,7 @@ class LibraryController(
|
|||
}
|
||||
|
||||
private fun setSubtitle() {
|
||||
if (!singleCategory && presenter.showAllCategories &&
|
||||
if (isBindingInitialized && !singleCategory && presenter.showAllCategories &&
|
||||
!binding.headerTitle.text.isNullOrBlank() && !binding.recyclerCover.isClickable
|
||||
) {
|
||||
activityBinding?.cardToolbar?.subtitle = binding.headerTitle.text.toString()
|
||||
|
@ -551,11 +564,9 @@ class LibraryController(
|
|||
swipeRefreshLayout = binding.swipeRefresh,
|
||||
afterInsets = { insets ->
|
||||
binding.categoryRecycler.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = binding.libraryGridRecycler.recycler.paddingTop
|
||||
}
|
||||
binding.fastScroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = binding.libraryGridRecycler.recycler.paddingTop
|
||||
topMargin = insets.getInsets(systemBars()).top + (activityBinding?.cardToolbar?.height ?: 0) + 12.dpToPx
|
||||
}
|
||||
updateSmallerViewsTopMargins()
|
||||
binding.headerCard.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = insets.getInsets(systemBars()).top + 4.dpToPx
|
||||
}
|
||||
|
@ -583,11 +594,42 @@ class LibraryController(
|
|||
|
||||
if (presenter.libraryItems.isNotEmpty()) {
|
||||
presenter.restoreLibrary()
|
||||
if (justStarted) {
|
||||
val activityBinding = activityBinding ?: return
|
||||
val bigToolbarHeight = fullAppBarHeight ?: return
|
||||
if (lastUsedCategory > 0) {
|
||||
activityBinding.appBar.y =
|
||||
-bigToolbarHeight + activityBinding.cardFrame.height.toFloat()
|
||||
activityBinding.appBar.useSearchToolbarForMenu(true)
|
||||
}
|
||||
activityBinding.appBar.lockYPos = true
|
||||
}
|
||||
} else {
|
||||
binding.recyclerLayout.alpha = 0f
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSmallerViewsTopMargins() {
|
||||
val activityBinding = activityBinding ?: return
|
||||
val bigToolbarHeight = fullAppBarHeight ?: return
|
||||
val value = max(
|
||||
0,
|
||||
bigToolbarHeight + activityBinding.appBar.y.roundToInt()
|
||||
) + activityBinding.appBar.paddingTop
|
||||
if (value != binding.fastScroller.marginTop) {
|
||||
binding.fastScroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = value
|
||||
}
|
||||
binding.emptyView.updatePadding(
|
||||
top = bigToolbarHeight + activityBinding.appBar.paddingTop,
|
||||
bottom = binding.libraryGridRecycler.recycler.paddingBottom
|
||||
)
|
||||
binding.progress.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = (bigToolbarHeight + activityBinding.appBar.paddingTop) / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSwipeRefresh() = with(binding.swipeRefresh) {
|
||||
setOnRefreshListener {
|
||||
isRefreshing = false
|
||||
|
@ -598,48 +640,7 @@ class LibraryController(
|
|||
updateLibrary(it)
|
||||
}
|
||||
}
|
||||
presenter.allCategories.size <= 1 || presenter.groupType > BY_DEFAULT -> {
|
||||
updateLibrary()
|
||||
}
|
||||
preferences.updateOnRefresh().get() == -1 -> {
|
||||
var selected = 0
|
||||
activity!!.materialAlertDialog()
|
||||
.setTitle(R.string.what_should_update)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setSingleChoiceItems(
|
||||
arrayOf(
|
||||
context.getString(
|
||||
R.string.top_category,
|
||||
presenter.allCategories.first().name
|
||||
),
|
||||
context.getString(
|
||||
R.string.categories_in_global_update
|
||||
)
|
||||
),
|
||||
-1
|
||||
) { dialog, index ->
|
||||
selected = index
|
||||
(dialog as? AlertDialog)?.getButton(DialogInterface.BUTTON_POSITIVE)?.isEnabled = true
|
||||
}
|
||||
.setPositiveButton(
|
||||
R.string.update
|
||||
) { _, _ ->
|
||||
preferences.updateOnRefresh().set(selected)
|
||||
when (selected) {
|
||||
0 -> updateLibrary(presenter.allCategories.first())
|
||||
else -> updateLibrary()
|
||||
}
|
||||
}
|
||||
.show().apply {
|
||||
getButton(DialogInterface.BUTTON_POSITIVE).isEnabled = false
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
when (preferences.updateOnRefresh().get()) {
|
||||
0 -> updateLibrary(presenter.allCategories.first())
|
||||
else -> updateLibrary()
|
||||
}
|
||||
}
|
||||
else -> updateLibrary()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -796,7 +797,7 @@ class LibraryController(
|
|||
if (!next) {
|
||||
val fPosition =
|
||||
(binding.libraryGridRecycler.recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
|
||||
if (fPosition != adapter.currentItems.indexOf(category)) {
|
||||
if (fPosition > adapter.currentItems.indexOf(category)) {
|
||||
scrollToHeader(category.category.order)
|
||||
return
|
||||
}
|
||||
|
@ -955,12 +956,11 @@ class LibraryController(
|
|||
activityBinding?.cardToolbar?.setOnLongClickListener {
|
||||
val suggestion = preferences.librarySearchSuggestion().get()
|
||||
if (suggestion.isNotBlank()) {
|
||||
val searchItem =
|
||||
activityBinding?.cardToolbar?.menu?.findItem(R.id.action_search)
|
||||
val searchView = searchItem?.actionView as? SearchView
|
||||
val searchItem = activityBinding?.cardToolbar?.searchItem
|
||||
val searchView = activityBinding?.cardToolbar?.searchView
|
||||
?: return@setOnLongClickListener false
|
||||
searchItem.expandActionView()
|
||||
searchView.setQuery(suggestion, false)
|
||||
searchItem?.expandActionView()
|
||||
searchView.setQuery(suggestion.removeSuffix("…"), false)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -993,6 +993,7 @@ class LibraryController(
|
|||
|
||||
override fun onDestroyView(view: View) {
|
||||
LibraryUpdateService.removeListener(this)
|
||||
activityBinding?.appBar?.lockYPos = false
|
||||
destroyActionModeIfNeeded()
|
||||
if (isBindingInitialized) {
|
||||
binding.libraryGridRecycler.recycler.removeOnScrollListener(scrollListener)
|
||||
|
@ -1031,8 +1032,19 @@ class LibraryController(
|
|||
binding.recyclerLayout.animate().alpha(1f).setDuration(500).start()
|
||||
}
|
||||
if (justStarted && freshStart) {
|
||||
val activeC = activeCategory
|
||||
scrollToHeader(activeCategory)
|
||||
binding.libraryGridRecycler.recycler.post {
|
||||
if (isControllerVisible) {
|
||||
activityBinding?.appBar?.y = 0f
|
||||
activityBinding?.appBar?.updateAppBarAfterY(binding.libraryGridRecycler.recycler)
|
||||
if (activeC > 0) {
|
||||
activityBinding?.appBar?.useSearchToolbarForMenu(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
activityBinding?.appBar?.lockYPos = false
|
||||
binding.libraryGridRecycler.recycler.post {
|
||||
elevateAppBar(binding.libraryGridRecycler.recycler.canScrollVertically(-1))
|
||||
setActiveCategory()
|
||||
|
@ -1056,18 +1068,16 @@ class LibraryController(
|
|||
binding.libraryGridRecycler.recycler.scrollToPosition(0)
|
||||
shouldScrollToTop = false
|
||||
}
|
||||
if (onRoot) {
|
||||
listOf(activityBinding?.toolbar, binding.headerTitle).forEach {
|
||||
it?.setOnClickListener {
|
||||
val recycler = binding.libraryGridRecycler.recycler
|
||||
if (!singleCategory) {
|
||||
showCategories(recycler.translationY == 0f)
|
||||
}
|
||||
}
|
||||
if (!hasMovedHopper && isAnimatingHopper == null) {
|
||||
showSlideAnimation()
|
||||
if (isControllerVisible) {
|
||||
binding.headerTitle.setOnClickListener {
|
||||
val recycler = binding.libraryGridRecycler.recycler
|
||||
if (!singleCategory) {
|
||||
showCategories(recycler.translationY == 0f)
|
||||
}
|
||||
}
|
||||
if (!hasMovedHopper && isAnimatingHopper == null) {
|
||||
showSlideAnimation()
|
||||
}
|
||||
setSubtitle()
|
||||
showMiniBar()
|
||||
}
|
||||
|
@ -1106,13 +1116,12 @@ class LibraryController(
|
|||
binding.recyclerCover.isClickable = show
|
||||
binding.recyclerCover.isFocusable = show
|
||||
if (closeSearch) {
|
||||
activityBinding?.cardToolbar?.menu?.findItem(R.id.action_search)?.collapseActionView()
|
||||
activityBinding?.cardToolbar?.searchItem?.collapseActionView()
|
||||
}
|
||||
val full = binding.categoryRecycler.height.toFloat() + binding.libraryGridRecycler.recycler.paddingTop
|
||||
val full = binding.categoryRecycler.height.toFloat() + binding.categoryRecycler.marginTop
|
||||
val translateY = if (show) full else 0f
|
||||
binding.libraryGridRecycler.recycler.animate().translationY(translateY).apply {
|
||||
setUpdateListener {
|
||||
activityBinding?.appBar?.y = 0f
|
||||
updateHopperY()
|
||||
}
|
||||
}.start()
|
||||
|
@ -1127,7 +1136,6 @@ class LibraryController(
|
|||
binding.categoryRecycler.scrollToCategory(activeCategory)
|
||||
}
|
||||
binding.fastScroller.hideScrollbar()
|
||||
activityBinding?.appBar?.y = 0f
|
||||
elevateAppBar(false)
|
||||
binding.filterBottomSheet.filterBottomSheet.sheetBehavior?.hide()
|
||||
} else {
|
||||
|
@ -1146,13 +1154,9 @@ class LibraryController(
|
|||
}
|
||||
val headerPosition = adapter.indexOf(pos)
|
||||
if (headerPosition > -1) {
|
||||
val appbar = activityBinding?.appBar
|
||||
val activityBinding = activityBinding ?: return
|
||||
binding.libraryGridRecycler.recycler.suppressLayout(true)
|
||||
val appbarOffset = if (appbar?.y ?: 0f > -20) 0 else (
|
||||
appbar?.y?.plus(
|
||||
view?.rootWindowInsetsCompat?.getInsets(systemBars())?.top ?: 0
|
||||
) ?: 0f
|
||||
).roundToInt() + 30.dpToPx
|
||||
val appbarOffset = if (pos <= 0) 0 else -fullAppBarHeight!! + activityBinding.cardFrame.height
|
||||
val previousHeader = adapter.getItem(adapter.indexOf(pos - 1)) as? LibraryHeaderItem
|
||||
(binding.libraryGridRecycler.recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
|
||||
headerPosition,
|
||||
|
@ -1170,11 +1174,14 @@ class LibraryController(
|
|||
activeCategory = pos
|
||||
preferences.lastUsedCategory().set(pos)
|
||||
binding.libraryGridRecycler.recycler.suppressLayout(false)
|
||||
binding.libraryGridRecycler.recycler.post {
|
||||
activityBinding.appBar.y = 0f
|
||||
activityBinding.appBar.updateAppBarAfterY(binding.libraryGridRecycler.recycler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onRefresh() {
|
||||
activity?.invalidateOptionsMenu()
|
||||
showCategories(false)
|
||||
presenter.getLibrary()
|
||||
destroyActionModeIfNeeded()
|
||||
|
@ -1184,7 +1191,6 @@ class LibraryController(
|
|||
* Called when a filter is changed.
|
||||
*/
|
||||
private fun onFilterChanged() {
|
||||
activity?.invalidateOptionsMenu()
|
||||
presenter.requestFilterUpdate()
|
||||
destroyActionModeIfNeeded()
|
||||
}
|
||||
|
@ -1222,6 +1228,7 @@ class LibraryController(
|
|||
viewScope.launchUI {
|
||||
adapter.performFilterAsync()
|
||||
}
|
||||
moveRecyclerViewUp()
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -1610,8 +1617,6 @@ class LibraryController(
|
|||
}
|
||||
}
|
||||
|
||||
override fun sheetIsFullscreen(): Boolean = false
|
||||
|
||||
override fun handleSheetBack(): Boolean {
|
||||
if (binding.recyclerCover.isClickable) {
|
||||
showCategories(false)
|
||||
|
@ -1629,41 +1634,47 @@ class LibraryController(
|
|||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.library, menu)
|
||||
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
searchView.queryHint = resources?.getString(R.string.library_search_hint)
|
||||
val searchItem = activityBinding?.cardToolbar?.searchItem
|
||||
val searchView = activityBinding?.cardToolbar?.searchView
|
||||
activityBinding?.cardToolbar?.setQueryHint(resources?.getString(R.string.library_search_hint), query.isEmpty())
|
||||
|
||||
searchItem.collapseActionView()
|
||||
if (query.isNotEmpty()) {
|
||||
searchItem.expandActionView()
|
||||
searchView.setQuery(query, true)
|
||||
searchView.clearFocus()
|
||||
if (activityBinding?.cardToolbar?.isSearchExpanded != true) {
|
||||
searchItem?.expandActionView()
|
||||
searchView?.setQuery(query, true)
|
||||
searchView?.clearFocus()
|
||||
} else {
|
||||
searchView?.setQuery(query, false)
|
||||
}
|
||||
search(query)
|
||||
} else if (activityBinding?.cardToolbar?.isSearchExpanded == true) {
|
||||
searchItem?.collapseActionView()
|
||||
}
|
||||
|
||||
setOnQueryTextChangeListener(searchView) {
|
||||
setOnQueryTextChangeListener(activityBinding?.cardToolbar?.searchView) {
|
||||
if (!it.isNullOrEmpty() && binding.recyclerCover.isClickable) {
|
||||
showCategories(false)
|
||||
}
|
||||
search(it)
|
||||
}
|
||||
searchItem.fixExpand(
|
||||
onExpand = {
|
||||
if (!binding.recyclerCover.isClickable && query.isBlank() &&
|
||||
!singleCategory && presenter.showAllCategories
|
||||
) {
|
||||
showCategories(true)
|
||||
}
|
||||
invalidateMenuOnExpand()
|
||||
},
|
||||
onCollapse = {
|
||||
if (binding.recyclerCover.isClickable) {
|
||||
showCategories(false)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
override fun onActionViewExpand(item: MenuItem?) {
|
||||
if (!binding.recyclerCover.isClickable && query.isBlank() &&
|
||||
!singleCategory && presenter.showAllCategories
|
||||
) {
|
||||
showCategories(true)
|
||||
binding.libraryGridRecycler.recycler.post {
|
||||
activityBinding?.appBar?.y = (activityBinding?.appBar?.yNeededForSmallToolbar ?: 0).toFloat()
|
||||
activityBinding?.appBar?.updateAppBarAfterY(binding.libraryGridRecycler.recycler)
|
||||
}
|
||||
)
|
||||
hideItemsIfExpanded(searchItem, menu)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActionViewCollapse(item: MenuItem?) {
|
||||
if (binding.recyclerCover.isClickable) {
|
||||
showCategories(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
|
|
@ -1160,10 +1160,10 @@ class LibraryPresenter(
|
|||
sourceManager.get(
|
||||
distinctSources.randomOrNull(random)?.source ?: 0L
|
||||
)?.name
|
||||
randomSource?.chopByWords(15)
|
||||
randomSource?.chopByWords(30)
|
||||
}
|
||||
randomTitle -> {
|
||||
libraryManga.randomOrNull(random)?.title?.chopByWords(15)
|
||||
libraryManga.randomOrNull(random)?.title?.chopByWords(30)
|
||||
}
|
||||
in randomTags -> {
|
||||
val tags = recentManga.map {
|
||||
|
|
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.main
|
|||
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.app.assist.AssistContent
|
||||
import android.content.Context
|
||||
|
@ -21,6 +22,9 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.appcompat.view.menu.ActionMenuItemView
|
||||
import androidx.appcompat.view.menu.MenuItemImpl
|
||||
import androidx.appcompat.widget.ActionMenuView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.net.toUri
|
||||
|
@ -29,6 +33,8 @@ import androidx.core.view.ViewCompat
|
|||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
|
@ -60,6 +66,7 @@ import eu.kanade.tachiyomi.extension.ExtensionManager
|
|||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.base.MaterialMenuSheet
|
||||
import eu.kanade.tachiyomi.ui.base.SmallToolbarInterface
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
|
@ -75,6 +82,7 @@ import eu.kanade.tachiyomi.ui.source.BrowseController
|
|||
import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
|
||||
import eu.kanade.tachiyomi.util.manga.MangaShortcutManager
|
||||
import eu.kanade.tachiyomi.util.system.contextCompatDrawable
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.hasSideNavBar
|
||||
import eu.kanade.tachiyomi.util.system.isBottomTappable
|
||||
|
@ -87,7 +95,9 @@ import eu.kanade.tachiyomi.util.system.toast
|
|||
import eu.kanade.tachiyomi.util.view.backgroundColor
|
||||
import eu.kanade.tachiyomi.util.view.blurBehindWindow
|
||||
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsetsCompat
|
||||
import eu.kanade.tachiyomi.util.view.findChild
|
||||
import eu.kanade.tachiyomi.util.view.getItemView
|
||||
import eu.kanade.tachiyomi.util.view.moveRecyclerViewUp
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import eu.kanade.tachiyomi.util.view.withFadeInTransaction
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
|
@ -108,9 +118,8 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
|
||||
protected lateinit var router: Router
|
||||
|
||||
private val searchDrawable by lazy { contextCompatDrawable(R.drawable.ic_search_24dp) }
|
||||
protected val searchDrawable by lazy { contextCompatDrawable(R.drawable.ic_search_24dp) }
|
||||
protected val backDrawable by lazy { contextCompatDrawable(R.drawable.ic_arrow_back_24dp) }
|
||||
private val dismissDrawable by lazy { contextCompatDrawable(R.drawable.ic_close_24dp) }
|
||||
private var gestureDetector: GestureDetectorCompat? = null
|
||||
|
||||
private var snackBar: Snackbar? = null
|
||||
|
@ -131,6 +140,15 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
var currentToolbar: Toolbar? = null
|
||||
var ogWidth: Int = Int.MAX_VALUE
|
||||
|
||||
private val actionButtonSize: Pair<Int, Int> by lazy {
|
||||
val attrs = intArrayOf(android.R.attr.minWidth, android.R.attr.minHeight)
|
||||
val ta = obtainStyledAttributes(androidx.appcompat.R.style.Widget_AppCompat_ActionButton, attrs)
|
||||
val dimenW = ta.getDimensionPixelSize(0, 0.dpToPx)
|
||||
val dimenH = ta.getDimensionPixelSize(1, 0.dpToPx)
|
||||
ta.recycle()
|
||||
dimenW to dimenH
|
||||
}
|
||||
|
||||
fun setUndoSnackBar(snackBar: Snackbar?, extraViewToCheck: View? = null) {
|
||||
this.snackBar = snackBar
|
||||
canDismissSnackBar = false
|
||||
|
@ -151,6 +169,14 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
val toolbarHeight: Int
|
||||
get() = max(binding.toolbar.height, binding.cardFrame.height)
|
||||
|
||||
fun bigToolbarHeight(includeSearchToolbar: Boolean, includeTabs: Boolean, includeLargeToolbar: Boolean): Int {
|
||||
return if (!includeLargeToolbar || !binding.appBar.useLargeToolbar) {
|
||||
toolbarHeight + if (includeTabs) 48.dpToPx else 0
|
||||
} else {
|
||||
binding.appBar.getEstimatedLayout(includeSearchToolbar, includeTabs, includeLargeToolbar)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
@ -205,9 +231,11 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
val content: ViewGroup = binding.mainContent
|
||||
DownloadService.addListener(this)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
supportActionBar?.setDisplayShowCustomEnabled(true)
|
||||
|
||||
setNavBarColor(content.rootWindowInsetsCompat)
|
||||
binding.appBar.mainActivity = this
|
||||
nav.isVisible = false
|
||||
content.doOnApplyWindowInsetsCompat { v, insets, _ ->
|
||||
setNavBarColor(insets)
|
||||
|
@ -302,23 +330,52 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
}
|
||||
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
val rootSearchController = router.backstack.lastOrNull()?.controller
|
||||
if (rootSearchController is RootSearchInterface) {
|
||||
rootSearchController.expandSearch()
|
||||
} else onBackPressed()
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
binding.cardToolbar.setNavigationOnClickListener {
|
||||
val rootSearchController = router.backstack.lastOrNull()?.controller
|
||||
if (rootSearchController is RootSearchInterface) {
|
||||
rootSearchController.expandSearch()
|
||||
if ((
|
||||
rootSearchController is RootSearchInterface ||
|
||||
(currentToolbar != binding.cardToolbar && binding.appBar.useLargeToolbar)
|
||||
) &&
|
||||
rootSearchController !is SmallToolbarInterface
|
||||
) {
|
||||
binding.cardToolbar.menu.findItem(R.id.action_search)?.expandActionView()
|
||||
} else onBackPressed()
|
||||
}
|
||||
|
||||
binding.cardToolbar.searchItem?.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
|
||||
val controller = router.backstack.lastOrNull()?.controller
|
||||
controller?.moveRecyclerViewUp()
|
||||
(controller as? BaseController<*>)?.onActionViewExpand(item)
|
||||
(controller as? SettingsController)?.onActionViewExpand(item)
|
||||
binding.cardToolbar.menu.forEach { it.isVisible = false }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
|
||||
val controller = router.backstack.lastOrNull()?.controller
|
||||
setupSearchTBMenu(binding.toolbar.menu, true)
|
||||
(controller as? BaseController<*>)?.onActionViewCollapse(item)
|
||||
(controller as? SettingsController)?.onActionViewCollapse(item)
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
binding.appBar.alpha = 1f
|
||||
|
||||
binding.cardToolbar.setOnClickListener {
|
||||
binding.cardToolbar.menu.findItem(R.id.action_search)?.expandActionView()
|
||||
}
|
||||
|
||||
binding.cardToolbar.setOnMenuItemClickListener {
|
||||
if (router.backstack.lastOrNull()?.controller?.onOptionsItemSelected(it) == true) {
|
||||
return@setOnMenuItemClickListener true
|
||||
} else return@setOnMenuItemClickListener onOptionsItemSelected(it)
|
||||
}
|
||||
|
||||
nav.isVisible = !hideBottomNav
|
||||
binding.bottomView?.visibility = if (hideBottomNav) View.GONE else binding.bottomView?.visibility ?: View.GONE
|
||||
nav.alpha = if (hideBottomNav) 0f else 1f
|
||||
|
@ -332,7 +389,8 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
handler: ControllerChangeHandler
|
||||
) {
|
||||
syncActivityViewWithController(to, from, isPush)
|
||||
binding.appBar.y = 0f
|
||||
binding.appBar.isVisible = true
|
||||
binding.appBar.alpha = 1f
|
||||
if (!isPush || router.backstackSize == 1) {
|
||||
nav.translationY = 0f
|
||||
}
|
||||
|
@ -346,7 +404,6 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
container: ViewGroup,
|
||||
handler: ControllerChangeHandler
|
||||
) {
|
||||
binding.appBar.y = 0f
|
||||
nav.translationY = 0f
|
||||
showDLQueueTutorial()
|
||||
if (router.backstackSize == 1) {
|
||||
|
@ -363,7 +420,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
|
||||
syncActivityViewWithController(router.backstack.lastOrNull()?.controller)
|
||||
|
||||
val navIcon = if (router.backstackSize > 1) backDrawable else searchDrawable
|
||||
val navIcon = if (router.backstackSize > 1) backDrawable else null
|
||||
binding.toolbar.navigationIcon = navIcon
|
||||
(router.backstack.lastOrNull()?.controller as? BaseController<*>)?.setTitle()
|
||||
(router.backstack.lastOrNull()?.controller as? SettingsController)?.setTitle()
|
||||
|
@ -403,38 +460,57 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
setFloatingToolbar(canShowFloatingToolbar(router.backstack.lastOrNull()?.controller), changeBG = false)
|
||||
}
|
||||
|
||||
open fun setFloatingToolbar(show: Boolean, solidBG: Boolean = false, changeBG: Boolean = true) {
|
||||
val oldTB = currentToolbar
|
||||
currentToolbar = if (show) {
|
||||
override fun onTitleChanged(title: CharSequence?, color: Int) {
|
||||
super.onTitleChanged(title, color)
|
||||
binding.cardToolbar.title = searchTitle
|
||||
val onExpandedController = if (this::router.isInitialized) router.backstack.lastOrNull()?.controller !is SmallToolbarInterface else false
|
||||
binding.appBar.setTitle(title, onExpandedController)
|
||||
}
|
||||
|
||||
var searchTitle: String?
|
||||
get() {
|
||||
return try {
|
||||
(router.backstack.lastOrNull()?.controller as? BaseController<*>)?.getSearchTitle()
|
||||
?: (router.backstack.lastOrNull()?.controller as? SettingsController)?.getSearchTitle()
|
||||
} catch (_: Exception) {
|
||||
binding.cardToolbar.title?.toString()
|
||||
}
|
||||
}
|
||||
set(title) {
|
||||
binding.cardToolbar.title = title
|
||||
}
|
||||
|
||||
open fun setFloatingToolbar(show: Boolean, solidBG: Boolean = false, changeBG: Boolean = true, showSearchAnyway: Boolean = false) {
|
||||
val controller = if (this::router.isInitialized) router.backstack.lastOrNull()?.controller else null
|
||||
val useLargeTB = binding.appBar.useLargeToolbar
|
||||
val onSearchController = canShowFloatingToolbar(controller)
|
||||
val onSmallerController = controller is SmallToolbarInterface || !useLargeTB
|
||||
currentToolbar = if (show && ((showSearchAnyway && onSearchController) || onSmallerController)) {
|
||||
binding.cardToolbar
|
||||
} else {
|
||||
binding.toolbar
|
||||
}
|
||||
if (oldTB != currentToolbar) {
|
||||
setSupportActionBar(currentToolbar)
|
||||
}
|
||||
binding.toolbar.isVisible = !show
|
||||
binding.cardFrame.isVisible = show
|
||||
binding.toolbar.isVisible = !(onSmallerController && onSearchController)
|
||||
binding.cardFrame.isVisible = (show || showSearchAnyway) && onSearchController
|
||||
val bgColor = binding.appBar.backgroundColor ?: Color.TRANSPARENT
|
||||
if (changeBG && (if (solidBG) bgColor == Color.TRANSPARENT else false)) {
|
||||
binding.appBar.setBackgroundColor(
|
||||
if (show && !solidBG) Color.TRANSPARENT else getResourceColor(R.attr.colorSurface)
|
||||
)
|
||||
}
|
||||
currentToolbar?.setNavigationOnClickListener {
|
||||
val rootSearchController = router.backstack.lastOrNull()?.controller
|
||||
if (rootSearchController is RootSearchInterface) {
|
||||
rootSearchController.expandSearch()
|
||||
} else onBackPressed()
|
||||
setupSearchTBMenu(binding.toolbar.menu)
|
||||
if (currentToolbar != binding.cardToolbar) {
|
||||
binding.cardToolbar.menu?.children?.toList()?.forEach {
|
||||
it.isVisible = false
|
||||
}
|
||||
}
|
||||
if (oldTB != currentToolbar) {
|
||||
invalidateOptionsMenu()
|
||||
val onRoot = !this::router.isInitialized || router.backstackSize == 1
|
||||
if (!useLargeTB) {
|
||||
binding.cardToolbar.navigationIcon = if (onRoot) searchDrawable else backDrawable
|
||||
} else if (showSearchAnyway) {
|
||||
binding.cardToolbar.navigationIcon = if (!show || onRoot) searchDrawable else backDrawable
|
||||
}
|
||||
}
|
||||
|
||||
fun setDismissIcon(enabled: Boolean) {
|
||||
binding.cardToolbar.navigationIcon = if (enabled) dismissDrawable else searchDrawable
|
||||
binding.toolbar.navigationIcon = if (enabled) dismissDrawable else searchDrawable
|
||||
binding.cardToolbar.title = searchTitle
|
||||
}
|
||||
|
||||
private fun setNavBarColor(insets: WindowInsetsCompat?) {
|
||||
|
@ -494,6 +570,15 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
super.onSupportActionModeFinished(mode)
|
||||
}
|
||||
|
||||
fun setStatusBarColorTransparent(show: Boolean) {
|
||||
window?.statusBarColor = if (show) {
|
||||
ColorUtils.setAlphaComponent(window?.statusBarColor ?: Color.TRANSPARENT, 0)
|
||||
} else {
|
||||
val color = getResourceColor(android.R.attr.statusBarColor)
|
||||
ColorUtils.setAlphaComponent(window?.statusBarColor ?: color, Color.alpha(color))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setExtensionsBadge() {
|
||||
val updates = preferences.extensionUpdatesCount().get()
|
||||
if (updates > 0) {
|
||||
|
@ -695,12 +780,21 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
overflowDialog = null
|
||||
DownloadService.removeListener(this)
|
||||
if (isBindingInitialized) {
|
||||
binding.appBar.mainActivity = null
|
||||
binding.toolbar.setNavigationOnClickListener(null)
|
||||
binding.cardToolbar.setNavigationOnClickListener(null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (binding.cardToolbar.isSearchExpanded && binding.cardFrame.isVisible) {
|
||||
binding.cardToolbar.searchItem?.collapseActionView()
|
||||
return
|
||||
}
|
||||
backPress()
|
||||
}
|
||||
|
||||
open fun backPress() {
|
||||
val sheetController = router.backstack.lastOrNull()?.controller as? BottomSheetController
|
||||
if (if (router.backstackSize == 1) !(sheetController?.handleSheetBack() ?: false)
|
||||
else !router.handleBack()
|
||||
|
@ -759,12 +853,118 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
router.setRoot(controller.withFadeInTransaction().tag(id.toString()))
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
|
||||
val searchItem = menu?.findItem(R.id.action_search)
|
||||
if (currentToolbar == binding.cardToolbar) {
|
||||
override fun onPreparePanel(featureId: Int, view: View?, menu: Menu): Boolean {
|
||||
val prepare = super.onPreparePanel(featureId, view, menu)
|
||||
if (canShowFloatingToolbar(router.backstack.lastOrNull()?.controller)) {
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
searchItem?.isVisible = false
|
||||
}
|
||||
return super.onPrepareOptionsMenu(menu)
|
||||
setupSearchTBMenu(menu)
|
||||
return prepare
|
||||
}
|
||||
|
||||
fun setSearchTBMenuIfInvalid() = setupSearchTBMenu(binding.toolbar.menu)
|
||||
|
||||
private fun setupSearchTBMenu(menu: Menu?, showAnyway: Boolean = false) {
|
||||
val toolbar = binding.cardToolbar
|
||||
val currentItemsId = toolbar.menu.children.toList().map { it.itemId }
|
||||
val newMenuIds = menu?.children?.toList()?.map { it.itemId }.orEmpty()
|
||||
menu?.children?.toList()?.let { menuItems ->
|
||||
val searchActive = toolbar.isSearchExpanded
|
||||
menuItems.forEachIndexed { index, oldMenuItem ->
|
||||
if (oldMenuItem.itemId == R.id.action_search) return@forEachIndexed
|
||||
val isVisible = oldMenuItem.isVisible &&
|
||||
(currentToolbar == toolbar || !binding.appBar.useLargeToolbar) && (!searchActive || showAnyway)
|
||||
addOrUpdateMenuItem(oldMenuItem, toolbar.menu, isVisible, currentItemsId, index)
|
||||
}
|
||||
}
|
||||
toolbar.menu.children.toList().forEach {
|
||||
if (it.itemId != R.id.action_search && !newMenuIds.contains(it.itemId)) {
|
||||
toolbar.menu.removeItem(it.itemId)
|
||||
}
|
||||
}
|
||||
|
||||
// Done because sometimes ActionMenuItemViews have a width/height of 0 and never update
|
||||
val actionMenuView = toolbar.findChild<ActionMenuView>()
|
||||
if (binding.appBar.isVisible && toolbar.isVisible &&
|
||||
toolbar.width > 0 && actionMenuView?.children?.any { it.width == 0 } == true
|
||||
) {
|
||||
actionMenuView.children.forEach {
|
||||
if (it !is ActionMenuItemView) return@forEach
|
||||
it.updateLayoutParams<ViewGroup.LayoutParams> {
|
||||
width = actionButtonSize.first
|
||||
height = actionButtonSize.second
|
||||
}
|
||||
}
|
||||
actionMenuView.requestLayout()
|
||||
}
|
||||
|
||||
val controller = if (this::router.isInitialized) router.backstack.lastOrNull()?.controller else null
|
||||
if (canShowFloatingToolbar(controller)) {
|
||||
binding.toolbar.menu.removeItem(R.id.action_search)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addOrUpdateMenuItem(oldMenuItem: MenuItem, menu: Menu, isVisible: Boolean, currentItemsId: List<Int>, index: Int) {
|
||||
if (currentItemsId.contains(oldMenuItem.itemId)) {
|
||||
val newItem = menu.findItem(oldMenuItem.itemId) ?: return
|
||||
if (newItem.icon != oldMenuItem.icon) {
|
||||
newItem.icon = oldMenuItem.icon
|
||||
}
|
||||
if (newItem.isVisible != isVisible) {
|
||||
newItem.isVisible = isVisible
|
||||
}
|
||||
updateSubMenu(oldMenuItem, newItem)
|
||||
return
|
||||
}
|
||||
val menuItem = if (oldMenuItem.hasSubMenu()) {
|
||||
menu.addSubMenu(
|
||||
oldMenuItem.groupId,
|
||||
oldMenuItem.itemId,
|
||||
index,
|
||||
oldMenuItem.title
|
||||
).item
|
||||
} else {
|
||||
menu.add(
|
||||
oldMenuItem.groupId,
|
||||
oldMenuItem.itemId,
|
||||
index,
|
||||
oldMenuItem.title
|
||||
)
|
||||
}
|
||||
menuItem.isVisible = isVisible
|
||||
menuItem.actionView = oldMenuItem.actionView
|
||||
menuItem.icon = oldMenuItem.icon
|
||||
menuItem.isChecked = oldMenuItem.isChecked
|
||||
updateSubMenu(oldMenuItem, menuItem)
|
||||
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
private fun updateSubMenu(oldMenuItem: MenuItem, menuItem: MenuItem) {
|
||||
if (oldMenuItem.hasSubMenu()) {
|
||||
val oldSubMenu = oldMenuItem.subMenu
|
||||
val newMenuIds = oldSubMenu.children.toList().map { it.itemId }
|
||||
val currentItemsId = menuItem.subMenu.children.toList().map { it.itemId }
|
||||
var isExclusiveCheckable = false
|
||||
var isCheckable = false
|
||||
oldSubMenu.children.toList().forEachIndexed { index, oldSubMenuItem ->
|
||||
val isSubVisible = oldSubMenuItem.isVisible
|
||||
addOrUpdateMenuItem(oldSubMenuItem, menuItem.subMenu, isSubVisible, currentItemsId, index)
|
||||
if (!isExclusiveCheckable) {
|
||||
isExclusiveCheckable = (oldSubMenuItem as? MenuItemImpl)?.isExclusiveCheckable ?: false
|
||||
}
|
||||
if (!isCheckable) {
|
||||
isCheckable = oldSubMenuItem.isCheckable
|
||||
}
|
||||
}
|
||||
menuItem.subMenu.setGroupCheckable(oldSubMenu.children.first().groupId, isCheckable, isExclusiveCheckable)
|
||||
menuItem.subMenu.children.toList().forEach {
|
||||
if (!newMenuIds.contains(it.itemId)) {
|
||||
menuItem.subMenu.removeItem(it.itemId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
@ -836,8 +1036,8 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
setFloatingToolbar(canShowFloatingToolbar(to))
|
||||
val onRoot = router.backstackSize == 1
|
||||
val navIcon = if (onRoot) searchDrawable else backDrawable
|
||||
binding.toolbar.navigationIcon = navIcon
|
||||
binding.cardToolbar.navigationIcon = navIcon
|
||||
binding.toolbar.navigationIcon = if (onRoot) null else backDrawable
|
||||
binding.cardToolbar.navigationIcon = if (binding.appBar.useLargeToolbar) searchDrawable else navIcon
|
||||
binding.cardToolbar.subtitle = null
|
||||
|
||||
nav.visibility = if (!hideBottomNav) View.VISIBLE else nav.visibility
|
||||
|
@ -1023,6 +1223,8 @@ interface RootSearchInterface {
|
|||
}
|
||||
}
|
||||
|
||||
interface TabbedInterface
|
||||
|
||||
interface FloatingSearchInterface {
|
||||
fun searchTitle(title: String?): String? {
|
||||
if (this is Controller) {
|
||||
|
@ -1039,5 +1241,4 @@ interface BottomSheetController {
|
|||
fun hideSheet()
|
||||
fun toggleSheet()
|
||||
fun handleSheetBack(): Boolean
|
||||
fun sheetIsFullscreen(): Boolean
|
||||
}
|
||||
|
|
|
@ -9,8 +9,10 @@ import com.bluelinelabs.conductor.Controller
|
|||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
|
||||
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||
import eu.kanade.tachiyomi.ui.base.SmallToolbarInterface
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
||||
|
@ -38,9 +40,17 @@ class SearchActivity : MainActivity() {
|
|||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (binding.cardToolbar.isSearchExpanded && binding.cardFrame.isVisible) {
|
||||
binding.cardToolbar.searchItem?.collapseActionView()
|
||||
return
|
||||
}
|
||||
backPress()
|
||||
}
|
||||
|
||||
override fun backPress() {
|
||||
if (router.backstack.size <= 1 || !router.handleBack()) {
|
||||
SecureActivityDelegate.locked = true
|
||||
super.onBackPressed()
|
||||
super.backPress()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,9 +66,19 @@ class SearchActivity : MainActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun setFloatingToolbar(show: Boolean, solidBG: Boolean, changeBG: Boolean) {
|
||||
super.setFloatingToolbar(show, solidBG, changeBG)
|
||||
currentToolbar?.setNavigationOnClickListener { popToRoot() }
|
||||
override fun setFloatingToolbar(show: Boolean, solidBG: Boolean, changeBG: Boolean, showSearchAnyway: Boolean) {
|
||||
super.setFloatingToolbar(show, solidBG, changeBG, showSearchAnyway)
|
||||
binding.toolbar.setNavigationOnClickListener { popToRoot() }
|
||||
binding.cardToolbar.setNavigationOnClickListener {
|
||||
val rootSearchController = router.backstack.lastOrNull()?.controller
|
||||
if ((
|
||||
rootSearchController is RootSearchInterface ||
|
||||
(currentToolbar != binding.cardToolbar && binding.appBar.useLargeToolbar)
|
||||
) && rootSearchController !is SmallToolbarInterface
|
||||
) {
|
||||
binding.cardToolbar.menu.findItem(R.id.action_search)?.expandActionView()
|
||||
} else popToRoot()
|
||||
}
|
||||
}
|
||||
|
||||
private fun intentShouldGoBack() =
|
||||
|
@ -74,7 +94,7 @@ class SearchActivity : MainActivity() {
|
|||
return
|
||||
}
|
||||
setFloatingToolbar(canShowFloatingToolbar(to))
|
||||
binding.cardToolbar.navigationIcon = backDrawable
|
||||
binding.cardToolbar.navigationIcon = if (binding.appBar.useLargeToolbar) searchDrawable else backDrawable
|
||||
binding.toolbar.navigationIcon = backDrawable
|
||||
|
||||
nav.isVisible = false
|
||||
|
|
|
@ -58,6 +58,7 @@ import eu.kanade.tachiyomi.source.Source
|
|||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.base.MaterialMenuSheet
|
||||
import eu.kanade.tachiyomi.ui.base.SmallToolbarInterface
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
|
@ -97,15 +98,16 @@ import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
|
|||
import eu.kanade.tachiyomi.util.system.setCustomTitleAndMessage
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsetsCompat
|
||||
import eu.kanade.tachiyomi.util.view.getText
|
||||
import eu.kanade.tachiyomi.util.view.requestFilePermissionsSafe
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||
import eu.kanade.tachiyomi.util.view.setStyle
|
||||
import eu.kanade.tachiyomi.util.view.setTextColorAlpha
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import eu.kanade.tachiyomi.util.view.toolbarHeight
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.widget.LinearLayoutManagerAccurateOffset
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
@ -121,6 +123,7 @@ class MangaDetailsController :
|
|||
FlexibleAdapter.OnItemLongClickListener,
|
||||
ActionMode.Callback,
|
||||
MangaDetailsAdapter.MangaDetailsInterface,
|
||||
SmallToolbarInterface,
|
||||
FlexibleAdapter.OnItemMoveListener {
|
||||
|
||||
constructor(
|
||||
|
@ -353,7 +356,6 @@ class MangaDetailsController :
|
|||
presenter.onDestroy()
|
||||
adapter = null
|
||||
trackingBottomSheet = null
|
||||
updateToolbarTitleAlpha(1f)
|
||||
super.onDestroyView(view)
|
||||
}
|
||||
|
||||
|
@ -363,46 +365,39 @@ class MangaDetailsController :
|
|||
|
||||
binding.recycler.adapter = adapter
|
||||
adapter?.isSwipeEnabled = true
|
||||
binding.recycler.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.recycler.layoutManager = LinearLayoutManagerAccurateOffset(view.context)
|
||||
binding.recycler.addItemDecoration(
|
||||
MangaDetailsDivider(view.context)
|
||||
)
|
||||
binding.recycler.setHasFixedSize(true)
|
||||
val attrsArray = intArrayOf(R.attr.mainActionBarSize)
|
||||
val array = view.context.obtainStyledAttributes(attrsArray)
|
||||
val appbarHeight = array.getDimensionPixelSize(0, 0)
|
||||
array.recycle()
|
||||
val appbarHeight = activityBinding?.appBar?.attrToolbarHeight ?: 0
|
||||
val offset = 10.dpToPx
|
||||
binding.swipeRefresh.setDistanceToTriggerSync(70.dpToPx)
|
||||
|
||||
if (isTablet) {
|
||||
val tHeight = toolbarHeight.takeIf { it ?: 0 > 0 } ?: appbarHeight
|
||||
headerHeight = tHeight +
|
||||
(view.rootWindowInsetsCompat?.getInsets(systemBars())?.top ?: 0)
|
||||
val insetsCompat = view.rootWindowInsetsCompat ?: activityBinding?.root?.rootWindowInsetsCompat
|
||||
headerHeight = tHeight + (insetsCompat?.getInsets(systemBars())?.top ?: 0)
|
||||
binding.recycler.updatePaddingRelative(top = headerHeight + 4.dpToPx)
|
||||
binding.recycler.doOnApplyWindowInsetsCompat { _, insets, _ ->
|
||||
setInsets(insets, appbarHeight, offset)
|
||||
}
|
||||
} else {
|
||||
scrollViewWith(
|
||||
binding.recycler,
|
||||
padBottom = true,
|
||||
customPadding = true,
|
||||
afterInsets = { insets ->
|
||||
setInsets(insets, appbarHeight, offset)
|
||||
},
|
||||
liftOnScroll = {
|
||||
colorToolbar(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
scrollViewWith(
|
||||
binding.recycler,
|
||||
padBottom = true,
|
||||
customPadding = true,
|
||||
swipeRefreshLayout = binding.swipeRefresh,
|
||||
afterInsets = { insets ->
|
||||
setInsets(insets, appbarHeight, offset)
|
||||
},
|
||||
liftOnScroll = {
|
||||
colorToolbar(it)
|
||||
}
|
||||
)
|
||||
binding.recycler.addOnScrollListener(
|
||||
object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (!isTablet) {
|
||||
updateToolbarTitleAlpha(isScrollingDown = dy > 0)
|
||||
updateToolbarTitleAlpha(isScrollingDown = dy > 0 && !binding.root.context.isTablet())
|
||||
val atTop = !recyclerView.canScrollVertically(-1)
|
||||
val tY = getHeader()?.binding?.backdrop?.translationY ?: 0f
|
||||
getHeader()?.binding?.backdrop?.translationY = max(0f, tY + dy * 0.25f)
|
||||
|
@ -562,6 +557,8 @@ class MangaDetailsController :
|
|||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||
super.onChangeStarted(handler, type)
|
||||
if (type.isEnter) {
|
||||
activityBinding?.appBar?.y = 0f
|
||||
activityBinding?.appBar?.updateAppBarAfterY(binding.recycler)
|
||||
updateToolbarTitleAlpha(0f)
|
||||
setStatusBarAndToolbar()
|
||||
} else {
|
||||
|
@ -1118,25 +1115,20 @@ class MangaDetailsController :
|
|||
) return
|
||||
val scrolledList = binding.recycler
|
||||
val toolbarTextView = activityBinding?.toolbar?.toolbarTitle ?: return
|
||||
toolbarTextView.setTextColor(
|
||||
ColorUtils.setAlphaComponent(
|
||||
toolbarTextView.currentTextColor,
|
||||
(
|
||||
when {
|
||||
// Specific alpha provided
|
||||
alpha != null -> alpha
|
||||
val tbAlpha = when {
|
||||
isTablet -> 0f
|
||||
// Specific alpha provided
|
||||
alpha != null -> alpha
|
||||
|
||||
// First item isn't in view, full opacity
|
||||
((scrolledList.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() > 0) -> 1f
|
||||
((scrolledList.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition() == 0) -> 0f
|
||||
// First item isn't in view, full opacity
|
||||
((scrolledList.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() > 0) -> 1f
|
||||
((scrolledList.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition() == 0) -> 0f
|
||||
|
||||
// Based on scroll amount when first item is in view
|
||||
else -> (scrolledList.computeVerticalScrollOffset() - (20.dpToPx))
|
||||
.coerceIn(0, 255) / 255f
|
||||
} * 255
|
||||
).roundToInt()
|
||||
)
|
||||
)
|
||||
// Based on scroll amount when first item is in view
|
||||
else -> (scrolledList.computeVerticalScrollOffset() - (20.dpToPx))
|
||||
.coerceIn(0, 255) / 255f
|
||||
}
|
||||
toolbarTextView.setTextColorAlpha((tbAlpha * 255).roundToInt())
|
||||
}
|
||||
|
||||
private fun downloadChapters(choice: Int) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.migration
|
|||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
@ -13,7 +14,9 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
|||
import eu.kanade.tachiyomi.ui.migration.manga.design.PreMigrationController
|
||||
import eu.kanade.tachiyomi.util.system.await
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
import eu.kanade.tachiyomi.widget.LinearLayoutManagerAccurateOffset
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import rx.schedulers.Schedulers
|
||||
|
@ -45,8 +48,7 @@ class MigrationController :
|
|||
scrollViewWith(binding.migrationRecycler, padBottom = true)
|
||||
|
||||
adapter = FlexibleAdapter(null, this)
|
||||
binding.migrationRecycler.layoutManager =
|
||||
androidx.recyclerview.widget.LinearLayoutManager(view.context)
|
||||
binding.migrationRecycler.layoutManager = LinearLayoutManagerAccurateOffset(view.context)
|
||||
binding.migrationRecycler.adapter = adapter
|
||||
}
|
||||
|
||||
|
@ -83,10 +85,9 @@ class MigrationController :
|
|||
binding.migrationRecycler.adapter = adapter
|
||||
}
|
||||
adapter?.updateDataSet(state.mangaForSource, true)
|
||||
/*if (switching) launchUI {
|
||||
binding.migrationRecycler.alpha = 0f
|
||||
binding.migrationRecycler.animate().alpha(1f).setStartDelay(100).setDuration(200).start()
|
||||
}*/
|
||||
activityBinding?.appBar?.doOnNextLayout {
|
||||
binding.migrationRecycler.requestApplyInsets()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|||
import eu.kanade.tachiyomi.databinding.PreMigrationControllerBinding
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.base.SmallToolbarInterface
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
||||
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationListController
|
||||
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationProcedureConfig
|
||||
|
@ -33,8 +34,8 @@ import uy.kohesive.injekt.injectLazy
|
|||
|
||||
class PreMigrationController(bundle: Bundle? = null) :
|
||||
BaseController<PreMigrationControllerBinding>(bundle),
|
||||
FlexibleAdapter
|
||||
.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
SmallToolbarInterface,
|
||||
StartMigrationListener {
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
private val prefs: PreferencesHelper by injectLazy()
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
package eu.kanade.tachiyomi.ui.recents
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.RoundedCorner
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.core.view.updatePaddingRelative
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
|
@ -42,6 +45,7 @@ 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.main.TabbedInterface
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.recents.options.TabbedRecentsOptionsSheet
|
||||
|
@ -58,19 +62,24 @@ import eu.kanade.tachiyomi.util.view.activityBinding
|
|||
import eu.kanade.tachiyomi.util.view.collapse
|
||||
import eu.kanade.tachiyomi.util.view.compatToolTipText
|
||||
import eu.kanade.tachiyomi.util.view.expand
|
||||
import eu.kanade.tachiyomi.util.view.fullAppBarHeight
|
||||
import eu.kanade.tachiyomi.util.view.hide
|
||||
import eu.kanade.tachiyomi.util.view.isCollapsed
|
||||
import eu.kanade.tachiyomi.util.view.isControllerVisible
|
||||
import eu.kanade.tachiyomi.util.view.isExpanded
|
||||
import eu.kanade.tachiyomi.util.view.isHidden
|
||||
import eu.kanade.tachiyomi.util.view.moveRecyclerViewUp
|
||||
import eu.kanade.tachiyomi.util.view.requestFilePermissionsSafe
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||
import eu.kanade.tachiyomi.util.view.setStyle
|
||||
import eu.kanade.tachiyomi.util.view.smoothScrollToTop
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import eu.kanade.tachiyomi.util.view.updateGradiantBGRadius
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.widget.LinearLayoutManagerAccurateOffset
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Fragment that shows recently read manga.
|
||||
|
@ -84,6 +93,7 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
FlexibleAdapter.OnItemLongClickListener,
|
||||
FlexibleAdapter.OnItemMoveListener,
|
||||
FlexibleAdapter.EndlessScrollListener,
|
||||
TabbedInterface,
|
||||
RootSearchInterface,
|
||||
FloatingSearchInterface,
|
||||
BottomSheetController,
|
||||
|
@ -106,16 +116,24 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
private var lastChapterId: Long? = null
|
||||
private var showingDownloads = false
|
||||
var headerHeight = 0
|
||||
private var ogRadius = 0f
|
||||
private var deviceRadius = 0f
|
||||
|
||||
private var query = ""
|
||||
set(value) {
|
||||
field = value
|
||||
presenter.query = value
|
||||
}
|
||||
|
||||
override val mainRecycler: RecyclerView
|
||||
get() = binding.recycler
|
||||
|
||||
override fun getTitle(): String? {
|
||||
return if (showingDownloads) {
|
||||
resources?.getString(R.string.download_queue)
|
||||
} else searchTitle(
|
||||
return view?.context?.getString(R.string.recents)
|
||||
}
|
||||
|
||||
override fun getSearchTitle(): String? {
|
||||
return searchTitle(
|
||||
view?.context?.getString(
|
||||
when (presenter.viewType) {
|
||||
RecentsPresenter.VIEW_TYPE_ONLY_HISTORY -> R.string.history
|
||||
|
@ -137,10 +155,11 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
// Initialize adapter
|
||||
val isReturning = this::adapter.isInitialized
|
||||
adapter = RecentMangaAdapter(this)
|
||||
adapter.setPreferenceFlows()
|
||||
binding.recycler.adapter = adapter
|
||||
binding.recycler.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.recycler.layoutManager = LinearLayoutManagerAccurateOffset(view.context)
|
||||
binding.recycler.setHasFixedSize(true)
|
||||
binding.recycler.recycledViewPool.setMaxRecycledViews(0, 0)
|
||||
binding.recycler.addItemDecoration(
|
||||
|
@ -150,28 +169,36 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
adapter.itemTouchHelperCallback.setSwipeFlags(
|
||||
if (view.resources.isLTR) ItemTouchHelper.LEFT else ItemTouchHelper.RIGHT
|
||||
)
|
||||
val attrsArray = intArrayOf(R.attr.mainActionBarSize)
|
||||
val array = view.context.obtainStyledAttributes(attrsArray)
|
||||
val appBarHeight = array.getDimensionPixelSize(0, 0)
|
||||
array.recycle()
|
||||
binding.swipeRefresh.setStyle()
|
||||
scrollViewWith(
|
||||
binding.recycler,
|
||||
swipeRefreshLayout = binding.swipeRefresh,
|
||||
includeTabView = true,
|
||||
afterInsets = {
|
||||
val appBarHeight = activityBinding?.appBar?.attrToolbarHeight ?: 0
|
||||
headerHeight = it.getInsets(systemBars()).top + appBarHeight + 48.dpToPx
|
||||
binding.recycler.updatePaddingRelative(
|
||||
bottom = activityBinding?.bottomNav?.height ?: it.getInsets(systemBars()).bottom
|
||||
)
|
||||
binding.recentsEmptyView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = headerHeight
|
||||
bottomMargin =
|
||||
activityBinding?.bottomNav?.height ?: it.getInsets(systemBars()).bottom
|
||||
binding.downloadBottomSheet.sheetLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
height = appBarHeight + it.getInsets(systemBars()).top
|
||||
}
|
||||
val bigToolbarHeight = fullAppBarHeight ?: 0
|
||||
|
||||
binding.recentsEmptyView.updatePadding(
|
||||
top = bigToolbarHeight + it.getInsets(systemBars()).top,
|
||||
bottom = activityBinding?.bottomNav?.height ?: it.getInsets(systemBars()).bottom
|
||||
)
|
||||
binding.progress.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = (bigToolbarHeight + it.getInsets(systemBars()).top) / 2
|
||||
}
|
||||
if (activityBinding?.bottomNav == null) {
|
||||
setBottomPadding()
|
||||
}
|
||||
deviceRadius = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
it.toWindowInsets()?.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT)?.radius?.toFloat() ?: ogRadius
|
||||
} else {
|
||||
ogRadius
|
||||
}
|
||||
},
|
||||
onBottomNavUpdate = {
|
||||
setBottomPadding()
|
||||
|
@ -179,6 +206,10 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
)
|
||||
|
||||
viewScope.launchUI {
|
||||
if (!isReturning && adapter.itemCount == 0) {
|
||||
activityBinding?.appBar?.y = 0f
|
||||
activityBinding?.appBar?.lockYPos = true
|
||||
}
|
||||
val height =
|
||||
activityBinding?.bottomNav?.height ?: view.rootWindowInsetsCompat?.getInsets(
|
||||
systemBars()
|
||||
|
@ -188,21 +219,30 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
bottom = height
|
||||
)
|
||||
val isExpanded = binding.downloadBottomSheet.root.sheetBehavior.isExpanded()
|
||||
activityBinding?.tabsFrameLayout?.isVisible = !isExpanded
|
||||
if (isExpanded) {
|
||||
(activity as? MainActivity)?.showTabBar(show = false, animate = false)
|
||||
setRecentsAppBarBG(1f)
|
||||
}
|
||||
binding.downloadBottomSheet.dlRecycler.alpha = isExpanded.toInt().toFloat()
|
||||
binding.downloadBottomSheet.sheetLayout.backgroundTintList = ColorStateList.valueOf(
|
||||
ColorUtils.blendARGB(
|
||||
view.context.getResourceColor(R.attr.colorPrimaryVariant),
|
||||
view.context.getResourceColor(R.attr.background),
|
||||
isExpanded.toInt().toFloat()
|
||||
)
|
||||
)
|
||||
binding.downloadBottomSheet.root.backgroundTintList =
|
||||
binding.downloadBottomSheet.sheetLayout.backgroundTintList
|
||||
binding.downloadBottomSheet.titleText.alpha = (!isExpanded).toInt().toFloat()
|
||||
binding.downloadBottomSheet.sheetToolbar.alpha = isExpanded.toInt().toFloat()
|
||||
if (binding.downloadBottomSheet.root.sheetBehavior.isCollapsed()) {
|
||||
if (hasQueue()) {
|
||||
binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.isHideable =
|
||||
false
|
||||
} else {
|
||||
binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.isHideable =
|
||||
true
|
||||
binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.state =
|
||||
BottomSheetBehavior.STATE_HIDDEN
|
||||
}
|
||||
} else if (binding.downloadBottomSheet.root.sheetBehavior.isHidden()) {
|
||||
if (!hasQueue()) {
|
||||
binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.skipCollapsed =
|
||||
true
|
||||
} else {
|
||||
binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.skipCollapsed =
|
||||
false
|
||||
binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.state =
|
||||
BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
}
|
||||
updateTitleAndMenu()
|
||||
}
|
||||
|
||||
|
@ -223,58 +263,37 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
// Doing some fun math to hide the tab bar just as the title text of the
|
||||
// dl sheet is under the toolbar
|
||||
val cap = height * (1 / 12600f) + 479f / 700
|
||||
val elValue = max(
|
||||
max(0f, (progress - cap)) / (1 - cap),
|
||||
if (binding.recycler.canScrollVertically(-1)) 1f else 0f
|
||||
).coerceIn(0f, 1f)
|
||||
setRecentsAppBarBG(elValue)
|
||||
binding.downloadBottomSheet.sheetLayout.alpha = 1 - max(0f, progress / cap)
|
||||
binding.downloadBottomSheet.titleText.alpha = 1 - max(0f, progress / cap)
|
||||
binding.downloadBottomSheet.sheetToolbar.alpha = max(0f, progress / cap)
|
||||
binding.downloadBottomSheet.pill.alpha = binding.downloadBottomSheet.titleText.alpha * 0.25f
|
||||
binding.downloadBottomSheet.dlRecycler.alpha = progress * 10
|
||||
binding.downloadBottomSheet.sheetLayout.backgroundTintList =
|
||||
ColorStateList.valueOf(
|
||||
ColorUtils.blendARGB(
|
||||
view.context.getResourceColor(R.attr.colorPrimaryVariant),
|
||||
view.context.getResourceColor(R.attr.background),
|
||||
(progress * 2f).coerceIn(0f, 1f)
|
||||
)
|
||||
)
|
||||
val oldShow = showingDownloads
|
||||
showingDownloads = progress > 0.92f
|
||||
if (router.backstack.lastOrNull()?.controller != this@RecentsController) {
|
||||
if (!isControllerVisible) {
|
||||
return
|
||||
}
|
||||
binding.downloadBottomSheet.root.backgroundTintList =
|
||||
binding.downloadBottomSheet.sheetLayout.backgroundTintList
|
||||
activityBinding?.appBar?.y = max(
|
||||
activityBinding!!.appBar.y,
|
||||
-headerHeight * (1 - progress)
|
||||
)
|
||||
activityBinding?.tabsFrameLayout?.let { tabs ->
|
||||
tabs.alpha = 1 - max(0f, progress / cap)
|
||||
if (tabs.alpha <= 0 && tabs.isVisible) {
|
||||
tabs.isVisible = false
|
||||
} else if (tabs.alpha > 0 && !tabs.isVisible) {
|
||||
tabs.isVisible = true
|
||||
}
|
||||
if (isControllerVisible) {
|
||||
activityBinding?.appBar?.alpha = (1 - progress * 3) + 0.5f
|
||||
}
|
||||
binding.downloadBottomSheet.root.updateGradiantBGRadius(
|
||||
ogRadius,
|
||||
deviceRadius,
|
||||
progress,
|
||||
binding.downloadBottomSheet.sheetLayout
|
||||
)
|
||||
if (oldShow != showingDownloads) {
|
||||
updateTitleAndMenu()
|
||||
activity?.invalidateOptionsMenu()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStateChanged(p0: View, state: Int) {
|
||||
if (this@RecentsController.view == null) return
|
||||
if (state == BottomSheetBehavior.STATE_EXPANDED) activityBinding?.appBar?.y = 0f
|
||||
if (state == BottomSheetBehavior.STATE_EXPANDED || state == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||
binding.downloadBottomSheet.sheetLayout.alpha =
|
||||
if (state == BottomSheetBehavior.STATE_COLLAPSED) 1f else 0f
|
||||
showingDownloads = state == BottomSheetBehavior.STATE_EXPANDED
|
||||
updateTitleAndMenu()
|
||||
activity?.invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
if (router.backstack.lastOrNull()?.controller == this@RecentsController) {
|
||||
if (isControllerVisible) {
|
||||
activityBinding?.tabsFrameLayout?.isVisible =
|
||||
state != BottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
|
@ -349,7 +368,8 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
LibraryUpdateService.start(view.context)
|
||||
}
|
||||
}
|
||||
|
||||
ogRadius = view.resources.getDimension(R.dimen.rounded_radius)
|
||||
setSheetToolbar()
|
||||
if (showingDownloads) {
|
||||
binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.expand()
|
||||
}
|
||||
|
@ -359,27 +379,28 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
binding.downloadBottomSheet.root.sheetBehavior?.isGestureInsetBottomIgnored = true
|
||||
}
|
||||
|
||||
fun updateTitleAndMenu() {
|
||||
if (router.backstack.lastOrNull()?.controller == this) {
|
||||
val activity = (activity as? MainActivity) ?: return
|
||||
activity.setFloatingToolbar(!showingDownloads, true)
|
||||
if (showingDownloads) {
|
||||
setRecentsAppBarBG(1f)
|
||||
private fun setSheetToolbar() {
|
||||
binding.downloadBottomSheet.sheetToolbar.title = view?.context?.getString(R.string.download_queue)
|
||||
binding.downloadBottomSheet.sheetToolbar.overflowIcon?.setTint(view?.context?.getResourceColor(R.attr.actionBarTintColor) ?: Color.BLACK)
|
||||
binding.downloadBottomSheet.sheetToolbar.setOnMenuItemClickListener { item ->
|
||||
return@setOnMenuItemClickListener binding.downloadBottomSheet.dlBottomSheet.onOptionsItemSelected(item)
|
||||
}
|
||||
binding.downloadBottomSheet.sheetToolbar.setNavigationOnClickListener {
|
||||
if (binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.isHideable == true) {
|
||||
binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.hide()
|
||||
} else {
|
||||
binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.collapse()
|
||||
}
|
||||
setTitle()
|
||||
}
|
||||
}
|
||||
|
||||
fun setRecentsAppBarBG(value: Float) {
|
||||
val context = view?.context ?: return
|
||||
val color = ColorUtils.blendARGB(
|
||||
context.getResourceColor(R.attr.colorSurface),
|
||||
context.getResourceColor(R.attr.colorPrimaryVariant),
|
||||
value
|
||||
)
|
||||
activityBinding?.appBar?.setBackgroundColor(color)
|
||||
activity?.window?.statusBarColor =
|
||||
ColorUtils.setAlphaComponent(color, (0.87f * 255).roundToInt())
|
||||
fun updateTitleAndMenu() {
|
||||
if (isControllerVisible) {
|
||||
val activity = (activity as? MainActivity) ?: return
|
||||
activityBinding?.appBar?.isInvisible = showingDownloads
|
||||
(activity as? MainActivity)?.setStatusBarColorTransparent(showingDownloads)
|
||||
setTitle()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setBottomPadding() {
|
||||
|
@ -454,11 +475,11 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
setBottomPadding()
|
||||
binding.downloadBottomSheet.dlBottomSheet.update()
|
||||
|
||||
if (BuildConfig.DEBUG && query.isBlank()) {
|
||||
if (BuildConfig.DEBUG && query.isBlank() && isControllerVisible) {
|
||||
val searchItem =
|
||||
(activity as? MainActivity)?.binding?.cardToolbar?.menu?.findItem(R.id.action_search)
|
||||
val searchView = searchItem?.actionView as? SearchView ?: return
|
||||
if (router.backstack.lastOrNull()?.controller != this) return
|
||||
if (!isControllerVisible) return
|
||||
setOnQueryTextChangeListener(searchView) {
|
||||
if (query != it) {
|
||||
query = it ?: return@setOnQueryTextChangeListener false
|
||||
|
@ -496,6 +517,9 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
adapter.removeAllScrollableHeaders()
|
||||
adapter.updateItems(recents)
|
||||
adapter.onLoadMoreComplete(null)
|
||||
if (isControllerVisible) {
|
||||
activityBinding?.appBar?.lockYPos = false
|
||||
}
|
||||
if (!hasNewItems || presenter.viewType == RecentsPresenter.VIEW_TYPE_GROUP_ALL ||
|
||||
recents.isEmpty()
|
||||
) {
|
||||
|
@ -517,8 +541,14 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
} else {
|
||||
binding.recentsEmptyView.hide()
|
||||
}
|
||||
val isSearchExpanded = activityBinding?.cardToolbar?.isSearchExpanded == true
|
||||
if (shouldMoveToTop) {
|
||||
binding.recycler.scrollToPosition(0)
|
||||
if (isSearchExpanded) {
|
||||
moveRecyclerViewUp(scrollUpAnyway = true)
|
||||
} else {
|
||||
(binding.recycler.layoutManager as? LinearLayoutManager)
|
||||
?.scrollToPositionWithOffset(0, 0)
|
||||
}
|
||||
}
|
||||
if (lastChapterId != null) {
|
||||
refreshItem(lastChapterId ?: 0L)
|
||||
|
@ -675,38 +705,25 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
override fun isSearching() = query.isNotEmpty()
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
if (onRoot) (activity as? MainActivity)?.setDismissIcon(showingDownloads)
|
||||
if (showingDownloads) {
|
||||
inflater.inflate(R.menu.download_queue, menu)
|
||||
} else {
|
||||
inflater.inflate(R.menu.recents, menu)
|
||||
inflater.inflate(R.menu.recents, menu)
|
||||
|
||||
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(query, true)
|
||||
searchView.clearFocus()
|
||||
}
|
||||
setOnQueryTextChangeListener(searchView) {
|
||||
if (query != it) {
|
||||
query = it ?: return@setOnQueryTextChangeListener false
|
||||
// loadNoMore()
|
||||
resetProgressItem()
|
||||
refresh()
|
||||
}
|
||||
true
|
||||
}
|
||||
searchItem.fixExpandInvalidate()
|
||||
hideItemsIfExpanded(searchItem, menu)
|
||||
val searchItem = activityBinding?.cardToolbar?.searchItem
|
||||
val searchView = activityBinding?.cardToolbar?.searchView
|
||||
activityBinding?.cardToolbar?.setQueryHint(view?.context?.getString(R.string.search_recents), !isSearching())
|
||||
if (isSearching()) {
|
||||
searchItem?.expandActionView()
|
||||
searchView?.setQuery(query, true)
|
||||
searchView?.clearFocus()
|
||||
}
|
||||
setOnQueryTextChangeListener(activityBinding?.cardToolbar?.searchView) {
|
||||
if (query != it) {
|
||||
query = it ?: return@setOnQueryTextChangeListener false
|
||||
// loadNoMore()
|
||||
resetProgressItem()
|
||||
refresh()
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
super.onPrepareOptionsMenu(menu)
|
||||
if (showingDownloads) binding.downloadBottomSheet.dlBottomSheet.prepareMenu(menu)
|
||||
}
|
||||
|
||||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||
|
@ -786,8 +803,6 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
else binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.expand()
|
||||
}
|
||||
|
||||
override fun sheetIsFullscreen(): Boolean = binding.downloadBottomSheet.dlBottomSheet.sheetBehavior.isExpanded()
|
||||
|
||||
override fun expandSearch() {
|
||||
if (showingDownloads) {
|
||||
binding.downloadBottomSheet.dlBottomSheet.dismiss()
|
||||
|
@ -797,9 +812,6 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (showingDownloads) {
|
||||
return binding.downloadBottomSheet.dlBottomSheet.onOptionsItemSelected(item)
|
||||
}
|
||||
when (item.itemId) {
|
||||
R.id.display_options -> {
|
||||
displaySheet = TabbedRecentsOptionsSheet(
|
||||
|
|
|
@ -135,8 +135,7 @@ class RecentsPresenter(
|
|||
}
|
||||
val viewType = customViewType ?: viewType
|
||||
|
||||
val showRead = ((preferences.showReadInAllRecents().get() || query.isNotEmpty()) && !limit) ||
|
||||
includeReadAnyway == true
|
||||
val showRead = ((preferences.showReadInAllRecents().get() || query.isNotEmpty()) && !limit) || includeReadAnyway
|
||||
val isUngrouped = viewType > VIEW_TYPE_GROUP_ALL || query.isNotEmpty()
|
||||
val groupChaptersUpdates = preferences.groupChaptersUpdates().get()
|
||||
val groupChaptersHistory = preferences.groupChaptersHistory().get()
|
||||
|
@ -195,6 +194,10 @@ class RecentsPresenter(
|
|||
else -> emptyList()
|
||||
}
|
||||
|
||||
if (cReading.size < ENDLESS_LIMIT) {
|
||||
finished = true
|
||||
}
|
||||
|
||||
if (!isCustom &&
|
||||
(pageOffset == 0 || updatePageCount)
|
||||
) {
|
||||
|
@ -284,7 +287,7 @@ class RecentsPresenter(
|
|||
.compareTo(d1)
|
||||
}
|
||||
val byDay =
|
||||
pairs.groupByTo(map, { getMapKey(it.first.history.last_read) })
|
||||
pairs.groupByTo(map) { getMapKey(it.first.history.last_read) }
|
||||
byDay.flatMap {
|
||||
val dateItem = DateItem(it.key, true)
|
||||
it.value
|
||||
|
|
|
@ -22,6 +22,7 @@ import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.isAuthenticationSupport
|
|||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.startAuthentication
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.widget.preference.AdaptiveTitlePreferenceCategory
|
||||
import eu.kanade.tachiyomi.widget.preference.IntListMatPreference
|
||||
import eu.kanade.tachiyomi.widget.preference.ListMatPreference
|
||||
import eu.kanade.tachiyomi.widget.preference.MultiListMatPreference
|
||||
|
@ -103,12 +104,7 @@ inline fun PreferenceGroup.triStateListPreference(
|
|||
}
|
||||
|
||||
inline fun PreferenceScreen.preferenceCategory(block: (@DSL PreferenceCategory).() -> Unit): PreferenceCategory {
|
||||
return addThenInit(
|
||||
PreferenceCategory(context).apply {
|
||||
isIconSpaceReserved = false
|
||||
},
|
||||
block
|
||||
)
|
||||
return addThenInit(AdaptiveTitlePreferenceCategory(context), block)
|
||||
}
|
||||
|
||||
inline fun PreferenceScreen.switchPreference(block: (@DSL SwitchPreferenceCompat).() -> Unit): SwitchPreferenceCompat {
|
||||
|
|
|
@ -4,6 +4,8 @@ import android.annotation.SuppressLint
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
|
||||
|
@ -13,6 +15,8 @@ import eu.kanade.tachiyomi.util.system.appDelegateNightMode
|
|||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.getPrefTheme
|
||||
import eu.kanade.tachiyomi.util.system.isInNightMode
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.moveRecyclerViewUp
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlin.math.max
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
||||
|
@ -82,6 +86,35 @@ class SettingsAppearanceController : SettingsController() {
|
|||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
switchPreference {
|
||||
key = Keys.useLargeToolbar
|
||||
titleRes = R.string.expanded_toolbar
|
||||
summaryRes = R.string.show_larger_toolbar
|
||||
defaultValue = true
|
||||
|
||||
onChange {
|
||||
val useLarge = it as Boolean
|
||||
activityBinding?.appBar?.setToolbarModeBy(this@SettingsAppearanceController, !useLarge)
|
||||
activityBinding?.appBar?.hideBigView(!useLarge, !useLarge)
|
||||
activityBinding?.toolbar?.alpha = 1f
|
||||
activityBinding?.toolbar?.translationY = 0f
|
||||
activityBinding?.toolbar?.isVisible = true
|
||||
activityBinding?.appBar?.doOnNextLayout {
|
||||
listView.requestApplyInsets()
|
||||
listView.post {
|
||||
if (useLarge) {
|
||||
moveRecyclerViewUp(true)
|
||||
} else {
|
||||
activityBinding?.appBar?.updateAppBarAfterY(listView)
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.details_page
|
||||
switchPreference {
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.os.Bundle
|
|||
import android.util.TypedValue
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
@ -20,8 +21,10 @@ 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.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
import eu.kanade.tachiyomi.widget.LinearLayoutManagerAccurateOffset
|
||||
import kotlinx.coroutines.MainScope
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
|
@ -39,6 +42,11 @@ abstract class SettingsController : PreferenceController() {
|
|||
var untilDestroySubscriptions = CompositeSubscription()
|
||||
private set
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
listView.layoutManager = LinearLayoutManagerAccurateOffset(view?.context)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View {
|
||||
if (untilDestroySubscriptions.isUnsubscribed) {
|
||||
untilDestroySubscriptions = CompositeSubscription()
|
||||
|
@ -80,6 +88,9 @@ abstract class SettingsController : PreferenceController() {
|
|||
|
||||
abstract fun setupPreferenceScreen(screen: PreferenceScreen): PreferenceScreen
|
||||
|
||||
open fun onActionViewExpand(item: MenuItem?) { }
|
||||
open fun onActionViewCollapse(item: MenuItem?) { }
|
||||
|
||||
private fun getThemedContext(): Context {
|
||||
val tv = TypedValue()
|
||||
activity!!.theme.resolveAttribute(R.attr.preferenceTheme, tv, true)
|
||||
|
@ -97,11 +108,12 @@ abstract class SettingsController : PreferenceController() {
|
|||
}
|
||||
}
|
||||
|
||||
open fun getTitle(): String? {
|
||||
if (this is FloatingSearchInterface) {
|
||||
return searchTitle(preferenceScreen?.title?.toString()?.lowercase(Locale.ROOT))
|
||||
}
|
||||
return preferenceScreen?.title?.toString()
|
||||
open fun getTitle(): String? = preferenceScreen?.title?.toString()
|
||||
|
||||
open fun getSearchTitle(): String? {
|
||||
return if (this is FloatingSearchInterface) {
|
||||
searchTitle(preferenceScreen?.title?.toString()?.lowercase(Locale.ROOT))
|
||||
} else null
|
||||
}
|
||||
|
||||
fun setTitle() {
|
||||
|
@ -113,7 +125,8 @@ abstract class SettingsController : PreferenceController() {
|
|||
parentController = parentController.parentController
|
||||
}
|
||||
|
||||
(activity as? AppCompatActivity)?.supportActionBar?.title = getTitle()
|
||||
(activity as? AppCompatActivity)?.title = getTitle()
|
||||
(activity as? MainActivity)?.searchTitle = getSearchTitle()
|
||||
}
|
||||
|
||||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||
|
|
|
@ -174,18 +174,6 @@ class SettingsLibraryController : SettingsController() {
|
|||
allSelectionRes = R.string.all
|
||||
}
|
||||
|
||||
intListPreference(activity) {
|
||||
key = Keys.updateOnRefresh
|
||||
titleRes = R.string.categories_on_manual
|
||||
|
||||
entriesRes = arrayOf(
|
||||
R.string.first_category,
|
||||
R.string.categories_in_global_update
|
||||
)
|
||||
entryRange = 0..1
|
||||
defaultValue = -1
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
key = Keys.refreshCoversToo
|
||||
titleRes = R.string.auto_refresh_covers
|
||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.ui.setting
|
|||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
|
@ -11,6 +10,7 @@ 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.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
|
||||
class SettingsMainController : SettingsController(), FloatingSearchInterface {
|
||||
|
@ -88,29 +88,14 @@ class SettingsMainController : SettingsController(), FloatingSearchInterface {
|
|||
}
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.settings_main, menu)
|
||||
|
||||
// Initialize search option.
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
searchView.maxWidth = Int.MAX_VALUE
|
||||
|
||||
// Change hint to show global search.
|
||||
searchView.queryHint = applicationContext?.getString(R.string.search_settings)
|
||||
activityBinding?.cardToolbar?.searchQueryHint = applicationContext?.getString(R.string.search_settings)
|
||||
}
|
||||
|
||||
searchItem.setOnActionExpandListener(
|
||||
object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
|
||||
SettingsSearchController.lastSearch = "" // reset saved search query
|
||||
router.pushController(
|
||||
RouterTransaction.with(SettingsSearchController())
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
override fun onActionViewExpand(item: MenuItem?) {
|
||||
SettingsSearchController.lastSearch = "" // reset saved search query
|
||||
router.pushController(
|
||||
RouterTransaction.with(SettingsSearchController())
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,13 +15,18 @@ import eu.kanade.tachiyomi.data.preference.plusAssign
|
|||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.icon
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.isControllerVisible
|
||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||
import eu.kanade.tachiyomi.widget.preference.SwitchPreferenceCategory
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.TreeMap
|
||||
|
||||
class SettingsSourcesController : SettingsController() {
|
||||
class SettingsSourcesController : SettingsController(), FloatingSearchInterface {
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
@ -35,6 +40,10 @@ class SettingsSourcesController : SettingsController() {
|
|||
private var sourcesByLang: TreeMap<String, MutableList<HttpSource>> = TreeMap()
|
||||
private var sorting = SourcesSort.Alpha
|
||||
|
||||
override fun getSearchTitle(): String? {
|
||||
return view?.context?.getString(R.string.search)
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.filter
|
||||
sorting = SourcesSort.from(preferences.sourceSorting().get()) ?: SourcesSort.Alpha
|
||||
|
@ -43,7 +52,7 @@ class SettingsSourcesController : SettingsController() {
|
|||
val activeLangsCodes = preferences.enabledLanguages().get()
|
||||
|
||||
// Get a map of sources grouped by language.
|
||||
sourcesByLang = onlineSources.groupByTo(TreeMap(), { it.lang })
|
||||
sourcesByLang = onlineSources.groupByTo(TreeMap()) { it.lang }
|
||||
|
||||
// Order first by active languages, then inactive ones
|
||||
orderedLangs = sourcesByLang.keys.filter { it in activeLangsCodes } + sourcesByLang.keys
|
||||
|
@ -163,26 +172,43 @@ class SettingsSourcesController : SettingsController() {
|
|||
if (sorting == SourcesSort.Alpha) menu.findItem(R.id.action_sort_alpha).isChecked = true
|
||||
else menu.findItem(R.id.action_sort_enabled).isChecked = true
|
||||
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
searchView.maxWidth = Int.MAX_VALUE
|
||||
|
||||
if (query.isNotEmpty()) {
|
||||
searchItem.expandActionView()
|
||||
searchView.setQuery(query, true)
|
||||
searchView.clearFocus()
|
||||
val useSearchTB = showFloatingBar()
|
||||
val searchItem = if (useSearchTB) activityBinding?.cardToolbar?.searchItem
|
||||
else (menu.findItem(R.id.action_search))
|
||||
val searchView = if (useSearchTB) activityBinding?.cardToolbar?.searchView
|
||||
else searchItem?.actionView as? SearchView
|
||||
if (!useSearchTB) {
|
||||
searchView?.maxWidth = Int.MAX_VALUE
|
||||
}
|
||||
|
||||
searchView.queryTextChanges().filter { router.backstack.lastOrNull()?.controller == this }
|
||||
.subscribeUntilDestroy {
|
||||
activityBinding?.cardToolbar?.setQueryHint(getSearchTitle(), query.isEmpty())
|
||||
|
||||
if (query.isNotEmpty()) {
|
||||
searchItem?.expandActionView()
|
||||
searchView?.setQuery(query, true)
|
||||
searchView?.clearFocus()
|
||||
}
|
||||
|
||||
setOnQueryTextChangeListener(activityBinding?.cardToolbar?.searchView) {
|
||||
query = it ?: ""
|
||||
drawSources()
|
||||
true
|
||||
}
|
||||
|
||||
searchView?.queryTextChanges()?.filter { isControllerVisible }
|
||||
?.subscribeUntilDestroy {
|
||||
query = it.toString()
|
||||
drawSources()
|
||||
}
|
||||
|
||||
// Fixes problem with the overflow icon showing up in lieu of search
|
||||
searchItem.fixExpand(onExpand = { invalidateMenuOnExpand() })
|
||||
if (useSearchTB) {
|
||||
// Fixes problem with the overflow icon showing up in lieu of search
|
||||
searchItem?.fixExpand(onExpand = { invalidateMenuOnExpand() })
|
||||
}
|
||||
}
|
||||
|
||||
override fun showFloatingBar() = activityBinding?.appBar?.useLargeToolbar == true
|
||||
|
||||
var expandActionViewFromInteraction = false
|
||||
private fun MenuItem.fixExpand(onExpand: ((MenuItem) -> Boolean)? = null, onCollapse: ((MenuItem) -> Boolean)? = null) {
|
||||
setOnActionExpandListener(
|
||||
|
@ -248,6 +274,14 @@ class SettingsSourcesController : SettingsController() {
|
|||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
item.isChecked = true
|
||||
(activity as? MainActivity)?.let {
|
||||
val otherTB = if (it.currentToolbar == it.binding.cardToolbar) {
|
||||
it.binding.toolbar
|
||||
} else {
|
||||
it.binding.cardToolbar
|
||||
}
|
||||
otherTB.menu.findItem(item.itemId).isChecked = true
|
||||
}
|
||||
preferences.sourceSorting().set(sorting.value)
|
||||
drawSources()
|
||||
return true
|
||||
|
|
|
@ -10,9 +10,11 @@ import androidx.appcompat.widget.SearchView
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.SettingsSearchControllerBinding
|
||||
import eu.kanade.tachiyomi.ui.base.SmallToolbarInterface
|
||||
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.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.liftAppbarWith
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
|
||||
|
@ -23,13 +25,14 @@ import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
|||
class SettingsSearchController :
|
||||
NucleusController<SettingsSearchControllerBinding, SettingsSearchPresenter>(),
|
||||
FloatingSearchInterface,
|
||||
SmallToolbarInterface,
|
||||
SettingsSearchAdapter.OnTitleClickListener {
|
||||
|
||||
/**
|
||||
* Adapter containing search results grouped by lang.
|
||||
*/
|
||||
private var adapter: SettingsSearchAdapter? = null
|
||||
private lateinit var searchView: SearchView
|
||||
private var searchView: SearchView? = null
|
||||
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
|
@ -60,31 +63,15 @@ class SettingsSearchController :
|
|||
// Inflate menu.
|
||||
inflater.inflate(R.menu.settings_main, menu)
|
||||
|
||||
// Initialize search menu
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
searchView = searchItem.actionView as SearchView
|
||||
searchView.maxWidth = Int.MAX_VALUE
|
||||
val searchItem = activityBinding?.cardToolbar?.searchItem
|
||||
searchView = activityBinding?.cardToolbar?.searchView
|
||||
|
||||
// Change hint to show "search settings."
|
||||
searchView.queryHint = applicationContext?.getString(R.string.search_settings)
|
||||
activityBinding?.cardToolbar?.setQueryHint(applicationContext?.getString(R.string.search_settings), false)
|
||||
|
||||
searchItem.expandActionView()
|
||||
searchItem?.expandActionView()
|
||||
setItems(getResultSet())
|
||||
|
||||
searchItem.setOnActionExpandListener(
|
||||
object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
|
||||
router.popCurrentController()
|
||||
return false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
searchView.setOnQueryTextListener(
|
||||
searchView?.setOnQueryTextListener(
|
||||
object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
setItems(getResultSet(query))
|
||||
|
@ -101,7 +88,11 @@ class SettingsSearchController :
|
|||
}
|
||||
)
|
||||
|
||||
searchView.setQuery(lastSearch, true)
|
||||
searchView?.setQuery(lastSearch, true)
|
||||
}
|
||||
|
||||
override fun onActionViewCollapse(item: MenuItem?) {
|
||||
router.popCurrentController()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
|
@ -159,7 +150,7 @@ class SettingsSearchController :
|
|||
* Opens a catalogue with the given search.
|
||||
*/
|
||||
override fun onTitleClick(ctrl: SettingsController) {
|
||||
searchView.query.let {
|
||||
searchView?.query.let {
|
||||
lastSearch = it.toString()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
package eu.kanade.tachiyomi.ui.source
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.RoundedCorner
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePaddingRelative
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -23,6 +26,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|||
import com.google.android.material.snackbar.Snackbar
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.databinding.BrowseControllerBinding
|
||||
|
@ -47,22 +51,25 @@ import eu.kanade.tachiyomi.util.system.openInBrowser
|
|||
import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
|
||||
import eu.kanade.tachiyomi.util.system.spToPx
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.checkHeightThen
|
||||
import eu.kanade.tachiyomi.util.view.collapse
|
||||
import eu.kanade.tachiyomi.util.view.expand
|
||||
import eu.kanade.tachiyomi.util.view.isCollapsed
|
||||
import eu.kanade.tachiyomi.util.view.isExpanded
|
||||
import eu.kanade.tachiyomi.util.view.isControllerVisible
|
||||
import eu.kanade.tachiyomi.util.view.requestFilePermissionsSafe
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import eu.kanade.tachiyomi.util.view.toolbarHeight
|
||||
import eu.kanade.tachiyomi.util.view.updateGradiantBGRadius
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.widget.LinearLayoutManagerAccurateOffset
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* This controller shows and manages the different catalogues enabled by the user.
|
||||
|
@ -96,6 +103,12 @@ class BrowseController :
|
|||
|
||||
var snackbar: Snackbar? = null
|
||||
|
||||
private var ogRadius = 0f
|
||||
private var deviceRadius = 0f
|
||||
|
||||
override val mainRecycler: RecyclerView
|
||||
get() = binding.sourceRecycler
|
||||
|
||||
/**
|
||||
* Called when controller is initialized.
|
||||
*/
|
||||
|
@ -103,15 +116,10 @@ class BrowseController :
|
|||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun getTitle(): String? {
|
||||
return if (showingExtensions) {
|
||||
view?.context?.getString(
|
||||
when (binding.bottomSheet.tabs.selectedTabPosition) {
|
||||
0 -> R.string.extensions
|
||||
else -> R.string.source_migration
|
||||
}
|
||||
)
|
||||
} else searchTitle(view?.context?.getString(R.string.sources)?.lowercase(Locale.ROOT))
|
||||
override fun getTitle(): String? = view?.context?.getString(R.string.browse)
|
||||
|
||||
override fun getSearchTitle(): String? {
|
||||
return searchTitle(view?.context?.getString(R.string.sources)?.lowercase(Locale.ROOT))
|
||||
}
|
||||
|
||||
val presenter = SourcePresenter(this)
|
||||
|
@ -120,36 +128,37 @@ class BrowseController :
|
|||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
val isReturning = adapter != null
|
||||
adapter = SourceAdapter(this)
|
||||
// Create binding.sourceRecycler and set adapter.
|
||||
binding.sourceRecycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context)
|
||||
binding.sourceRecycler.layoutManager = LinearLayoutManagerAccurateOffset(view.context)
|
||||
|
||||
binding.sourceRecycler.adapter = adapter
|
||||
adapter?.isSwipeEnabled = true
|
||||
adapter?.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
|
||||
val attrsArray = intArrayOf(R.attr.mainActionBarSize)
|
||||
val array = view.context.obtainStyledAttributes(attrsArray)
|
||||
val appBarHeight = array.getDimensionPixelSize(0, 0)
|
||||
array.recycle()
|
||||
scrollViewWith(
|
||||
binding.sourceRecycler,
|
||||
customPadding = true,
|
||||
afterInsets = {
|
||||
headerHeight = it.getInsets(systemBars()).top + appBarHeight
|
||||
headerHeight = binding.sourceRecycler.paddingTop
|
||||
binding.sourceRecycler.updatePaddingRelative(
|
||||
top = headerHeight,
|
||||
bottom = (activityBinding?.bottomNav?.height ?: it.getBottomGestureInsets()) + 58.spToPx
|
||||
)
|
||||
if (activityBinding?.bottomNav == null) {
|
||||
setBottomPadding()
|
||||
}
|
||||
deviceRadius = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
it.toWindowInsets()?.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT)?.radius?.toFloat() ?: ogRadius
|
||||
} else {
|
||||
ogRadius
|
||||
}
|
||||
},
|
||||
onBottomNavUpdate = {
|
||||
setBottomPadding()
|
||||
}
|
||||
)
|
||||
|
||||
if (!isReturning) {
|
||||
activityBinding?.appBar?.lockYPos = true
|
||||
}
|
||||
binding.sourceRecycler.post {
|
||||
setBottomSheetTabs(if (binding.bottomSheet.root.sheetBehavior.isCollapsed()) 0f else 1f)
|
||||
binding.sourceRecycler.updatePaddingRelative(
|
||||
|
@ -167,12 +176,12 @@ class BrowseController :
|
|||
object : BottomSheetBehavior
|
||||
.BottomSheetCallback() {
|
||||
override fun onSlide(bottomSheet: View, progress: Float) {
|
||||
activityBinding?.appBar?.y = max(activityBinding!!.appBar.y, -headerHeight * (1 - progress))
|
||||
val oldShow = showingExtensions
|
||||
showingExtensions = progress > 0.92f
|
||||
if (oldShow != showingExtensions) {
|
||||
updateTitleAndMenu()
|
||||
}
|
||||
binding.bottomSheet.sheetToolbar.isVisible = true
|
||||
setBottomSheetTabs(max(0f, progress))
|
||||
}
|
||||
|
||||
|
@ -184,14 +193,12 @@ class BrowseController :
|
|||
binding.bottomSheet.root.isExpanding = false
|
||||
}
|
||||
val extBottomSheet = binding.bottomSheet.root
|
||||
if (state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
activityBinding?.appBar?.y = 0f
|
||||
}
|
||||
if (state == BottomSheetBehavior.STATE_EXPANDED ||
|
||||
state == BottomSheetBehavior.STATE_COLLAPSED
|
||||
) {
|
||||
binding.bottomSheet.root.sheetBehavior?.isDraggable = true
|
||||
showingExtensions = state == BottomSheetBehavior.STATE_EXPANDED
|
||||
binding.bottomSheet.sheetToolbar.isVisible = showingExtensions
|
||||
updateTitleAndMenu()
|
||||
if (state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
extBottomSheet.fetchOnlineExtensionsIfNeeded()
|
||||
|
@ -213,29 +220,93 @@ class BrowseController :
|
|||
if (showingExtensions) {
|
||||
binding.bottomSheet.root.sheetBehavior?.expand()
|
||||
}
|
||||
ogRadius = view.resources.getDimension(R.dimen.rounded_radius)
|
||||
|
||||
setSheetToolbar()
|
||||
presenter.onCreate()
|
||||
if (presenter.sourceItems.isNotEmpty()) {
|
||||
setSources(presenter.sourceItems, presenter.lastUsedItem)
|
||||
} else {
|
||||
binding.sourceRecycler.checkHeightThen {
|
||||
binding.sourceRecycler.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateTitleAndMenu() {
|
||||
if (router.backstack.lastOrNull()?.controller == this) {
|
||||
val activity = (activity as? MainActivity) ?: return
|
||||
(activity as? MainActivity)?.setFloatingToolbar(!showingExtensions)
|
||||
if (showingExtensions) {
|
||||
val color = activity.getResourceColor(R.attr.colorPrimaryVariant)
|
||||
activityBinding?.appBar?.setBackgroundColor(color)
|
||||
activity.window?.statusBarColor =
|
||||
ColorUtils.setAlphaComponent(color, (0.87f * 255).roundToInt())
|
||||
private fun updateSheetMenu() {
|
||||
binding.bottomSheet.sheetToolbar.title =
|
||||
view?.context?.getString(
|
||||
if (binding.bottomSheet.tabs.selectedTabPosition == 0) R.string.extensions
|
||||
else R.string.source_migration
|
||||
)
|
||||
val onExtensionTab = binding.bottomSheet.tabs.selectedTabPosition == 0
|
||||
if (binding.bottomSheet.sheetToolbar.menu.findItem(if (onExtensionTab) R.id.action_search else R.id.action_migration_guide) != null) {
|
||||
return
|
||||
}
|
||||
val oldSearchView = binding.bottomSheet.sheetToolbar.menu.findItem(R.id.action_search)?.actionView as? SearchView
|
||||
oldSearchView?.setOnQueryTextListener(null)
|
||||
binding.bottomSheet.sheetToolbar.menu.clear()
|
||||
binding.bottomSheet.sheetToolbar.inflateMenu(
|
||||
if (binding.bottomSheet.tabs.selectedTabPosition == 0) R.menu.extension_main
|
||||
else R.menu.migration_main
|
||||
)
|
||||
|
||||
// Initialize search option.
|
||||
binding.bottomSheet.sheetToolbar.menu.findItem(R.id.action_search)?.let { searchItem ->
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
|
||||
// Change hint to show global search.
|
||||
searchView.queryHint = view?.context?.getString(R.string.search_extensions)
|
||||
if (extQuery.isNotEmpty()) {
|
||||
searchView.setOnQueryTextListener(null)
|
||||
searchItem.expandActionView()
|
||||
searchView.setQuery(extQuery, true)
|
||||
searchView.clearFocus()
|
||||
} else {
|
||||
activityBinding?.appBar?.setBackgroundColor(Color.TRANSPARENT)
|
||||
activity.window?.statusBarColor = activity.getResourceColor(
|
||||
android.R.attr.statusBarColor
|
||||
)
|
||||
searchItem.collapseActionView()
|
||||
}
|
||||
activity.invalidateOptionsMenu()
|
||||
setTitle()
|
||||
// Create query listener which opens the global search view.
|
||||
setOnQueryTextChangeListener(searchView) {
|
||||
extQuery = it ?: ""
|
||||
binding.bottomSheet.root.drawExtensions()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSheetToolbar() {
|
||||
binding.bottomSheet.sheetToolbar.setOnMenuItemClickListener { item ->
|
||||
when (item.itemId) {
|
||||
// Initialize option to open catalogue settings.
|
||||
R.id.action_filter -> {
|
||||
val controller = ExtensionFilterController()
|
||||
router.pushController(
|
||||
RouterTransaction.with(controller)
|
||||
.popChangeHandler(SettingsSourcesFadeChangeHandler())
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
}
|
||||
R.id.action_migration_guide -> {
|
||||
activity?.openInBrowser(HELP_URL)
|
||||
}
|
||||
R.id.action_sources_settings -> {
|
||||
router.pushController(SettingsBrowseController().withFadeTransaction())
|
||||
}
|
||||
}
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
binding.bottomSheet.sheetToolbar.setNavigationOnClickListener {
|
||||
binding.bottomSheet.root.sheetBehavior?.collapse()
|
||||
}
|
||||
updateSheetMenu()
|
||||
}
|
||||
|
||||
fun updateTitleAndMenu() {
|
||||
if (isControllerVisible) {
|
||||
val activity = (activity as? MainActivity) ?: return
|
||||
activityBinding?.appBar?.isInvisible = showingExtensions
|
||||
(activity as? MainActivity)?.setStatusBarColorTransparent(showingExtensions)
|
||||
updateSheetMenu()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,9 +314,27 @@ class BrowseController :
|
|||
val bottomSheet = binding.bottomSheet.root
|
||||
val halfStepProgress = (max(0.5f, progress) - 0.5f) * 2
|
||||
binding.bottomSheet.tabs.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = ((activityBinding?.appBar?.height?.minus(9f.dpToPx) ?: 0f) * halfStepProgress).toInt()
|
||||
topMargin = (
|
||||
(
|
||||
activityBinding?.appBar?.paddingTop
|
||||
?.minus(9f.dpToPx)
|
||||
?.plus(toolbarHeight ?: 0) ?: 0f
|
||||
) * halfStepProgress
|
||||
).toInt()
|
||||
}
|
||||
binding.bottomSheet.pill.alpha = (1 - progress) * 0.25f
|
||||
binding.bottomSheet.sheetToolbar.alpha = progress
|
||||
if (isControllerVisible) {
|
||||
activityBinding?.appBar?.alpha = (1 - progress * 3) + 0.5f
|
||||
}
|
||||
|
||||
binding.bottomSheet.root.updateGradiantBGRadius(
|
||||
ogRadius,
|
||||
deviceRadius,
|
||||
progress,
|
||||
binding.bottomSheet.sheetLayout
|
||||
)
|
||||
|
||||
val selectedColor = ColorUtils.setAlphaComponent(
|
||||
bottomSheet.context.getResourceColor(R.attr.tabBarIconColor),
|
||||
(progress * 255).toInt()
|
||||
|
@ -318,8 +407,6 @@ class BrowseController :
|
|||
}
|
||||
}
|
||||
|
||||
override fun sheetIsFullscreen(): Boolean = binding.bottomSheet.root.sheetBehavior.isExpanded()
|
||||
|
||||
override fun handleSheetBack(): Boolean {
|
||||
if (showingExtensions) {
|
||||
if (binding.bottomSheet.root.canGoBack()) {
|
||||
|
@ -346,9 +433,22 @@ class BrowseController :
|
|||
binding.bottomSheet.root.updateExtTitle()
|
||||
binding.bottomSheet.root.presenter.refreshExtensions()
|
||||
presenter.updateSources()
|
||||
if (type.isEnter) {
|
||||
activityBinding?.appBar?.doOnNextLayout {
|
||||
activityBinding?.appBar?.y = 0f
|
||||
activityBinding?.appBar?.updateAppBarAfterY(binding.sourceRecycler)
|
||||
}
|
||||
updateSheetMenu()
|
||||
}
|
||||
}
|
||||
if (!type.isEnter) {
|
||||
binding.bottomSheet.root.canExpand = false
|
||||
activityBinding?.appBar?.alpha = 1f
|
||||
activityBinding?.appBar?.isInvisible = false
|
||||
binding.bottomSheet.sheetToolbar.menu.findItem(R.id.action_search)?.let { searchItem ->
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
searchView.clearFocus()
|
||||
}
|
||||
} else {
|
||||
binding.bottomSheet.root.presenter.refreshMigrations()
|
||||
updateTitleAndMenu()
|
||||
|
@ -372,7 +472,15 @@ class BrowseController :
|
|||
binding.bottomSheet.root.presenter.refreshMigrations()
|
||||
setBottomPadding()
|
||||
if (showingExtensions) {
|
||||
activity.invalidateOptionsMenu()
|
||||
updateSheetMenu()
|
||||
}
|
||||
if (BuildConfig.DEBUG && isControllerVisible) {
|
||||
val searchView = activityBinding?.cardToolbar?.searchView
|
||||
|
||||
setOnQueryTextChangeListener(searchView, onlyOnSubmit = true) {
|
||||
if (!it.isNullOrBlank()) performGlobalSearch(it)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,52 +570,19 @@ class BrowseController :
|
|||
* @param inflater used to load the menu xml.
|
||||
*/
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
if (onRoot) (activity as? MainActivity)?.setDismissIcon(showingExtensions)
|
||||
if (showingExtensions) {
|
||||
if (binding.bottomSheet.tabs.selectedTabPosition == 0) {
|
||||
// Inflate menu
|
||||
inflater.inflate(R.menu.extension_main, menu)
|
||||
// Inflate menu
|
||||
inflater.inflate(R.menu.catalogue_main, menu)
|
||||
|
||||
// Initialize search option.
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
// Initialize search option.
|
||||
val searchView = activityBinding?.cardToolbar?.searchView
|
||||
|
||||
// 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)
|
||||
}
|
||||
} else {
|
||||
// Inflate menu
|
||||
inflater.inflate(R.menu.catalogue_main, menu)
|
||||
// Change hint to show global search.
|
||||
activityBinding?.cardToolbar?.searchQueryHint = view?.context?.getString(R.string.global_search)
|
||||
|
||||
// Initialize search option.
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
|
||||
// Change hint to show global search.
|
||||
searchView.queryHint = view?.context?.getString(R.string.global_search)
|
||||
|
||||
searchItem.fixExpandInvalidate()
|
||||
// Create query listener which opens the global search view.
|
||||
setOnQueryTextChangeListener(searchView, true) {
|
||||
if (!it.isNullOrBlank()) performGlobalSearch(it)
|
||||
true
|
||||
}
|
||||
hideItemsIfExpanded(searchItem, menu)
|
||||
// Create query listener which opens the global search view.
|
||||
setOnQueryTextChangeListener(searchView, true) {
|
||||
if (!it.isNullOrBlank()) performGlobalSearch(it)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -525,10 +600,7 @@ class BrowseController :
|
|||
when (item.itemId) {
|
||||
// Initialize option to open catalogue settings.
|
||||
R.id.action_filter -> {
|
||||
val controller =
|
||||
if (showingExtensions) {
|
||||
ExtensionFilterController()
|
||||
} else SettingsSourcesController()
|
||||
val controller = SettingsSourcesController()
|
||||
router.pushController(
|
||||
RouterTransaction.with(controller)
|
||||
.popChangeHandler(SettingsSourcesFadeChangeHandler())
|
||||
|
@ -552,6 +624,9 @@ class BrowseController :
|
|||
fun setSources(sources: List<IFlexible<*>>, lastUsed: SourceItem?) {
|
||||
adapter?.updateDataSet(sources, false)
|
||||
setLastUsedSource(lastUsed)
|
||||
if (isControllerVisible) {
|
||||
activityBinding?.appBar?.lockYPos = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.util.system.withUIContext
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -66,7 +67,7 @@ class SourcePresenter(
|
|||
else -> d1.compareTo(d2)
|
||||
}
|
||||
}
|
||||
val byLang = sources.groupByTo(map, { it.lang })
|
||||
val byLang = sources.groupByTo(map) { it.lang }
|
||||
sourceItems = byLang.flatMap {
|
||||
val langItem = LangItem(it.key)
|
||||
it.value.map { source ->
|
||||
|
@ -94,6 +95,7 @@ class SourcePresenter(
|
|||
private fun loadLastUsedSource() {
|
||||
lastUsedJob?.cancel()
|
||||
lastUsedJob = preferences.lastUsedCatalogueSource().asFlow()
|
||||
.drop(1)
|
||||
.onEach {
|
||||
lastUsedItem = getLastUsedSource(it)
|
||||
withUIContext {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package eu.kanade.tachiyomi.ui.source.browse
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
|
@ -9,11 +10,11 @@ import android.view.MenuInflater
|
|||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.WindowInsetsCompat.Type.ime
|
||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -27,6 +28,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|||
import eu.kanade.tachiyomi.databinding.BrowseSourceControllerBinding
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.icon
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
|
@ -41,8 +43,11 @@ import eu.kanade.tachiyomi.util.addOrRemoveToFavorites
|
|||
import eu.kanade.tachiyomi.util.system.connectivityManager
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.applyBottomAnimatedInsets
|
||||
import eu.kanade.tachiyomi.util.view.fullAppBarHeight
|
||||
import eu.kanade.tachiyomi.util.view.inflate
|
||||
import eu.kanade.tachiyomi.util.view.isControllerVisible
|
||||
import eu.kanade.tachiyomi.util.view.requestFilePermissionsSafe
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||
|
@ -50,8 +55,10 @@ import eu.kanade.tachiyomi.util.view.snack
|
|||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
import eu.kanade.tachiyomi.widget.EmptyView
|
||||
import eu.kanade.tachiyomi.widget.LinearLayoutManagerAccurateOffset
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Controller to manage the catalogues available in the app.
|
||||
|
@ -115,14 +122,28 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
/** Current filter sheet */
|
||||
var filterSheet: SourceFilterSheet? = null
|
||||
|
||||
private val isBehindGlobalSearch: Boolean
|
||||
get() = router.backstackSize >= 2 && router.backstack[router.backstackSize - 2].controller is GlobalSearchController
|
||||
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override val mainRecycler: RecyclerView?
|
||||
get() = recycler
|
||||
|
||||
override fun getTitle(): String? {
|
||||
return presenter.source.name
|
||||
}
|
||||
|
||||
override fun getSearchTitle(): String? {
|
||||
return searchTitle(presenter.source.name)
|
||||
}
|
||||
|
||||
override fun getBigIcon(): Drawable? {
|
||||
return presenter.source.icon()
|
||||
}
|
||||
|
||||
override fun createPresenter(): BrowseSourcePresenter {
|
||||
return BrowseSourcePresenter(args.getLong(SOURCE_ID_KEY), args.getString(SEARCH_QUERY_KEY))
|
||||
}
|
||||
|
@ -139,6 +160,9 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
binding.fab.isVisible = presenter.sourceFilters.isNotEmpty()
|
||||
binding.fab.setOnClickListener { showFilters() }
|
||||
binding.progress.isVisible = true
|
||||
activityBinding?.appBar?.y = 0f
|
||||
activityBinding?.appBar?.updateAppBarAfterY(recycler)
|
||||
activityBinding?.appBar?.lockYPos = true
|
||||
requestFilePermissionsSafe(301, preferences, presenter.source is LocalSource)
|
||||
}
|
||||
|
||||
|
@ -151,9 +175,13 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
|
||||
private fun setupRecycler(view: View) {
|
||||
var oldPosition = RecyclerView.NO_POSITION
|
||||
var oldOffset = 0f
|
||||
val oldRecycler = binding.catalogueView.getChildAt(1)
|
||||
if (oldRecycler is RecyclerView) {
|
||||
oldPosition = (oldRecycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
|
||||
oldPosition = (oldRecycler.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition()
|
||||
.takeIf { it != RecyclerView.NO_POSITION }
|
||||
?: (oldRecycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
|
||||
oldOffset = oldRecycler.layoutManager?.findViewByPosition(oldPosition)?.y?.minus(oldRecycler.paddingTop) ?: 0f
|
||||
oldRecycler.adapter = null
|
||||
|
||||
binding.catalogueView.removeView(oldRecycler)
|
||||
|
@ -162,7 +190,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
val recycler = if (presenter.isListMode) {
|
||||
RecyclerView(view.context).apply {
|
||||
id = R.id.recycler
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManagerAccurateOffset(context)
|
||||
layoutParams = RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
|
||||
}
|
||||
|
@ -184,6 +212,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
recycler.setHasFixedSize(true)
|
||||
recycler.adapter = adapter
|
||||
|
||||
binding.catalogueView.addView(recycler, 1)
|
||||
scrollViewWith(
|
||||
recycler,
|
||||
true,
|
||||
|
@ -193,6 +222,14 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
bottomMargin = insets.getInsets(systemBars() or ime()).bottom + 16.dpToPx
|
||||
}
|
||||
}
|
||||
val bigToolbarHeight = fullAppBarHeight ?: 0
|
||||
binding.progress.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = (bigToolbarHeight + insets.getInsets(systemBars()).top) / 2
|
||||
}
|
||||
binding.emptyView.updatePadding(
|
||||
top = (bigToolbarHeight + insets.getInsets(systemBars()).top),
|
||||
bottom = insets.getInsets(systemBars()).bottom
|
||||
)
|
||||
}
|
||||
)
|
||||
binding.fab.applyBottomAnimatedInsets(16.dpToPx)
|
||||
|
@ -209,9 +246,11 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
}
|
||||
)
|
||||
|
||||
binding.catalogueView.addView(recycler, 1)
|
||||
if (oldPosition != RecyclerView.NO_POSITION) {
|
||||
recycler.layoutManager?.scrollToPosition(oldPosition)
|
||||
(recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(oldPosition, oldOffset.roundToInt())
|
||||
if (oldPosition > 0 && (activity as? MainActivity)?.currentToolbar != activityBinding?.cardToolbar) {
|
||||
activityBinding?.appBar?.useSearchToolbarForMenu(true)
|
||||
}
|
||||
}
|
||||
this.recycler = recycler
|
||||
}
|
||||
|
@ -220,58 +259,45 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
inflater.inflate(R.menu.browse_source, menu)
|
||||
|
||||
// Initialize search menu
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
val searchItem = activityBinding?.cardToolbar?.searchItem
|
||||
val searchView = activityBinding?.cardToolbar?.searchView
|
||||
|
||||
activityBinding?.cardToolbar?.setQueryHint("", !isBehindGlobalSearch && presenter.query.isBlank())
|
||||
val query = presenter.query
|
||||
if (query.isNotBlank()) {
|
||||
searchItem.expandActionView()
|
||||
searchView.setQuery(query, true)
|
||||
searchView.clearFocus()
|
||||
searchItem?.expandActionView()
|
||||
searchView?.setQuery(query, true)
|
||||
searchView?.clearFocus()
|
||||
} else if (activityBinding?.cardToolbar?.isSearchExpanded == true) {
|
||||
searchItem?.collapseActionView()
|
||||
searchView?.setQuery("", true)
|
||||
}
|
||||
|
||||
// val searchEventsObservable = searchView.queryTextChangeEvents()
|
||||
// .skip(1)
|
||||
// .filter { router.backstack.lastOrNull()?.controller == this@BrowseSourceController }
|
||||
// .share()
|
||||
// val writingObservable = searchEventsObservable
|
||||
// .filter { !it.isSubmitted }
|
||||
// .debounce(1250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
||||
// val submitObservable = searchEventsObservable
|
||||
// .filter { it.isSubmitted }
|
||||
//
|
||||
// searchViewSubscription?.unsubscribe()
|
||||
// searchViewSubscription = Observable.merge(writingObservable, submitObservable)
|
||||
// .map { it.queryText().toString() }
|
||||
// .subscribeUntilDestroy { searchWithQuery(it) }
|
||||
|
||||
setOnQueryTextChangeListener(searchView, onlyOnSubmit = true, hideKbOnSubmit = true) {
|
||||
searchWithQuery(it ?: "")
|
||||
true
|
||||
}
|
||||
|
||||
searchItem.fixExpand(
|
||||
onExpand = { invalidateMenuOnExpand() },
|
||||
onCollapse = {
|
||||
if (router.backstackSize >= 2 && router.backstack[router.backstackSize - 2].controller is GlobalSearchController) {
|
||||
router.popController(this)
|
||||
} else {
|
||||
searchWithQuery("")
|
||||
}
|
||||
true
|
||||
}
|
||||
)
|
||||
|
||||
// Show next display mode
|
||||
menu.findItem(R.id.action_display_mode).apply {
|
||||
val icon = if (presenter.isListMode) {
|
||||
updateDisplayMenuItem(menu)
|
||||
}
|
||||
|
||||
private fun updateDisplayMenuItem(menu: Menu?, isListMode: Boolean? = null) {
|
||||
menu?.findItem(R.id.action_display_mode)?.apply {
|
||||
val icon = if (isListMode ?: presenter.isListMode) {
|
||||
R.drawable.ic_view_module_24dp
|
||||
} else {
|
||||
R.drawable.ic_view_list_24dp
|
||||
}
|
||||
setIcon(icon)
|
||||
}
|
||||
hideItemsIfExpanded(searchItem, menu)
|
||||
}
|
||||
|
||||
override fun onActionViewCollapse(item: MenuItem?) {
|
||||
if (isBehindGlobalSearch) {
|
||||
router.popController(this)
|
||||
} else {
|
||||
searchWithQuery("")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
|
@ -463,15 +489,16 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
resetProgressItem()
|
||||
}
|
||||
adapter.onLoadMoreComplete(mangas)
|
||||
if (isControllerVisible) {
|
||||
activityBinding?.appBar?.lockYPos = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
super.onActivityResumed(activity)
|
||||
|
||||
if (BuildConfig.DEBUG && presenter.query.isBlank()) {
|
||||
val searchItem =
|
||||
(activity as? MainActivity)?.binding?.cardToolbar?.menu?.findItem(R.id.action_search)
|
||||
val searchView = searchItem?.actionView as? SearchView ?: return
|
||||
if (BuildConfig.DEBUG && isControllerVisible) {
|
||||
val searchView = activityBinding?.cardToolbar?.searchView
|
||||
setOnQueryTextChangeListener(searchView, onlyOnSubmit = true, hideKbOnSubmit = true) {
|
||||
searchWithQuery(it ?: "")
|
||||
true
|
||||
|
@ -531,6 +558,9 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
setAction(R.string.retry, retryAction)
|
||||
}
|
||||
}
|
||||
if (isControllerVisible) {
|
||||
activityBinding?.appBar?.lockYPos = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun getErrorMessage(error: Throwable): String {
|
||||
|
@ -581,13 +611,14 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
/**
|
||||
* Swaps the current display mode.
|
||||
*/
|
||||
fun swapDisplayMode() {
|
||||
private fun swapDisplayMode() {
|
||||
val view = view ?: return
|
||||
val adapter = adapter ?: return
|
||||
|
||||
presenter.swapDisplayMode()
|
||||
val isListMode = presenter.isListMode
|
||||
activity?.invalidateOptionsMenu()
|
||||
updateDisplayMenuItem(activityBinding?.toolbar?.menu, isListMode)
|
||||
updateDisplayMenuItem(activityBinding?.cardToolbar?.menu, isListMode)
|
||||
setupRecycler(view)
|
||||
if (!isListMode || !view.context.connectivityManager.isActiveNetworkMetered) {
|
||||
// Initialize mangas if going to grid view or if over wifi when going to list view
|
||||
|
|
|
@ -6,16 +6,17 @@ import android.view.Menu
|
|||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
import androidx.core.view.updatePaddingRelative
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
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.SmallToolbarInterface
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
|
@ -26,6 +27,7 @@ import eu.kanade.tachiyomi.util.addOrRemoveToFavorites
|
|||
import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import eu.kanade.tachiyomi.util.view.toolbarHeight
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
|
@ -42,6 +44,7 @@ open class GlobalSearchController(
|
|||
bundle: Bundle? = null
|
||||
) : NucleusController<SourceGlobalSearchControllerBinding, GlobalSearchPresenter>(bundle),
|
||||
FloatingSearchInterface,
|
||||
SmallToolbarInterface,
|
||||
GlobalSearchAdapter.OnTitleClickListener,
|
||||
GlobalSearchCardAdapter.OnMangaClickListener {
|
||||
|
||||
|
@ -148,31 +151,38 @@ open class GlobalSearchController(
|
|||
inflater.inflate(R.menu.catalogue_new_list, menu)
|
||||
|
||||
// Initialize search menu
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
activityBinding?.cardToolbar?.setQueryHint(view?.context?.getString(R.string.global_search), false)
|
||||
activityBinding?.cardToolbar?.searchItem?.expandActionView()
|
||||
activityBinding?.cardToolbar?.searchView?.setQuery(presenter.query, false)
|
||||
|
||||
searchItem.isVisible = customTitle == null
|
||||
searchItem.setOnActionExpandListener(
|
||||
object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
|
||||
searchView.onActionViewExpanded() // Required to show the query in the view
|
||||
searchView.setQuery(presenter.query, false)
|
||||
return true
|
||||
}
|
||||
setOnQueryTextChangeListener(activityBinding?.cardToolbar?.searchView, onlyOnSubmit = true, hideKbOnSubmit = true) {
|
||||
presenter.search(it ?: "")
|
||||
setTitle() // Update toolbar title
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
)
|
||||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||
super.onChangeStarted(handler, type)
|
||||
if (type.isEnter) {
|
||||
val searchView = activityBinding?.cardToolbar?.searchView ?: return
|
||||
val searchItem = activityBinding?.cardToolbar?.searchItem ?: return
|
||||
searchItem.expandActionView()
|
||||
searchView.setQuery(presenter.query, false)
|
||||
}
|
||||
}
|
||||
|
||||
searchView.queryTextChangeEvents()
|
||||
.filter { it.isSubmitted }
|
||||
.subscribeUntilDestroy {
|
||||
presenter.search(it.queryText().toString())
|
||||
searchItem.collapseActionView()
|
||||
setTitle() // Update toolbar title
|
||||
}
|
||||
override fun onActionViewExpand(item: MenuItem?) {
|
||||
val searchView = activityBinding?.cardToolbar?.searchView ?: return
|
||||
searchView.setQuery(presenter.query, false)
|
||||
}
|
||||
|
||||
override fun onActionViewCollapse(item: MenuItem?) {
|
||||
if (activity is SearchActivity && extensionFilter != null) {
|
||||
(activity as? SearchActivity)?.backPress()
|
||||
} else if (customTitle == null) {
|
||||
router.popCurrentController()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -253,6 +263,7 @@ open class GlobalSearchController(
|
|||
customTitle = null
|
||||
setTitle()
|
||||
activity?.invalidateOptionsMenu()
|
||||
activityBinding?.appBar?.updateAppBarAfterY(binding.recycler)
|
||||
}
|
||||
}
|
||||
adapter?.updateDataSet(searchResult)
|
||||
|
|
|
@ -37,6 +37,6 @@ class LatestUpdatesController(bundle: Bundle) : BrowseSourceController(bundle) {
|
|||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
super.onPrepareOptionsMenu(menu)
|
||||
menu.findItem(R.id.action_search).isVisible = false
|
||||
menu.findItem(R.id.action_search)?.isVisible = false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package eu.kanade.tachiyomi.util.view
|
||||
|
||||
import android.Manifest
|
||||
import android.animation.Animator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
|
@ -17,6 +17,7 @@ import android.view.ViewGroup
|
|||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.core.content.ContextCompat
|
||||
|
@ -26,8 +27,12 @@ import androidx.core.net.toUri
|
|||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsCompat.Type.ime
|
||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePaddingRelative
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
|
@ -37,33 +42,35 @@ import com.bluelinelabs.conductor.RouterTransaction
|
|||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.databinding.MainActivityBinding
|
||||
import eu.kanade.tachiyomi.ui.base.SmallToolbarInterface
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.OneWayFadeChangeHandler
|
||||
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.TabbedInterface
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsController
|
||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.isTablet
|
||||
import eu.kanade.tachiyomi.util.system.materialAlertDialog
|
||||
import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
|
||||
import eu.kanade.tachiyomi.util.system.toInt
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.random.Random
|
||||
|
||||
fun Controller.setOnQueryTextChangeListener(
|
||||
searchView: SearchView,
|
||||
searchView: SearchView?,
|
||||
onlyOnSubmit: Boolean = false,
|
||||
hideKbOnSubmit: Boolean = true,
|
||||
f: (text: String?) -> Boolean
|
||||
) {
|
||||
searchView.setOnQueryTextListener(
|
||||
searchView?.setOnQueryTextListener(
|
||||
object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
if (!onlyOnSubmit && router.backstack.lastOrNull()
|
||||
|
@ -75,7 +82,7 @@ fun Controller.setOnQueryTextChangeListener(
|
|||
}
|
||||
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
if (router.backstack.lastOrNull()?.controller == this@setOnQueryTextChangeListener) {
|
||||
if (isControllerVisible) {
|
||||
if (hideKbOnSubmit) {
|
||||
val imm =
|
||||
activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
||||
|
@ -105,16 +112,13 @@ fun Controller.removeQueryListener() {
|
|||
|
||||
fun Controller.liftAppbarWith(recycler: RecyclerView, padView: Boolean = false) {
|
||||
if (padView) {
|
||||
val attrsArray = intArrayOf(R.attr.mainActionBarSize)
|
||||
val array = recycler.context.obtainStyledAttributes(attrsArray)
|
||||
var appBarHeight = (
|
||||
if (toolbarHeight ?: 0 > 0) toolbarHeight!!
|
||||
else array.getDimensionPixelSize(0, 0)
|
||||
if (fullAppBarHeight ?: 0 > 0) fullAppBarHeight!!
|
||||
else activityBinding?.appBar?.attrToolbarHeight ?: 0
|
||||
)
|
||||
array.recycle()
|
||||
activityBinding!!.toolbar.post {
|
||||
if (toolbarHeight!! > 0) {
|
||||
appBarHeight = toolbarHeight!!
|
||||
if (fullAppBarHeight!! > 0) {
|
||||
appBarHeight = fullAppBarHeight!!
|
||||
recycler.requestApplyInsets()
|
||||
}
|
||||
}
|
||||
|
@ -136,13 +140,14 @@ fun Controller.liftAppbarWith(recycler: RecyclerView, padView: Boolean = false)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
view?.applyWindowInsetsForController()
|
||||
view?.applyWindowInsetsForController(activityBinding?.appBar?.attrToolbarHeight ?: 0)
|
||||
recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
|
||||
}
|
||||
|
||||
var toolbarColorAnim: ValueAnimator? = null
|
||||
var isToolbarColored = false
|
||||
|
||||
val preferences: PreferencesHelper by injectLazy()
|
||||
val colorToolbar: (Boolean) -> Unit = f@{ isColored ->
|
||||
isToolbarColored = isColored
|
||||
toolbarColorAnim?.cancel()
|
||||
|
@ -169,7 +174,13 @@ fun Controller.liftAppbarWith(recycler: RecyclerView, padView: Boolean = false)
|
|||
if (floatingBar) {
|
||||
setAppBarBG(0f)
|
||||
}
|
||||
colorToolbar(recycler.canScrollVertically(-1))
|
||||
|
||||
activityBinding?.appBar?.setToolbarModeBy(this)
|
||||
activityBinding?.appBar?.hideBigView(true)
|
||||
activityBinding?.appBar?.y = 0f
|
||||
activityBinding?.appBar?.updateAppBarAfterY(recycler)
|
||||
|
||||
setAppBarBG(0f)
|
||||
recycler.addOnScrollListener(
|
||||
object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
|
@ -194,22 +205,23 @@ fun Controller.scrollViewWith(
|
|||
liftOnScroll: ((Boolean) -> Unit)? = null,
|
||||
onLeavingController: (() -> Unit)? = null,
|
||||
onBottomNavUpdate: (() -> Unit)? = null,
|
||||
includeTabView: Boolean = false
|
||||
): ((Boolean) -> Unit) {
|
||||
var statusBarHeight = -1
|
||||
val tabBarHeight = 48.dpToPx
|
||||
activityBinding?.appBar?.lockYPos = false
|
||||
activityBinding?.appBar?.y = 0f
|
||||
val attrsArray = intArrayOf(R.attr.mainActionBarSize)
|
||||
val array = recycler.context.obtainStyledAttributes(attrsArray)
|
||||
val includeTabView = this is TabbedInterface
|
||||
activityBinding?.appBar?.useTabsInPreLayout = includeTabView
|
||||
activityBinding?.appBar?.setToolbarModeBy(this@scrollViewWith)
|
||||
var appBarHeight = (
|
||||
if (toolbarHeight ?: 0 > 0) toolbarHeight!!
|
||||
else array.getDimensionPixelSize(0, 0)
|
||||
) + if (includeTabView) tabBarHeight else 0
|
||||
array.recycle()
|
||||
if (fullAppBarHeight ?: 0 > 0) fullAppBarHeight!!
|
||||
else activityBinding?.appBar?.preLayoutHeight ?: 0
|
||||
)
|
||||
swipeRefreshLayout?.setDistanceToTriggerSync(150.dpToPx)
|
||||
activityBinding!!.toolbar.post {
|
||||
if (toolbarHeight!! > 0) {
|
||||
appBarHeight = toolbarHeight!! + if (includeTabView) tabBarHeight else 0
|
||||
val swipeCircle = swipeRefreshLayout?.findChild<ImageView>()
|
||||
activityBinding!!.appBar.doOnLayout {
|
||||
if (fullAppBarHeight!! > 0) {
|
||||
appBarHeight = fullAppBarHeight!!
|
||||
recycler.requestApplyInsets()
|
||||
}
|
||||
}
|
||||
|
@ -220,9 +232,13 @@ fun Controller.scrollViewWith(
|
|||
recycler.post {
|
||||
updateViewsNearBottom()
|
||||
}
|
||||
|
||||
setItemAnimatorForAppBar(recycler)
|
||||
|
||||
val randomTag = Random.nextLong()
|
||||
var lastY = 0f
|
||||
var fakeToolbarView: View? = null
|
||||
val preferences: PreferencesHelper by injectLazy()
|
||||
var fakeBottomNavView: View? = null
|
||||
if (!customPadding) {
|
||||
recycler.updatePaddingRelative(
|
||||
|
@ -232,7 +248,17 @@ fun Controller.scrollViewWith(
|
|||
) + appBarHeight
|
||||
)
|
||||
}
|
||||
val atTopOfRecyclerView: () -> Boolean = f@{
|
||||
if (this is SmallToolbarInterface || activityBinding?.appBar?.useLargeToolbar == false) {
|
||||
return@f !recycler.canScrollVertically(-1)
|
||||
}
|
||||
val activityBinding = activityBinding ?: return@f true
|
||||
return@f recycler.computeVerticalScrollOffset() - recycler.paddingTop <=
|
||||
0 - activityBinding.appBar.paddingTop -
|
||||
activityBinding.toolbar.height - if (includeTabView) tabBarHeight else 0
|
||||
}
|
||||
recycler.doOnApplyWindowInsetsCompat { view, insets, _ ->
|
||||
appBarHeight = fullAppBarHeight!!
|
||||
val headerHeight = insets.getInsets(systemBars()).top + appBarHeight
|
||||
if (!customPadding) view.updatePaddingRelative(
|
||||
top = headerHeight,
|
||||
|
@ -250,7 +276,6 @@ fun Controller.scrollViewWith(
|
|||
var toolbarColorAnim: ValueAnimator? = null
|
||||
var isToolbarColor = false
|
||||
var isInView = true
|
||||
val preferences: PreferencesHelper by injectLazy()
|
||||
val colorToolbar: (Boolean) -> Unit = f@{ isColored ->
|
||||
isToolbarColor = isColored
|
||||
if (liftOnScroll != null) {
|
||||
|
@ -260,7 +285,7 @@ fun Controller.scrollViewWith(
|
|||
val floatingBar =
|
||||
(this as? FloatingSearchInterface)?.showFloatingBar() == true && !includeTabView
|
||||
if (floatingBar) {
|
||||
setAppBarBG(0f, includeTabView)
|
||||
setAppBarBG(isColored.toInt().toFloat(), includeTabView)
|
||||
return@f
|
||||
}
|
||||
val percent = ImageUtil.getPercentOfColor(
|
||||
|
@ -288,6 +313,12 @@ fun Controller.scrollViewWith(
|
|||
super.onChangeStart(controller, changeHandler, changeType)
|
||||
isInView = changeType.isEnter
|
||||
if (changeType.isEnter) {
|
||||
activityBinding?.appBar?.hideBigView(
|
||||
this@scrollViewWith is SmallToolbarInterface,
|
||||
setTitleAlpha = this@scrollViewWith !is MangaDetailsController
|
||||
)
|
||||
activityBinding?.appBar?.setToolbarModeBy(this@scrollViewWith)
|
||||
activityBinding?.appBar?.useTabsInPreLayout = includeTabView
|
||||
colorToolbar(isToolbarColor)
|
||||
if (fakeToolbarView?.parent != null) {
|
||||
val parent = fakeToolbarView?.parent as? ViewGroup ?: return
|
||||
|
@ -300,13 +331,10 @@ fun Controller.scrollViewWith(
|
|||
fakeBottomNavView = null
|
||||
}
|
||||
lastY = 0f
|
||||
activityBinding!!.appBar.updateAppBarAfterY(recycler)
|
||||
activityBinding!!.toolbar.tag = randomTag
|
||||
activityBinding!!.toolbar.setOnClickListener {
|
||||
if ((this@scrollViewWith as? BottomSheetController)?.sheetIsFullscreen() != true) {
|
||||
recycler.smoothScrollToTop()
|
||||
} else {
|
||||
(this@scrollViewWith as? BottomSheetController)?.toggleSheet()
|
||||
}
|
||||
recycler.smoothScrollToTop()
|
||||
}
|
||||
} else {
|
||||
if (!customPadding && lastY == 0f && (
|
||||
|
@ -349,18 +377,17 @@ fun Controller.scrollViewWith(
|
|||
}
|
||||
}
|
||||
)
|
||||
colorToolbar(recycler.canScrollVertically(-1))
|
||||
colorToolbar(!atTopOfRecyclerView())
|
||||
|
||||
recycler.post {
|
||||
colorToolbar(recycler.canScrollVertically(-1))
|
||||
activityBinding!!.appBar.updateAppBarAfterY(recycler)
|
||||
colorToolbar(!atTopOfRecyclerView())
|
||||
}
|
||||
val isTablet = recycler.context.isTablet() && recycler.context.resources.configuration?.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
recycler.addOnScrollListener(
|
||||
object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (router?.backstack?.lastOrNull()
|
||||
?.controller == this@scrollViewWith && statusBarHeight > -1 &&
|
||||
if (isControllerVisible && statusBarHeight > -1 &&
|
||||
(this@scrollViewWith as? BaseController<*>)?.isDragging != true &&
|
||||
activity != null && activityBinding!!.appBar.height > 0 &&
|
||||
recycler.translationY == 0f
|
||||
|
@ -369,9 +396,8 @@ fun Controller.scrollViewWith(
|
|||
val shortAnimationDuration = resources?.getInteger(
|
||||
android.R.integer.config_shortAnimTime
|
||||
) ?: 0
|
||||
activityBinding!!.appBar.animate().y(0f)
|
||||
.setDuration(shortAnimationDuration.toLong())
|
||||
.start()
|
||||
activityBinding!!.appBar.y = 0f
|
||||
activityBinding!!.appBar.updateAppBarAfterY(recycler)
|
||||
if (router.backstackSize == 1 && isInView) {
|
||||
activityBinding!!.bottomNav?.let {
|
||||
val animator = it.animate()?.translationY(0f)
|
||||
|
@ -385,46 +411,43 @@ fun Controller.scrollViewWith(
|
|||
lastY = 0f
|
||||
if (isToolbarColor) colorToolbar(false)
|
||||
} else {
|
||||
if (!isTablet) {
|
||||
activityBinding!!.appBar.y -= dy
|
||||
activityBinding!!.appBar.y = MathUtils.clamp(
|
||||
activityBinding!!.appBar.y,
|
||||
-activityBinding!!.appBar.height.toFloat(),
|
||||
0f
|
||||
)
|
||||
activityBinding!!.bottomNav?.let { bottomNav ->
|
||||
if (bottomNav.isVisible && isInView) {
|
||||
if (preferences.hideBottomNavOnScroll().get()) {
|
||||
bottomNav.translationY += dy
|
||||
bottomNav.translationY = MathUtils.clamp(
|
||||
bottomNav.translationY,
|
||||
0f,
|
||||
bottomNav.height.toFloat()
|
||||
)
|
||||
updateViewsNearBottom()
|
||||
} else if (bottomNav.translationY != 0f) {
|
||||
bottomNav.translationY = 0f
|
||||
activityBinding!!.bottomView?.translationY = 0f
|
||||
}
|
||||
activityBinding!!.appBar.y -= dy
|
||||
activityBinding!!.appBar.updateAppBarAfterY(recycler)
|
||||
activityBinding!!.bottomNav?.let { bottomNav ->
|
||||
if (bottomNav.isVisible && isInView) {
|
||||
if (preferences.hideBottomNavOnScroll().get()) {
|
||||
bottomNav.translationY += dy
|
||||
bottomNav.translationY = MathUtils.clamp(
|
||||
bottomNav.translationY,
|
||||
0f,
|
||||
bottomNav.height.toFloat()
|
||||
)
|
||||
updateViewsNearBottom()
|
||||
} else if (bottomNav.translationY != 0f) {
|
||||
bottomNav.translationY = 0f
|
||||
activityBinding!!.bottomView?.translationY = 0f
|
||||
}
|
||||
}
|
||||
|
||||
if (!isToolbarColor && (
|
||||
dy == 0 ||
|
||||
(
|
||||
activityBinding!!.appBar.y <= -activityBinding!!.appBar.height.toFloat() ||
|
||||
dy == 0 && activityBinding!!.appBar.y == 0f
|
||||
)
|
||||
)
|
||||
) {
|
||||
colorToolbar(true)
|
||||
}
|
||||
} else {
|
||||
val notAtTop = recycler.canScrollVertically(-1)
|
||||
if (notAtTop != isToolbarColor) colorToolbar(notAtTop)
|
||||
}
|
||||
|
||||
if (!isToolbarColor && (
|
||||
dy == 0 ||
|
||||
(
|
||||
activityBinding!!.appBar.y <= -activityBinding!!.appBar.height.toFloat() ||
|
||||
dy == 0 && activityBinding!!.appBar.y == 0f
|
||||
)
|
||||
)
|
||||
) {
|
||||
colorToolbar(true)
|
||||
}
|
||||
val notAtTop = !atTopOfRecyclerView()
|
||||
if (notAtTop != isToolbarColor) colorToolbar(notAtTop)
|
||||
lastY = activityBinding!!.appBar.y
|
||||
}
|
||||
swipeCircle?.translationY = max(
|
||||
activityBinding!!.appBar.y,
|
||||
-activityBinding!!.appBar.height + activityBinding!!.appBar.paddingTop.toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,9 +456,6 @@ fun Controller.scrollViewWith(
|
|||
if (newState == RecyclerView.SCROLL_STATE_IDLE &&
|
||||
(this@scrollViewWith as? BaseController<*>)?.isDragging != true
|
||||
) {
|
||||
if (isTablet) {
|
||||
return
|
||||
}
|
||||
if (router?.backstack?.lastOrNull()
|
||||
?.controller == this@scrollViewWith && statusBarHeight > -1 &&
|
||||
activity != null && activityBinding!!.appBar.height > 0 &&
|
||||
|
@ -453,10 +473,12 @@ fun Controller.scrollViewWith(
|
|||
if (activityBinding!!.bottomNav?.isVisible == true &&
|
||||
preferences.hideBottomNavOnScroll().get()
|
||||
) closerToBottom else closerToTop
|
||||
lastY =
|
||||
if (closerToEdge && !atTop) (-activityBinding!!.appBar.height.toFloat()) else 0f
|
||||
activityBinding!!.appBar.animate().y(lastY)
|
||||
.setDuration(shortAnimationDuration.toLong()).start()
|
||||
lastY = activityBinding!!.appBar.snapAppBarY(recycler) {
|
||||
swipeCircle?.translationY = max(
|
||||
activityBinding!!.appBar.y,
|
||||
-activityBinding!!.appBar.height + activityBinding!!.appBar.paddingTop.toFloat()
|
||||
)
|
||||
}
|
||||
if (activityBinding!!.bottomNav?.isVisible == true &&
|
||||
isInView && preferences.hideBottomNavOnScroll().get()
|
||||
) {
|
||||
|
@ -471,8 +493,8 @@ fun Controller.scrollViewWith(
|
|||
animator?.start()
|
||||
}
|
||||
}
|
||||
if (recycler.canScrollVertically(-1) && !isToolbarColor) colorToolbar(true)
|
||||
else if (!recycler.canScrollVertically(-1) && isToolbarColor) colorToolbar(false)
|
||||
val notAtTop = !atTopOfRecyclerView()
|
||||
if (notAtTop != isToolbarColor) colorToolbar(notAtTop)
|
||||
}
|
||||
} else if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
|
||||
val view = activity?.window?.currentFocus ?: return
|
||||
|
@ -487,16 +509,112 @@ fun Controller.scrollViewWith(
|
|||
return colorToolbar
|
||||
}
|
||||
|
||||
fun Controller.setItemAnimatorForAppBar(recycler: RecyclerView) {
|
||||
var itemAppBarAnimator: Animator? = null
|
||||
|
||||
fun animateAppBar() {
|
||||
if (this !is SmallToolbarInterface) {
|
||||
itemAppBarAnimator?.cancel()
|
||||
val duration = (recycler.itemAnimator?.changeDuration ?: 250) * 2
|
||||
itemAppBarAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
|
||||
addUpdateListener {
|
||||
activityBinding?.appBar?.updateAppBarAfterY(recycler)
|
||||
}
|
||||
}
|
||||
itemAppBarAnimator?.duration = duration
|
||||
itemAppBarAnimator?.start()
|
||||
}
|
||||
}
|
||||
|
||||
recycler.itemAnimator = object : DefaultItemAnimator() {
|
||||
override fun animateMove(
|
||||
holder: RecyclerView.ViewHolder?,
|
||||
fromX: Int,
|
||||
fromY: Int,
|
||||
toX: Int,
|
||||
toY: Int
|
||||
): Boolean {
|
||||
animateAppBar()
|
||||
return super.animateMove(holder, fromX, fromY, toX, toY)
|
||||
}
|
||||
|
||||
override fun onAnimationFinished(viewHolder: RecyclerView.ViewHolder) {
|
||||
activityBinding?.appBar?.updateAppBarAfterY(recycler)
|
||||
super.onAnimationFinished(viewHolder)
|
||||
}
|
||||
|
||||
override fun animateChange(
|
||||
oldHolder: RecyclerView.ViewHolder,
|
||||
newHolder: RecyclerView.ViewHolder,
|
||||
preInfo: ItemHolderInfo,
|
||||
postInfo: ItemHolderInfo
|
||||
): Boolean {
|
||||
animateAppBar()
|
||||
return super.animateChange(oldHolder, newHolder, preInfo, postInfo)
|
||||
}
|
||||
|
||||
override fun animateChange(
|
||||
oldHolder: RecyclerView.ViewHolder?,
|
||||
newHolder: RecyclerView.ViewHolder?,
|
||||
fromX: Int,
|
||||
fromY: Int,
|
||||
toX: Int,
|
||||
toY: Int
|
||||
): Boolean {
|
||||
animateAppBar()
|
||||
return super.animateChange(oldHolder, newHolder, fromX, fromY, toX, toY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val Controller.mainRecyclerView: RecyclerView?
|
||||
get() = (this as? SettingsController)?.listView ?: (this as? BaseController<*>)?.mainRecycler
|
||||
|
||||
fun Controller.moveRecyclerViewUp(allTheWayUp: Boolean = false, scrollUpAnyway: Boolean = false) {
|
||||
if (activityBinding?.bigToolbar?.isVisible == false) return
|
||||
val recycler = mainRecyclerView ?: return
|
||||
val activityBinding = activityBinding ?: return
|
||||
val appBarOffset = activityBinding.appBar.toolbarDistanceToTop
|
||||
if (allTheWayUp && recycler.computeVerticalScrollOffset() - recycler.paddingTop <= fullAppBarHeight ?: activityBinding.appBar.preLayoutHeight) {
|
||||
(recycler.layoutManager as? LinearLayoutManager)?.scrollToPosition(0)
|
||||
recycler.post {
|
||||
activityBinding.appBar.updateAppBarAfterY(recycler)
|
||||
activityBinding.appBar.useSearchToolbarForMenu(false)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (scrollUpAnyway || recycler.computeVerticalScrollOffset() - recycler.paddingTop <= 0 - appBarOffset) {
|
||||
(recycler.layoutManager as? LinearLayoutManager)
|
||||
?.scrollToPositionWithOffset(0, activityBinding.appBar.yNeededForSmallToolbar)
|
||||
recycler.post {
|
||||
activityBinding.appBar.updateAppBarAfterY(recycler)
|
||||
activityBinding.appBar.useSearchToolbarForMenu(recycler.computeVerticalScrollOffset() != 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Controller.setAppBarBG(value: Float, includeTabView: Boolean = false) {
|
||||
val context = view?.context ?: return
|
||||
val floatingBar =
|
||||
(this as? FloatingSearchInterface)?.showFloatingBar() == true && !includeTabView
|
||||
if ((this as? BottomSheetController)?.sheetIsFullscreen() == true) return
|
||||
if (router.backstack.lastOrNull()?.controller != this) return
|
||||
if (!isControllerVisible) return
|
||||
if (floatingBar) {
|
||||
(activityBinding?.cardView as? CardView)?.setCardBackgroundColor(context.getResourceColor(R.attr.colorPrimaryVariant))
|
||||
activityBinding?.appBar?.setBackgroundColor(Color.TRANSPARENT)
|
||||
activity?.window?.statusBarColor = context.getResourceColor(android.R.attr.statusBarColor)
|
||||
if (this !is SmallToolbarInterface && activityBinding?.appBar?.useLargeToolbar == true) {
|
||||
val colorSurface = context.getResourceColor(R.attr.colorSurface)
|
||||
val color = ColorUtils.blendARGB(
|
||||
colorSurface,
|
||||
ColorUtils.setAlphaComponent(colorSurface, 0),
|
||||
value
|
||||
)
|
||||
activityBinding?.appBar?.backgroundColor = color
|
||||
} else {
|
||||
activityBinding?.appBar?.backgroundColor = Color.TRANSPARENT
|
||||
}
|
||||
if (activityBinding?.appBar?.isInvisible != true) {
|
||||
activity?.window?.statusBarColor =
|
||||
context.getResourceColor(android.R.attr.statusBarColor)
|
||||
}
|
||||
} else {
|
||||
val color = ColorUtils.blendARGB(
|
||||
context.getResourceColor(R.attr.colorSurface),
|
||||
|
@ -504,8 +622,10 @@ fun Controller.setAppBarBG(value: Float, includeTabView: Boolean = false) {
|
|||
value
|
||||
)
|
||||
activityBinding?.appBar?.setBackgroundColor(color)
|
||||
activity?.window?.statusBarColor =
|
||||
ColorUtils.setAlphaComponent(color, (0.87f * 255).roundToInt())
|
||||
if (activityBinding?.appBar?.isInvisible != true) {
|
||||
activity?.window?.statusBarColor =
|
||||
ColorUtils.setAlphaComponent(color, (0.87f * 255).roundToInt())
|
||||
}
|
||||
if ((this as? FloatingSearchInterface)?.showFloatingBar() == true) {
|
||||
val invColor = ColorUtils.blendARGB(
|
||||
context.getResourceColor(R.attr.colorSurface),
|
||||
|
@ -590,3 +710,14 @@ val Controller.activityBinding: MainActivityBinding?
|
|||
|
||||
val Controller.toolbarHeight: Int?
|
||||
get() = (activity as? MainActivity)?.toolbarHeight
|
||||
|
||||
/** Returns the expected height of the app bar - top insets, based on what the controller needs */
|
||||
val Controller.fullAppBarHeight: Int?
|
||||
get() = (activity as? MainActivity)?.bigToolbarHeight(
|
||||
(this as? FloatingSearchInterface)?.showFloatingBar() == true,
|
||||
this is TabbedInterface,
|
||||
this !is SmallToolbarInterface
|
||||
)
|
||||
|
||||
val Controller.isControllerVisible: Boolean
|
||||
get() = router.backstack.lastOrNull()?.controller == this
|
||||
|
|
|
@ -17,6 +17,7 @@ import android.graphics.Point
|
|||
import android.graphics.RenderEffect
|
||||
import android.graphics.Shader
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
import android.view.Gravity
|
||||
|
@ -44,6 +45,7 @@ import androidx.core.view.WindowInsetsAnimationCompat
|
|||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsCompat.Type.ime
|
||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.descendants
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.marginBottom
|
||||
|
@ -74,6 +76,7 @@ import eu.kanade.tachiyomi.util.system.pxToDp
|
|||
import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
|
||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
@ -194,13 +197,10 @@ fun View.applyBottomAnimatedInsets(
|
|||
)
|
||||
}
|
||||
|
||||
object ControllerViewWindowInsetsListener : OnApplyWindowInsetsListener {
|
||||
class ControllerViewWindowInsetsListener(private val topHeight: Int) : OnApplyWindowInsetsListener {
|
||||
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
|
||||
v.updateLayoutParams<FrameLayout.LayoutParams> {
|
||||
val attrsArray = intArrayOf(R.attr.mainActionBarSize)
|
||||
val array = v.context.obtainStyledAttributes(attrsArray)
|
||||
topMargin = insets.getInsets(systemBars()).top + array.getDimensionPixelSize(0, 0)
|
||||
array.recycle()
|
||||
topMargin = insets.getInsets(systemBars()).top + topHeight
|
||||
}
|
||||
return insets
|
||||
}
|
||||
|
@ -226,8 +226,8 @@ fun View.doOnApplyWindowInsetsCompat(f: (View, WindowInsetsCompat, ViewPaddingSt
|
|||
requestApplyInsetsWhenAttached()
|
||||
}
|
||||
|
||||
fun View.applyWindowInsetsForController() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(this, ControllerViewWindowInsetsListener)
|
||||
fun View.applyWindowInsetsForController(topHeight: Int) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(this, ControllerViewWindowInsetsListener(topHeight))
|
||||
requestApplyInsetsWhenAttached()
|
||||
}
|
||||
|
||||
|
@ -422,8 +422,18 @@ fun setCards(
|
|||
mainCard.strokeWidth = if (showOutline) 1.dpToPx else 0
|
||||
}
|
||||
|
||||
val View.backgroundColor
|
||||
var View.backgroundColor: Int?
|
||||
get() = (background as? ColorDrawable)?.color
|
||||
set(value) {
|
||||
if (value != null) setBackgroundColor(value) else background = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this ViewGroup's first descendant of specified class
|
||||
*/
|
||||
inline fun <reified T> ViewGroup.findChild(): T? {
|
||||
return children.find { it is T } as? T
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this ViewGroup's first descendant of specified class
|
||||
|
@ -493,6 +503,23 @@ fun Dialog.blurBehindWindow(
|
|||
}
|
||||
}
|
||||
|
||||
fun TextView.setTextColorAlpha(alpha: Int) {
|
||||
setTextColor(ColorUtils.setAlphaComponent(currentTextColor, alpha))
|
||||
}
|
||||
|
||||
fun View.updateGradiantBGRadius(ogRadius: Float, deviceRadius: Float, progress: Float, vararg updateOtherViews: View) {
|
||||
(background as? GradientDrawable)?.let { drawable ->
|
||||
val lerp = min(ogRadius, deviceRadius) * (1 - progress) +
|
||||
max(ogRadius, deviceRadius) * progress
|
||||
drawable.shape = GradientDrawable.RECTANGLE
|
||||
drawable.cornerRadii = floatArrayOf(lerp, lerp, lerp, lerp, 0f, 0f, 0f, 0f)
|
||||
background = drawable
|
||||
updateOtherViews.forEach {
|
||||
it.background = drawable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(31)
|
||||
fun View.animateBlur(
|
||||
@FloatRange(from = 0.1) from: Float,
|
||||
|
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
|||
import android.util.AttributeSet
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.util.system.pxToDp
|
||||
import kotlin.math.max
|
||||
|
@ -14,7 +14,7 @@ import kotlin.math.roundToInt
|
|||
class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
androidx.recyclerview.widget.RecyclerView(context, attrs) {
|
||||
|
||||
val manager = GridLayoutManager(context, 1)
|
||||
val manager = GridLayoutManagerAccurateOffset(context, 1)
|
||||
|
||||
var lastMeasuredWidth = 0
|
||||
var columnWidth = -1f
|
||||
|
|
|
@ -3,10 +3,13 @@ package eu.kanade.tachiyomi.widget
|
|||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.CommonViewEmptyBinding
|
||||
|
@ -43,6 +46,7 @@ class EmptyView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||
binding.textLabel.text = message
|
||||
|
||||
binding.actionsContainer.removeAllViews()
|
||||
binding.actionsContainer.isVisible = !actions.isNullOrEmpty()
|
||||
if (!actions.isNullOrEmpty()) {
|
||||
actions.forEach {
|
||||
val button =
|
||||
|
@ -51,8 +55,14 @@ class EmptyView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||
setText(it.resId)
|
||||
setOnClickListener(it.listener)
|
||||
}
|
||||
|
||||
binding.actionsContainer.addView(button)
|
||||
if (context.resources.configuration.screenHeightDp < 600) {
|
||||
button.textAlignment = View.TEXT_ALIGNMENT_TEXT_START
|
||||
button.updateLayoutParams<MarginLayoutParams> {
|
||||
width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
object EstimatedItemHeight {
|
||||
/** gives height of a view holder, or an estimated based on others, or a hard coded estimate */
|
||||
fun itemOrEstimatedHeight(
|
||||
pos: Int,
|
||||
itemViewType: Int?,
|
||||
childSizesMap: HashMap<Int, Int>,
|
||||
childTypeMap: HashMap<Int, Int>,
|
||||
childTypeHeightMap: HashMap<Int, HashMap<Int, Int>>,
|
||||
childTypeEstimateMap: HashMap<Int, Int>,
|
||||
childAvgHeightMap: HashMap<Int, Int>
|
||||
): Int {
|
||||
return if (childSizesMap[pos] != null) {
|
||||
childSizesMap[pos] ?: 0
|
||||
} else {
|
||||
val type = if (childTypeMap[pos] == null) {
|
||||
val t = itemViewType ?: 0
|
||||
childTypeMap[pos] = t
|
||||
t
|
||||
} else {
|
||||
childTypeMap[pos] ?: 0
|
||||
}
|
||||
when {
|
||||
childTypeEstimateMap[type] != null -> childTypeEstimateMap[type] ?: 0
|
||||
childAvgHeightMap[type] == null && !childTypeHeightMap[type]?.values.isNullOrEmpty() -> {
|
||||
val array = (childTypeHeightMap[type]?.values ?: mutableListOf(0)).toIntArray()
|
||||
childAvgHeightMap[type] = array
|
||||
.copyOfRange(0, min(array.size, 10))
|
||||
.average()
|
||||
.roundToInt()
|
||||
if (array.size >= 10) {
|
||||
childTypeEstimateMap[type] = childAvgHeightMap[type]!!
|
||||
}
|
||||
childAvgHeightMap[type] ?: 0
|
||||
}
|
||||
else -> childAvgHeightMap[type] ?: estimatedHeight(type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for estimates of heights of recycler view holders
|
||||
*
|
||||
* Only needed to provide in cases where a layout type only shows up when scroll
|
||||
* (for example: R.layout.chapters_item might not show until scrolling if
|
||||
* R.layout.manga_header_item is too tall
|
||||
*/
|
||||
private fun estimatedHeight(id: Int): Int {
|
||||
return when (id) {
|
||||
R.layout.recent_manga_item -> 92.dpToPx
|
||||
R.layout.recents_header_item -> 40.dpToPx
|
||||
R.layout.recent_chapters_section_item -> 32.dpToPx
|
||||
R.layout.chapters_item -> 60.dpToPx
|
||||
R.layout.manga_header_item -> 500.dpToPx
|
||||
R.layout.chapter_header_item -> 47.dpToPx
|
||||
R.layout.manga_grid_item -> 222.dpToPx
|
||||
R.layout.manga_list_item -> 52.dpToPx
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.tachiyomi.R
|
||||
import kotlin.math.max
|
||||
|
||||
class GridLayoutManagerAccurateOffset(context: Context?, spanCount: Int) : GridLayoutManager(context, spanCount) {
|
||||
|
||||
// map of child adapter position to its height.
|
||||
private val childSizesMap = HashMap<Int, Int>()
|
||||
private val childSpanMap = HashMap<Int, Int>()
|
||||
private val childTypeHeightMap = HashMap<Int, HashMap<Int, Int>>()
|
||||
private val childTypeMap = HashMap<Int, Int>()
|
||||
private val childTypeEstimateMap = HashMap<Int, Int>()
|
||||
var computedRange: Int? = null
|
||||
var rView: RecyclerView? = null
|
||||
|
||||
private val toolbarHeight by lazy {
|
||||
val attrsArray = intArrayOf(R.attr.mainActionBarSize)
|
||||
val array = (context ?: rView?.context)?.obtainStyledAttributes(attrsArray)
|
||||
val height = array?.getDimensionPixelSize(0, 0) ?: 0
|
||||
array?.recycle()
|
||||
height
|
||||
}
|
||||
|
||||
override fun onLayoutCompleted(state: RecyclerView.State) {
|
||||
super.onLayoutCompleted(state)
|
||||
computedRange = null
|
||||
for (i in 0 until childCount) {
|
||||
val child = getChildAt(i) ?: return
|
||||
val position = getPosition(child)
|
||||
childSizesMap[position] = child.height
|
||||
childSpanMap[position] = spanSizeLookup.getSpanSize(getPosition(child))
|
||||
val type = getItemViewType(child)
|
||||
childTypeMap[position] = type
|
||||
if (childTypeHeightMap[type] != null) {
|
||||
childTypeHeightMap[type]!![position] = child.height
|
||||
} else {
|
||||
childTypeHeightMap[type] = hashMapOf(position to child.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow(view: RecyclerView?) {
|
||||
super.onAttachedToWindow(view)
|
||||
rView = view
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow(view: RecyclerView?, recycler: RecyclerView.Recycler?) {
|
||||
super.onDetachedFromWindow(view, recycler)
|
||||
rView = null
|
||||
}
|
||||
|
||||
override fun computeVerticalScrollRange(state: RecyclerView.State): Int {
|
||||
if (childCount == 0) return 0
|
||||
computedRange?.let {
|
||||
return it
|
||||
}
|
||||
rView ?: return super.computeVerticalScrollRange(state)
|
||||
var scrolledY = 0
|
||||
var spanC = 0
|
||||
var maxHeight = 0
|
||||
val childAvgHeightMap = HashMap<Int, Int>()
|
||||
for (i in 0 until itemCount) {
|
||||
val height: Int = getItemHeight(i, childAvgHeightMap)
|
||||
val spanCurrentSize = childSpanMap[i] ?: spanSizeLookup.getSpanSize(i)
|
||||
if (spanCount <= spanCurrentSize) {
|
||||
scrolledY += height
|
||||
scrolledY += maxHeight
|
||||
maxHeight = 0
|
||||
spanC = 0
|
||||
} else if (spanCurrentSize == 1) {
|
||||
maxHeight = max(maxHeight, height)
|
||||
spanC++
|
||||
if (spanC <= spanCount) {
|
||||
scrolledY += maxHeight
|
||||
maxHeight = 0
|
||||
spanC = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
computedRange = scrolledY
|
||||
return scrolledY
|
||||
}
|
||||
|
||||
override fun computeVerticalScrollOffset(state: RecyclerView.State): Int {
|
||||
if (childCount == 0) {
|
||||
return 0
|
||||
}
|
||||
rView ?: return super.computeVerticalScrollOffset(state)
|
||||
val firstChild = getChildAt(0) ?: return 0
|
||||
val firstChildPosition = (0 until childCount)
|
||||
.mapNotNull { getChildAt(it) }
|
||||
.mapNotNull { pos -> getPosition(pos).takeIf { it != RecyclerView.NO_POSITION } }
|
||||
.minOrNull() ?: 0
|
||||
var scrolledY: Int = -firstChild.y.toInt()
|
||||
var spanC = 0
|
||||
var maxHeight = 0
|
||||
val childAvgHeightMap = HashMap<Int, Int>()
|
||||
for (i in 0 until firstChildPosition) {
|
||||
val height: Int = getItemHeight(i, childAvgHeightMap)
|
||||
val spanCurrentSize = childSpanMap[i] ?: spanSizeLookup.getSpanSize(i)
|
||||
if (spanCount <= spanCurrentSize) {
|
||||
scrolledY += height
|
||||
scrolledY += maxHeight
|
||||
maxHeight = 0
|
||||
spanC = 0
|
||||
} else if (spanCurrentSize == 1) {
|
||||
maxHeight = max(maxHeight, height)
|
||||
spanC++
|
||||
if (spanC <= spanCount) {
|
||||
scrolledY += maxHeight
|
||||
maxHeight = 0
|
||||
spanC = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
scrolledY += maxHeight
|
||||
return scrolledY + paddingTop
|
||||
}
|
||||
|
||||
private fun getItemHeight(pos: Int, childAvgHeightMap: HashMap<Int, Int>): Int {
|
||||
return EstimatedItemHeight.itemOrEstimatedHeight(
|
||||
pos,
|
||||
rView?.adapter?.getItemViewType(pos),
|
||||
childSizesMap,
|
||||
childTypeMap,
|
||||
childTypeHeightMap,
|
||||
childTypeEstimateMap,
|
||||
childAvgHeightMap
|
||||
)
|
||||
}
|
||||
|
||||
override fun findFirstVisibleItemPosition(): Int {
|
||||
return getFirstPos(rView, toolbarHeight)
|
||||
}
|
||||
|
||||
override fun findFirstCompletelyVisibleItemPosition(): Int {
|
||||
return getFirstCompletePos(rView, toolbarHeight)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
import androidx.core.view.marginTop
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
|
||||
|
||||
class LinearLayoutManagerAccurateOffset(context: Context?) : LinearLayoutManager(context) {
|
||||
|
||||
// map of child adapter position to its height.
|
||||
private val childSizesMap = HashMap<Int, Int>()
|
||||
private val childTypeMap = HashMap<Int, Int>()
|
||||
private val childTypeHeightMap = HashMap<Int, HashMap<Int, Int>>()
|
||||
private val childTypeEstimateMap = HashMap<Int, Int>()
|
||||
var rView: RecyclerView? = null
|
||||
var computedRange: Int? = null
|
||||
|
||||
private val toolbarHeight by lazy {
|
||||
val attrsArray = intArrayOf(R.attr.mainActionBarSize)
|
||||
val array = (context ?: rView?.context)?.obtainStyledAttributes(attrsArray)
|
||||
val height = array?.getDimensionPixelSize(0, 0) ?: 0
|
||||
array?.recycle()
|
||||
height
|
||||
}
|
||||
|
||||
override fun onLayoutCompleted(state: RecyclerView.State) {
|
||||
super.onLayoutCompleted(state)
|
||||
computedRange = null
|
||||
for (i in 0 until childCount) {
|
||||
val child = getChildAt(i) ?: return
|
||||
val position = getPosition(child)
|
||||
childSizesMap[position] = child.height
|
||||
val type = getItemViewType(child)
|
||||
childTypeMap[position] = type
|
||||
if (childTypeHeightMap[type] != null) {
|
||||
childTypeHeightMap[type]!![position] = child.height
|
||||
} else {
|
||||
childTypeHeightMap[type] = hashMapOf(position to child.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow(view: RecyclerView?) {
|
||||
super.onAttachedToWindow(view)
|
||||
rView = view
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow(view: RecyclerView?, recycler: RecyclerView.Recycler?) {
|
||||
super.onDetachedFromWindow(view, recycler)
|
||||
rView = null
|
||||
}
|
||||
|
||||
override fun computeVerticalScrollRange(state: RecyclerView.State): Int {
|
||||
if (childCount == 0) return 0
|
||||
computedRange?.let { return it }
|
||||
val childAvgHeightMap = HashMap<Int, Int>()
|
||||
val computedRange = (0 until itemCount).sumOf { getItemHeight(it, childAvgHeightMap) }
|
||||
this.computedRange = computedRange
|
||||
return computedRange
|
||||
}
|
||||
|
||||
override fun computeVerticalScrollOffset(state: RecyclerView.State): Int {
|
||||
if (childCount == 0) return 0
|
||||
val firstChild = getChildAt(0) ?: return 0
|
||||
val firstChildPosition = (0 to childCount).toList()
|
||||
.mapNotNull { getChildAt(it) }
|
||||
.mapNotNull { pos -> getPosition(pos).takeIf { it != RecyclerView.NO_POSITION } }
|
||||
.minOrNull() ?: 0
|
||||
val childAvgHeightMap = HashMap<Int, Int>()
|
||||
val scrolledY: Int = -firstChild.y.toInt() +
|
||||
(0 until firstChildPosition).sumOf { getItemHeight(it, childAvgHeightMap) }
|
||||
return scrolledY + paddingTop
|
||||
}
|
||||
|
||||
private fun getItemHeight(pos: Int, childAvgHeightMap: HashMap<Int, Int>): Int {
|
||||
return EstimatedItemHeight.itemOrEstimatedHeight(
|
||||
pos,
|
||||
rView?.adapter?.getItemViewType(pos),
|
||||
childSizesMap,
|
||||
childTypeMap,
|
||||
childTypeHeightMap,
|
||||
childTypeEstimateMap,
|
||||
childAvgHeightMap
|
||||
)
|
||||
}
|
||||
|
||||
override fun findFirstVisibleItemPosition(): Int {
|
||||
return getFirstPos(rView, toolbarHeight)
|
||||
}
|
||||
|
||||
override fun findFirstCompletelyVisibleItemPosition(): Int {
|
||||
return getFirstCompletePos(rView, toolbarHeight)
|
||||
}
|
||||
}
|
||||
|
||||
fun RecyclerView.LayoutManager.getFirstPos(recyclerView: RecyclerView?, toolbarHeight: Int): Int {
|
||||
val inset = recyclerView?.rootWindowInsetsCompat?.getInsets(systemBars())?.top ?: 0
|
||||
return (0 until childCount)
|
||||
.mapNotNull { getChildAt(it) }
|
||||
.filter {
|
||||
val isLibraryHeader = getItemViewType(it) == R.layout.library_category_header_item
|
||||
val marginTop = if (isLibraryHeader) it.findViewById<TextView>(R.id.category_title)?.marginTop ?: 0 else 0
|
||||
it.bottom >= inset + toolbarHeight - marginTop
|
||||
}
|
||||
.mapNotNull { pos -> getPosition(pos).takeIf { it != RecyclerView.NO_POSITION } }
|
||||
.minOrNull() ?: RecyclerView.NO_POSITION
|
||||
}
|
||||
|
||||
fun RecyclerView.LayoutManager.getFirstCompletePos(recyclerView: RecyclerView?, toolbarHeight: Int): Int {
|
||||
val inset = recyclerView?.rootWindowInsetsCompat?.getInsets(systemBars())?.top ?: 0
|
||||
return (0 until childCount)
|
||||
.mapNotNull { getChildAt(it) }
|
||||
.filter {
|
||||
val isLibraryHeader = getItemViewType(it) == R.layout.library_category_header_item
|
||||
val marginTop = if (isLibraryHeader) it.findViewById<TextView>(R.id.category_title)?.marginTop ?: 0 else 0
|
||||
it.y >= inset + toolbarHeight - marginTop
|
||||
}
|
||||
.mapNotNull { pos -> getPosition(pos).takeIf { it != RecyclerView.NO_POSITION } }
|
||||
.minOrNull() ?: RecyclerView.NO_POSITION
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package eu.kanade.tachiyomi.widget.preference
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceViewHolder
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
/**
|
||||
* PreferenceCategory that hides the title placeholder layout if the title is unset
|
||||
*/
|
||||
class AdaptiveTitlePreferenceCategory(context: Context) : PreferenceCategory(context) {
|
||||
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||
super.onBindViewHolder(holder)
|
||||
if (title.isNullOrBlank()) {
|
||||
holder.itemView.updateLayoutParams<RecyclerView.LayoutParams> {
|
||||
height = 0
|
||||
topMargin = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
51
app/src/main/res/layout-h600dp/common_view_empty.xml
Normal file
51
app/src/main/res/layout-h600dp/common_view_empty.xml
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/text_label"
|
||||
app:layout_constraintDimensionRatio="h,1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_max="128dp"
|
||||
app:layout_constraintHeight_min="24dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_constraintWidth_max="128dp"
|
||||
app:layout_constraintWidth_min="24dp"
|
||||
tools:src="@drawable/ic_file_download_24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_label"
|
||||
style="?textAppearanceLabelMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toTopOf="@id/actions_container"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/image_view"
|
||||
tools:text="Label" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/actions_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_label" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
</com.bluelinelabs.conductor.ChangeHandlerFrameLayout>
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
<eu.kanade.tachiyomi.ui.base.ExpandedAppBarLayout
|
||||
android:id="@+id/app_bar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -35,7 +35,9 @@
|
|||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
app:titleTextColor="?actionBarTintColor"
|
||||
android:layout_height="?attr/mainActionBarSize"
|
||||
android:layout_height="?mainActionBarSize"
|
||||
android:translationZ="5dp"
|
||||
android:outlineProvider="none"
|
||||
app:collapseIcon="@drawable/ic_arrow_back_24dp"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
|
@ -55,10 +57,49 @@
|
|||
tools:text="Title Text" />
|
||||
</eu.kanade.tachiyomi.ui.base.CenteredToolbar>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/big_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:paddingBottom="12dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/big_icon"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp"
|
||||
tools:srcCompat="@mipmap/ic_launcher"
|
||||
app:layout_constraintBottom_toBottomOf="@id/big_title"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/big_title"
|
||||
android:layout_marginStart="16dp"
|
||||
/>
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/big_title"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
style="?textAppearanceHeadlineLarge"
|
||||
android:layout_width="0dp"
|
||||
android:maxLines="2"
|
||||
android:layout_marginTop="52dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toEndOf="@id/big_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:drawableTint="?actionBarTintColor"
|
||||
android:ellipsize="end"
|
||||
android:layout_gravity="start"
|
||||
android:textColor="?actionBarTintColor"
|
||||
tools:text="Title Text" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/card_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/mainActionBarSize" >
|
||||
android:layout_height="?mainActionBarSize" >
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/card_view"
|
||||
|
@ -66,10 +107,10 @@
|
|||
android:layout_marginTop="4dp"
|
||||
app:cardBackgroundColor="?colorPrimaryVariant"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginStart="10dp"
|
||||
app:strokeWidth="0dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:cardCornerRadius="30dp"
|
||||
app:cardCornerRadius="24dp"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<eu.kanade.tachiyomi.ui.base.FloatingToolbar
|
||||
|
@ -162,7 +203,7 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:tabGravity="fill"/>
|
||||
</FrameLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
</eu.kanade.tachiyomi.ui.base.ExpandedAppBarLayout>
|
||||
|
||||
|
||||
<com.google.android.material.navigationrail.NavigationRailView
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
android:id="@+id/progress"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|center_horizontal"
|
||||
android:visibility="gone"/>
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
@ -32,9 +33,11 @@
|
|||
|
||||
<eu.kanade.tachiyomi.widget.EmptyView
|
||||
android:id="@+id/empty_view"
|
||||
tools:paddingTop="300dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
|
@ -3,23 +3,23 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
android:layout_height="wrap_content">
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/text_label"
|
||||
app:layout_constraintDimensionRatio="h,1:1"
|
||||
android:layout_width="128dp"
|
||||
android:layout_height="128dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintDimensionRatio="W,1:1"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_max="128dp"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintHeight_min="24dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/text_label"
|
||||
app:layout_constraintWidth_max="128dp"
|
||||
app:layout_constraintWidth_min="24dp"
|
||||
tools:src="@drawable/ic_file_download_24dp" />
|
||||
|
@ -29,23 +29,26 @@
|
|||
style="?textAppearanceLabelMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:gravity="center"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="0dp"
|
||||
app:layout_constrainedWidth="true"
|
||||
android:textAlignment="textStart"
|
||||
app:layout_constraintBottom_toTopOf="@id/actions_container"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/image_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/image_view"
|
||||
tools:text="Label" />
|
||||
tools:text="Label Label Label Label Label Label Label Label Label Label" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/actions_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:gravity="start"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/image_view"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_label" />
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
app:behavior_peekHeight="48sp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/sheet_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -44,9 +44,10 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:paddingTop="6dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/pill"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
|
@ -54,7 +55,36 @@
|
|||
android:textColor="?actionBarTintColor"
|
||||
android:textSize="18sp"
|
||||
tools:text="Downloads" />
|
||||
</LinearLayout>
|
||||
|
||||
<eu.kanade.tachiyomi.ui.base.CenteredToolbar
|
||||
android:id="@+id/sheet_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?mainActionBarSize"
|
||||
android:background="@android:color/transparent"
|
||||
app:collapseIcon="@drawable/ic_arrow_back_24dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/pill"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
app:menu="@menu/download_queue"
|
||||
app:navigationIcon="@drawable/ic_close_24dp"
|
||||
app:navigationIconTint="?actionBarTintColor"
|
||||
app:titleTextColor="?actionBarTintColor" >
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/toolbar_title"
|
||||
style="?textAppearanceTitleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:drawableTint="?actionBarTintColor"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="@string/extensions"
|
||||
android:textColor="?actionBarTintColor"
|
||||
android:textSize="20sp"
|
||||
tools:text="Title Text" />
|
||||
</eu.kanade.tachiyomi.ui.base.CenteredToolbar>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/recycler_layout"
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
app:behavior_peekHeight="60sp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/sheet_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -37,6 +37,40 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<eu.kanade.tachiyomi.ui.base.CenteredToolbar
|
||||
android:id="@+id/sheet_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
app:collapseIcon="@drawable/ic_arrow_back_24dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/tabs"
|
||||
app:layout_constraintHeight_max="?mainActionBarSize"
|
||||
app:layout_constraintTop_toBottomOf="@id/pill"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
app:navigationIcon="@drawable/ic_close_24dp"
|
||||
app:navigationIconTint="?actionBarTintColor"
|
||||
app:titleTextColor="?actionBarTintColor"
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/toolbar_title"
|
||||
style="?textAppearanceTitleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:drawableTint="?actionBarTintColor"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="@string/extensions"
|
||||
android:textColor="?actionBarTintColor"
|
||||
android:textSize="20sp"
|
||||
tools:drawableEnd="@drawable/ic_arrow_drop_down_24dp"
|
||||
tools:drawableStart="@drawable/ic_blank_24dp"
|
||||
tools:text="Title Text" />
|
||||
</eu.kanade.tachiyomi.ui.base.CenteredToolbar>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
style="@style/Theme.Widget.Tabs"
|
||||
|
@ -44,15 +78,14 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/menu"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/pill"
|
||||
app:tabTextColor="@color/tabs_selector_background"
|
||||
app:tabIndicatorColor="?attr/colorSecondary"
|
||||
app:tabMaxWidth="0dp"
|
||||
app:tabGravity="fill"
|
||||
app:tabMode="fixed" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/pager"
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
android:layout_height="match_parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="1.0">
|
||||
app:layout_constraintVertical_bias="0.0">
|
||||
|
||||
</com.bluelinelabs.conductor.ChangeHandlerFrameLayout>
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
<eu.kanade.tachiyomi.ui.base.ExpandedAppBarLayout
|
||||
android:id="@+id/app_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -35,6 +35,8 @@
|
|||
android:layout_width="match_parent"
|
||||
app:titleTextColor="?actionBarTintColor"
|
||||
android:layout_height="?mainActionBarSize"
|
||||
android:translationZ="5dp"
|
||||
android:outlineProvider="none"
|
||||
app:collapseIcon="@drawable/ic_arrow_back_24dp"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
|
@ -54,6 +56,45 @@
|
|||
tools:text="Title Text" />
|
||||
</eu.kanade.tachiyomi.ui.base.CenteredToolbar>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/big_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:paddingBottom="12dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/big_icon"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp"
|
||||
tools:srcCompat="@mipmap/ic_launcher"
|
||||
app:layout_constraintBottom_toBottomOf="@id/big_title"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/big_title"
|
||||
android:layout_marginStart="16dp"
|
||||
/>
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/big_title"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
style="?textAppearanceHeadlineLarge"
|
||||
android:layout_width="0dp"
|
||||
android:maxLines="2"
|
||||
android:layout_marginTop="52dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toEndOf="@id/big_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:drawableTint="?actionBarTintColor"
|
||||
android:ellipsize="end"
|
||||
android:layout_gravity="start"
|
||||
android:textColor="?actionBarTintColor"
|
||||
tools:text="Title Text" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/card_frame"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -161,7 +202,7 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:tabGravity="fill"/>
|
||||
</FrameLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
</eu.kanade.tachiyomi.ui.base.ExpandedAppBarLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/bottom_view"
|
||||
|
|
12
app/src/main/res/menu/search.xml
Normal file
12
app/src/main/res/menu/search.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_search"
|
||||
android:icon="@drawable/ic_search_24dp"
|
||||
android:title="@string/search"
|
||||
android:visible="false"
|
||||
app:actionViewClass="eu.kanade.tachiyomi.ui.base.MiniSearchView"
|
||||
app:showAsAction="collapseActionView|ifRoom" />
|
||||
</menu>
|
|
@ -14,7 +14,8 @@
|
|||
android:icon="@drawable/ic_sort_24dp"
|
||||
app:showAsAction="ifRoom">
|
||||
<menu>
|
||||
<group android:checkableBehavior="single">
|
||||
<group android:checkableBehavior="single"
|
||||
android:id="@+id/action_sort_group">
|
||||
<item
|
||||
android:id="@+id/action_sort_alpha"
|
||||
android:title="@string/alphabetically"/>
|
||||
|
|
|
@ -90,11 +90,7 @@
|
|||
<string name="category_is_empty">Category is empty</string>
|
||||
<string name="show_categories_while_filtering">Show empty categories while filtering</string>
|
||||
<string name="no_matches_for_filters_short">No matches for your filters</string>
|
||||
<string name="top_category">Top category (%1$s)</string>
|
||||
<string name="default_category">Default category</string>
|
||||
<string name="first_category">First category</string>
|
||||
<string name="categories_on_manual">Categories to update when manually refreshing</string>
|
||||
<string name="categories_in_global_update">All categories in global update</string>
|
||||
<string name="confirm_category_deletion">Delete category?</string>
|
||||
<string name="confirm_category_deletion_message">Manga in this category will moved into the
|
||||
default category.</string>
|
||||
|
@ -116,7 +112,6 @@
|
|||
|
||||
<!-- Updates -->
|
||||
<string name="update">Update</string>
|
||||
<string name="what_should_update">What should update?</string>
|
||||
<string name="updating_">Updating %1$s</string>
|
||||
<string name="update_available">Update available</string>
|
||||
<string name="new_version_available">New version available!</string>
|
||||
|
@ -696,6 +691,8 @@
|
|||
<string name="pure_black_dark_mode">Pure black dark mode</string>
|
||||
<string name="details_page">Details page</string>
|
||||
<string name="theme_buttons_based_on_cover">Theme buttons based on cover</string>
|
||||
<string name="expanded_toolbar">Expanded toolbar</string>
|
||||
<string name="show_larger_toolbar">Show a larger, expanded toolbar at the top of most pages (does not show on smaller devices regardless of setting)</string>
|
||||
<string name="hides_on_scroll">Hides when scrolling</string>
|
||||
<string name="hide_app_block_screenshots">Hide app contents when switching apps and block screenshots</string>
|
||||
<string name="hide_notification_content">Hide notification content</string>
|
||||
|
|
|
@ -126,6 +126,8 @@
|
|||
|
||||
<style name="BottomSheetDialogTheme" parent="@style/ThemeOverlay.Material3.BottomSheetDialog">
|
||||
<item name="paddingBottomSystemWindowInsets">false</item>
|
||||
<item name="paddingLeftSystemWindowInsets">false</item>
|
||||
<item name="paddingRightSystemWindowInsets">false</item>
|
||||
</style>
|
||||
|
||||
<style name="OverflowDialogTheme" parent="BottomSheetDialogTheme">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue