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:
Ivan Iskandar 2021-05-29 09:36:09 +07:00 committed by GitHub
parent aed6e12119
commit 9f744bc445
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 125 additions and 28 deletions

View File

@ -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<*>>) {

View File

@ -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
} }

View File

@ -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>) {

View File

@ -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) {

View File

@ -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.
*/ */

View File

@ -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)
}
}

View File

@ -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.

View File

@ -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
} }
} }
} }

View 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>

View 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>

View File

@ -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-->