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() {
super.show()
sheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
fun setFilters(items: List<IFlexible<*>>) {

View file

@ -2,8 +2,6 @@ package eu.kanade.tachiyomi.ui.main
import android.app.SearchManager
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.Gravity
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.util.lang.launchIO
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.view.setNavigationBarTransparentCompat
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.launchIn
@ -119,14 +116,7 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
// Make sure navigation bar is on bottom before we modify it
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
if (insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom > 0) {
window.navigationBarColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
!InternalResourceHelper.getBoolean(this, "config_navBarNeedsScrim", true)
) {
Color.TRANSPARENT
} else {
// Set navbar scrim 70% of navigationBarColor
getResourceColor(android.R.attr.navigationBarColor, 0.7F)
}
window.setNavigationBarTransparentCompat(this)
}
insets
}

View file

@ -53,7 +53,7 @@ class TrackSheet(
override fun show() {
super.show()
controller.presenter.refreshTrackers()
sheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
}
fun onNextTrackers(trackers: List<TrackItem>) {

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.reader.setting
import android.animation.ValueAnimator
import android.os.Bundle
import com.google.android.material.tabs.TabLayout
import eu.kanade.tachiyomi.R
@ -16,13 +17,21 @@ class ReaderSettingsSheet(
private val generalSettings = ReaderGeneralSettings(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?) {
super.onCreate(savedInstanceState)
sheetBehavior.isFitToContents = false
sheetBehavior.halfExpandedRatio = 0.25f
val sheetBackgroundDim = window?.attributes?.dimAmount ?: 0.25f
behavior.isFitToContents = false
behavior.halfExpandedRatio = 0.25f
val filterTabIndex = getTabViews().indexOf(colorFilterSettings)
binding.tabs.addOnTabSelectedListener(object : SimpleTabSelectedListener() {
@ -30,7 +39,15 @@ class ReaderSettingsSheet(
val isFilterTab = tab?.position == filterTabIndex
// 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
if (activity.menuVisible != !isFilterTab) {

View file

@ -15,8 +15,11 @@ import android.content.res.Resources
import android.graphics.Color
import android.net.ConnectivityManager
import android.net.Uri
import android.os.Build
import android.os.PowerManager
import android.view.Display
import android.view.View
import android.view.WindowManager
import android.widget.Toast
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
@ -172,6 +175,14 @@ val Context.powerManager: PowerManager
val Context.keyguardManager: KeyguardManager
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.
*/

View file

@ -1,7 +1,12 @@
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.Window
import eu.kanade.tachiyomi.util.system.InternalResourceHelper
import eu.kanade.tachiyomi.util.system.getResourceColor
fun Window.showBar() {
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
/**
* 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.CheckedTextView
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.RadioButton
import android.widget.Spinner
import android.widget.TextView
@ -16,7 +17,6 @@ import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.R
import com.google.android.material.internal.ScrimInsetsFrameLayout
import com.google.android.material.textfield.TextInputLayout
import eu.kanade.tachiyomi.util.view.inflate
import eu.kanade.tachiyomi.R as TR
@ -27,7 +27,7 @@ open class SimpleNavigationView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ScrimInsetsFrameLayout(context, attrs, defStyleAttr) {
) : FrameLayout(context, attrs, defStyleAttr) {
/**
* Recycler view containing all the items.

View file

@ -1,18 +1,24 @@
package eu.kanade.tachiyomi.widget.sheet
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
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) {
internal lateinit var sheetBehavior: BottomSheetBehavior<*>
abstract fun createView(inflater: LayoutInflater): View
override fun onCreate(savedInstanceState: Bundle?) {
@ -21,12 +27,39 @@ abstract class BaseBottomSheetDialog(context: Context) : BottomSheetDialog(conte
val rootView = createView(layoutInflater)
setContentView(rootView)
sheetBehavior = BottomSheetBehavior.from(rootView.parent as ViewGroup)
// Enforce max width for tablets
val width = context.resources.getDimensionPixelSize(R.dimen.bottom_sheet_width)
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">
<item name="android:windowIsFloating">false</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="android:windowAnimationStyle">@style/Animation.BottomSheetDialog</item>
</style>
<style name="Theme.BottomSheet.Style" parent="Widget.MaterialComponents.BottomSheet">
@ -116,6 +117,11 @@
<item name="cornerSizeBottomLeft">0dp</item>
</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-->