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
11 changed files with 125 additions and 28 deletions
|
@ -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<*>>) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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">
|
||||
<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-->
|
||||
|
|
Reference in a new issue