mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +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:
		@@ -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 New Issue
	
	Block a user