mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-22 19:31:48 +01:00
More bottom sheet improvements (#5183)
* Edge-to-edge bottom sheet when possible * ReaderSettingsSheet: Animate background dim changes * Adjust modal bottom sheet in-out animation * Use public method to get bottom sheet behavior * Set bottom sheet peek size to 50% screen height The current auto peek height gives too low value on landscape orientation * Set bottom sheet navigation bar scrim when needed
This commit is contained in:
parent
aed6e12119
commit
9f744bc445
@ -33,7 +33,7 @@ class SourceFilterSheet(
|
|||||||
|
|
||||||
override fun show() {
|
override fun show() {
|
||||||
super.show()
|
super.show()
|
||||||
sheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setFilters(items: List<IFlexible<*>>) {
|
fun setFilters(items: List<IFlexible<*>>) {
|
||||||
|
@ -2,8 +2,6 @@ package eu.kanade.tachiyomi.ui.main
|
|||||||
|
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -52,9 +50,8 @@ import eu.kanade.tachiyomi.ui.recent.history.HistoryController
|
|||||||
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
|
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||||
import eu.kanade.tachiyomi.util.system.InternalResourceHelper
|
|
||||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import eu.kanade.tachiyomi.util.view.setNavigationBarTransparentCompat
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.drop
|
import kotlinx.coroutines.flow.drop
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
@ -119,14 +116,7 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
|
|||||||
// Make sure navigation bar is on bottom before we modify it
|
// Make sure navigation bar is on bottom before we modify it
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
|
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
|
||||||
if (insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom > 0) {
|
if (insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom > 0) {
|
||||||
window.navigationBarColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
|
window.setNavigationBarTransparentCompat(this)
|
||||||
!InternalResourceHelper.getBoolean(this, "config_navBarNeedsScrim", true)
|
|
||||||
) {
|
|
||||||
Color.TRANSPARENT
|
|
||||||
} else {
|
|
||||||
// Set navbar scrim 70% of navigationBarColor
|
|
||||||
getResourceColor(android.R.attr.navigationBarColor, 0.7F)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
insets
|
insets
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ class TrackSheet(
|
|||||||
override fun show() {
|
override fun show() {
|
||||||
super.show()
|
super.show()
|
||||||
controller.presenter.refreshTrackers()
|
controller.presenter.refreshTrackers()
|
||||||
sheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onNextTrackers(trackers: List<TrackItem>) {
|
fun onNextTrackers(trackers: List<TrackItem>) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package eu.kanade.tachiyomi.ui.reader.setting
|
package eu.kanade.tachiyomi.ui.reader.setting
|
||||||
|
|
||||||
|
import android.animation.ValueAnimator
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
@ -16,13 +17,21 @@ class ReaderSettingsSheet(
|
|||||||
private val generalSettings = ReaderGeneralSettings(activity)
|
private val generalSettings = ReaderGeneralSettings(activity)
|
||||||
private val colorFilterSettings = ReaderColorFilterSettings(activity)
|
private val colorFilterSettings = ReaderColorFilterSettings(activity)
|
||||||
|
|
||||||
|
private val backgroundDimAnimator by lazy {
|
||||||
|
val sheetBackgroundDim = window?.attributes?.dimAmount ?: 0.25f
|
||||||
|
ValueAnimator.ofFloat(sheetBackgroundDim, 0f).also { valueAnimator ->
|
||||||
|
valueAnimator.duration = 250
|
||||||
|
valueAnimator.addUpdateListener {
|
||||||
|
window?.setDimAmount(it.animatedValue as Float)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
sheetBehavior.isFitToContents = false
|
behavior.isFitToContents = false
|
||||||
sheetBehavior.halfExpandedRatio = 0.25f
|
behavior.halfExpandedRatio = 0.25f
|
||||||
|
|
||||||
val sheetBackgroundDim = window?.attributes?.dimAmount ?: 0.25f
|
|
||||||
|
|
||||||
val filterTabIndex = getTabViews().indexOf(colorFilterSettings)
|
val filterTabIndex = getTabViews().indexOf(colorFilterSettings)
|
||||||
binding.tabs.addOnTabSelectedListener(object : SimpleTabSelectedListener() {
|
binding.tabs.addOnTabSelectedListener(object : SimpleTabSelectedListener() {
|
||||||
@ -30,7 +39,15 @@ class ReaderSettingsSheet(
|
|||||||
val isFilterTab = tab?.position == filterTabIndex
|
val isFilterTab = tab?.position == filterTabIndex
|
||||||
|
|
||||||
// Remove dimmed backdrop so color filter changes can be previewed
|
// Remove dimmed backdrop so color filter changes can be previewed
|
||||||
window?.setDimAmount(if (isFilterTab) 0f else sheetBackgroundDim)
|
backgroundDimAnimator.run {
|
||||||
|
if (isFilterTab) {
|
||||||
|
if (animatedFraction < 1f) {
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
} else if (animatedFraction > 0f) {
|
||||||
|
reverse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Hide toolbars
|
// Hide toolbars
|
||||||
if (activity.menuVisible != !isFilterTab) {
|
if (activity.menuVisible != !isFilterTab) {
|
||||||
|
@ -15,8 +15,11 @@ import android.content.res.Resources
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
|
import android.view.Display
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.AttrRes
|
import androidx.annotation.AttrRes
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
@ -172,6 +175,14 @@ val Context.powerManager: PowerManager
|
|||||||
val Context.keyguardManager: KeyguardManager
|
val Context.keyguardManager: KeyguardManager
|
||||||
get() = getSystemService()!!
|
get() = getSystemService()!!
|
||||||
|
|
||||||
|
val Context.displayCompat: Display?
|
||||||
|
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
display
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
getSystemService<WindowManager>()?.defaultDisplay
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method to acquire a partial wake lock.
|
* Convenience method to acquire a partial wake lock.
|
||||||
*/
|
*/
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
package eu.kanade.tachiyomi.util.view
|
package eu.kanade.tachiyomi.util.view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Build
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
|
import eu.kanade.tachiyomi.util.system.InternalResourceHelper
|
||||||
|
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||||
|
|
||||||
fun Window.showBar() {
|
fun Window.showBar() {
|
||||||
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
|
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
|
||||||
@ -22,3 +27,18 @@ fun Window.defaultBar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Window.isDefaultBar() = decorView.systemUiVisibility == View.SYSTEM_UI_FLAG_VISIBLE
|
fun Window.isDefaultBar() = decorView.systemUiVisibility == View.SYSTEM_UI_FLAG_VISIBLE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets navigation bar color to transparent if system's config_navBarNeedsScrim is false,
|
||||||
|
* otherwise it will use the theme navigationBarColor with 70% opacity.
|
||||||
|
*/
|
||||||
|
fun Window.setNavigationBarTransparentCompat(context: Context) {
|
||||||
|
navigationBarColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
|
||||||
|
!InternalResourceHelper.getBoolean(context, "config_navBarNeedsScrim", true)
|
||||||
|
) {
|
||||||
|
Color.TRANSPARENT
|
||||||
|
} else {
|
||||||
|
// Set navbar scrim 70% of navigationBarColor
|
||||||
|
context.getResourceColor(android.R.attr.navigationBarColor, 0.7F)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@ import android.view.ViewGroup
|
|||||||
import android.widget.CheckBox
|
import android.widget.CheckBox
|
||||||
import android.widget.CheckedTextView
|
import android.widget.CheckedTextView
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
|
import android.widget.FrameLayout
|
||||||
import android.widget.RadioButton
|
import android.widget.RadioButton
|
||||||
import android.widget.Spinner
|
import android.widget.Spinner
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
@ -16,7 +17,6 @@ import androidx.core.view.ViewCompat
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.R
|
import com.google.android.material.R
|
||||||
import com.google.android.material.internal.ScrimInsetsFrameLayout
|
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import eu.kanade.tachiyomi.util.view.inflate
|
import eu.kanade.tachiyomi.util.view.inflate
|
||||||
import eu.kanade.tachiyomi.R as TR
|
import eu.kanade.tachiyomi.R as TR
|
||||||
@ -27,7 +27,7 @@ open class SimpleNavigationView @JvmOverloads constructor(
|
|||||||
context: Context,
|
context: Context,
|
||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyleAttr: Int = 0
|
defStyleAttr: Int = 0
|
||||||
) : ScrimInsetsFrameLayout(context, attrs, defStyleAttr) {
|
) : FrameLayout(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recycler view containing all the items.
|
* Recycler view containing all the items.
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
package eu.kanade.tachiyomi.widget.sheet
|
package eu.kanade.tachiyomi.widget.sheet
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.DisplayMetrics
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.util.system.displayCompat
|
||||||
|
import eu.kanade.tachiyomi.util.view.setNavigationBarTransparentCompat
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
abstract class BaseBottomSheetDialog(context: Context) : BottomSheetDialog(context) {
|
abstract class BaseBottomSheetDialog(context: Context) : BottomSheetDialog(context) {
|
||||||
|
|
||||||
internal lateinit var sheetBehavior: BottomSheetBehavior<*>
|
|
||||||
|
|
||||||
abstract fun createView(inflater: LayoutInflater): View
|
abstract fun createView(inflater: LayoutInflater): View
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -21,12 +27,39 @@ abstract class BaseBottomSheetDialog(context: Context) : BottomSheetDialog(conte
|
|||||||
val rootView = createView(layoutInflater)
|
val rootView = createView(layoutInflater)
|
||||||
setContentView(rootView)
|
setContentView(rootView)
|
||||||
|
|
||||||
sheetBehavior = BottomSheetBehavior.from(rootView.parent as ViewGroup)
|
|
||||||
|
|
||||||
// Enforce max width for tablets
|
// Enforce max width for tablets
|
||||||
val width = context.resources.getDimensionPixelSize(R.dimen.bottom_sheet_width)
|
val width = context.resources.getDimensionPixelSize(R.dimen.bottom_sheet_width)
|
||||||
if (width > 0) {
|
if (width > 0) {
|
||||||
sheetBehavior.maxWidth = width
|
behavior.maxWidth = width
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set peek height to 50% display height
|
||||||
|
context.displayCompat?.let {
|
||||||
|
val metrics = DisplayMetrics()
|
||||||
|
it.getRealMetrics(metrics)
|
||||||
|
behavior.peekHeight = metrics.heightPixels / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set navbar color to transparent for edge-to-edge bottom sheet if we can use light navigation bar
|
||||||
|
// TODO Replace deprecated systemUiVisibility when material-components uses new API to modify status bar icons
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
window?.setNavigationBarTransparentCompat(context)
|
||||||
|
val isDarkMode = when (Injekt.get<PreferencesHelper>().themeMode().get()) {
|
||||||
|
PreferenceValues.ThemeMode.light -> false
|
||||||
|
PreferenceValues.ThemeMode.dark -> true
|
||||||
|
PreferenceValues.ThemeMode.system ->
|
||||||
|
context.resources.configuration.uiMode and
|
||||||
|
Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||||
|
}
|
||||||
|
val bottomSheet = rootView.parent as ViewGroup
|
||||||
|
var flags = bottomSheet.systemUiVisibility
|
||||||
|
flags = if (isDarkMode) {
|
||||||
|
flags and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
|
||||||
|
} else {
|
||||||
|
flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||||
|
}
|
||||||
|
bottomSheet.systemUiVisibility = flags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
app/src/main/res/anim/bottom_sheet_slide_in.xml
Normal file
10
app/src/main/res/anim/bottom_sheet_slide_in.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:duration="300"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in">
|
||||||
|
|
||||||
|
<translate
|
||||||
|
android:fromYDelta="100%p"
|
||||||
|
android:toYDelta="0" />
|
||||||
|
|
||||||
|
</set>
|
10
app/src/main/res/anim/bottom_sheet_slide_out.xml
Normal file
10
app/src/main/res/anim/bottom_sheet_slide_out.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:duration="300"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in">
|
||||||
|
|
||||||
|
<translate
|
||||||
|
android:fromYDelta="0"
|
||||||
|
android:toYDelta="100%p" />
|
||||||
|
|
||||||
|
</set>
|
@ -100,8 +100,9 @@
|
|||||||
<style name="Theme.BottomSheet" parent="ThemeOverlay.MaterialComponents.BottomSheetDialog">
|
<style name="Theme.BottomSheet" parent="ThemeOverlay.MaterialComponents.BottomSheetDialog">
|
||||||
<item name="android:windowIsFloating">false</item>
|
<item name="android:windowIsFloating">false</item>
|
||||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
<item name="android:navigationBarColor">?attr/colorPrimary</item>
|
<item name="android:navigationBarColor">?attr/colorSurface</item>
|
||||||
<item name="bottomSheetStyle">@style/Theme.BottomSheet.Style</item>
|
<item name="bottomSheetStyle">@style/Theme.BottomSheet.Style</item>
|
||||||
|
<item name="android:windowAnimationStyle">@style/Animation.BottomSheetDialog</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.BottomSheet.Style" parent="Widget.MaterialComponents.BottomSheet">
|
<style name="Theme.BottomSheet.Style" parent="Widget.MaterialComponents.BottomSheet">
|
||||||
@ -116,6 +117,11 @@
|
|||||||
<item name="cornerSizeBottomLeft">0dp</item>
|
<item name="cornerSizeBottomLeft">0dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="Animation.BottomSheetDialog" parent="Animation.AppCompat.Dialog">
|
||||||
|
<item name="android:windowEnterAnimation">@anim/bottom_sheet_slide_in</item>
|
||||||
|
<item name="android:windowExitAnimation">@anim/bottom_sheet_slide_out</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<!--===============-->
|
<!--===============-->
|
||||||
<!--Text Appearance-->
|
<!--Text Appearance-->
|
||||||
|
Loading…
Reference in New Issue
Block a user