From 9f744bc44589a3e44fb8b655615c2184d1748c0a Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Sat, 29 May 2021 09:36:09 +0700 Subject: [PATCH] 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 --- .../browse/source/browse/SourceFilterSheet.kt | 2 +- .../kanade/tachiyomi/ui/main/MainActivity.kt | 14 +----- .../tachiyomi/ui/manga/track/TrackSheet.kt | 2 +- .../ui/reader/setting/ReaderSettingsSheet.kt | 27 ++++++++--- .../util/system/ContextExtensions.kt | 11 +++++ .../tachiyomi/util/view/WindowExtensions.kt | 20 +++++++++ .../tachiyomi/widget/SimpleNavigationView.kt | 4 +- .../widget/sheet/BaseBottomSheetDialog.kt | 45 ++++++++++++++++--- .../main/res/anim/bottom_sheet_slide_in.xml | 10 +++++ .../main/res/anim/bottom_sheet_slide_out.xml | 10 +++++ app/src/main/res/values/styles.xml | 8 +++- 11 files changed, 125 insertions(+), 28 deletions(-) create mode 100644 app/src/main/res/anim/bottom_sheet_slide_in.xml create mode 100644 app/src/main/res/anim/bottom_sheet_slide_out.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt index fe1740752e..ddf9a9e820 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt @@ -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>) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 7529b561e4..1b61d859a4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -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() { // 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 } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSheet.kt index 9d74a5baac..c34784908d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSheet.kt @@ -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) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt index a600f23c15..ccb5877e04 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt @@ -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) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt index 0cb74bb9a7..9f6ddb878b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt @@ -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()?.defaultDisplay + } + /** * Convenience method to acquire a partial wake lock. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/WindowExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/WindowExtensions.kt index f7c68eed7b..cd3c29c24e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/view/WindowExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/WindowExtensions.kt @@ -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) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt index af0cdcfe47..45cfca1f36 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt @@ -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. diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/BaseBottomSheetDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/BaseBottomSheetDialog.kt index 42d8cef4b4..c4b2132082 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/BaseBottomSheetDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/BaseBottomSheetDialog.kt @@ -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().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 } } } diff --git a/app/src/main/res/anim/bottom_sheet_slide_in.xml b/app/src/main/res/anim/bottom_sheet_slide_in.xml new file mode 100644 index 0000000000..74f052a3fb --- /dev/null +++ b/app/src/main/res/anim/bottom_sheet_slide_in.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/anim/bottom_sheet_slide_out.xml b/app/src/main/res/anim/bottom_sheet_slide_out.xml new file mode 100644 index 0000000000..553c827a50 --- /dev/null +++ b/app/src/main/res/anim/bottom_sheet_slide_out.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 10bb94ff22..0d983e6d82 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -100,8 +100,9 @@ + +