Skip to content

Commit 05f84af

Browse files
committed
Add NavigationBarProperties to customize navigationBar
1 parent 896e968 commit 05f84af

File tree

1 file changed

+102
-11
lines changed
  • bottomsheetdialog-compose/src/main/kotlin/com/holix/android/bottomsheetdialog/compose

1 file changed

+102
-11
lines changed

bottomsheetdialog-compose/src/main/kotlin/com/holix/android/bottomsheetdialog/compose/BottomSheetDialog.kt

Lines changed: 102 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package com.holix.android.bottomsheetdialog.compose
22

3-
import android.app.Dialog
43
import android.content.Context
54
import android.graphics.Outline
5+
import android.os.Build
66
import android.view.*
77
import androidx.compose.runtime.*
88
import androidx.compose.runtime.saveable.rememberSaveable
99
import androidx.compose.ui.Modifier
1010
import androidx.compose.ui.graphics.Color
11+
import androidx.compose.ui.graphics.compositeOver
12+
import androidx.compose.ui.graphics.luminance
1113
import androidx.compose.ui.graphics.toArgb
1214
import androidx.compose.ui.layout.Layout
1315
import androidx.compose.ui.platform.*
@@ -16,14 +18,12 @@ import androidx.compose.ui.semantics.semantics
1618
import androidx.compose.ui.unit.Density
1719
import androidx.compose.ui.unit.LayoutDirection
1820
import androidx.compose.ui.unit.dp
19-
import androidx.compose.ui.window.Dialog
20-
import androidx.compose.ui.window.DialogProperties
2121
import androidx.compose.ui.window.SecureFlagPolicy
22+
import androidx.core.view.WindowCompat
2223
import androidx.lifecycle.ViewTreeLifecycleOwner
2324
import androidx.lifecycle.ViewTreeViewModelStoreOwner
2425
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
2526
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
26-
import com.google.android.material.bottomsheet.BottomSheetBehavior
2727
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
2828
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
2929
import com.google.android.material.bottomsheet.BottomSheetDialog
@@ -48,9 +48,23 @@ class BottomSheetDialogProperties constructor(
4848
val dismissOnClickOutside: Boolean = true,
4949
val dismissWithAnimation: Boolean = false,
5050
val securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
51-
val navigationBarColor: Color = Color.Unspecified
51+
val navigationBarProperties: NavigationBarProperties = NavigationBarProperties()
5252
) {
5353

54+
constructor(
55+
dismissOnBackPress: Boolean = true,
56+
dismissOnClickOutside: Boolean = true,
57+
dismissWithAnimation: Boolean = false,
58+
securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
59+
navigationBarColor: Color
60+
) : this(
61+
dismissOnBackPress,
62+
dismissOnClickOutside,
63+
dismissWithAnimation,
64+
securePolicy,
65+
NavigationBarProperties(color = navigationBarColor)
66+
)
67+
5468
override fun equals(other: Any?): Boolean {
5569
if (this === other) return true
5670
if (other !is BottomSheetDialogProperties) return false
@@ -59,7 +73,7 @@ class BottomSheetDialogProperties constructor(
5973
if (dismissOnClickOutside != other.dismissOnClickOutside) return false
6074
if (dismissWithAnimation != other.dismissWithAnimation) return false
6175
if (securePolicy != other.securePolicy) return false
62-
if (navigationBarColor != other.navigationBarColor) return false
76+
if (navigationBarProperties != other.navigationBarProperties) return false
6377

6478
return true
6579
}
@@ -69,11 +83,60 @@ class BottomSheetDialogProperties constructor(
6983
result = 31 * result + dismissOnClickOutside.hashCode()
7084
result = 31 * result + dismissWithAnimation.hashCode()
7185
result = 31 * result + securePolicy.hashCode()
72-
result = 31 * result + navigationBarColor.hashCode()
86+
result = 31 * result + navigationBarProperties.hashCode()
7387
return result
7488
}
7589
}
7690

91+
/**
92+
* Properties used to customize navigationBar.
93+
94+
* @param color The **desired** [Color] to set. This may require modification if running on an
95+
* API level that only supports white navigation bar icons. Additionally this will be ignored
96+
* and [Color.Transparent] will be used on API 29+ where gesture navigation is preferred or the
97+
* system UI automatically applies background protection in other navigation modes.
98+
* @param darkIcons Whether dark navigation bar icons would be preferable.
99+
* @param navigationBarContrastEnforced Whether the system should ensure that the navigation
100+
* bar has enough contrast when a fully transparent background is requested. Only supported on
101+
* API 29+.
102+
* @param transformColorForLightContent A lambda which will be invoked to transform [color] if
103+
* dark icons were requested but are not available. Defaults to applying a black scrim.
104+
*
105+
* Inspired by [Accompanist SystemUiController](https://github.com/google/accompanist/blob/main/systemuicontroller/src/main/java/com/google/accompanist/systemuicontroller/SystemUiController.kt)
106+
*/
107+
108+
@Immutable
109+
class NavigationBarProperties(
110+
val color: Color = Color.Unspecified,
111+
val darkIcons: Boolean = color.luminance() > 0.5f,
112+
val navigationBarContrastEnforced: Boolean = true,
113+
val transformColorForLightContent: (Color) -> Color = BlackScrimmed
114+
) {
115+
override fun equals(other: Any?): Boolean {
116+
if (this === other) return true
117+
if (other !is NavigationBarProperties) return false
118+
119+
if (color != other.color) return false
120+
if (darkIcons != other.darkIcons) return false
121+
if (navigationBarContrastEnforced != other.navigationBarContrastEnforced) return false
122+
if (transformColorForLightContent != other.transformColorForLightContent) return false
123+
124+
return true
125+
}
126+
127+
override fun hashCode(): Int {
128+
var result = color.hashCode()
129+
result = 31 * result + darkIcons.hashCode()
130+
result = 31 * result + navigationBarContrastEnforced.hashCode()
131+
return result
132+
}
133+
}
134+
135+
private val BlackScrim = Color(0f, 0f, 0f, 0.3f) // 30% opaque black
136+
private val BlackScrimmed: (Color) -> Color = { original ->
137+
BlackScrim.compositeOver(original)
138+
}
139+
77140
/**
78141
* Opens a bottomsheet dialog with the given content.
79142
*
@@ -204,6 +267,23 @@ private class BottomSheetDialogWrapper(
204267

205268
override val subCompositionView: AbstractComposeView get() = bottomSheetDialogLayout
206269

270+
// to control insets
271+
private val windowInsetsController = window?.let {
272+
WindowCompat.getInsetsController(it, it.decorView)
273+
}
274+
private var navigationBarDarkContentEnabled: Boolean
275+
get() = windowInsetsController?.isAppearanceLightNavigationBars == true
276+
set(value) {
277+
windowInsetsController?.isAppearanceLightNavigationBars = value
278+
}
279+
280+
private var isNavigationBarContrastEnforced: Boolean
281+
get() = Build.VERSION.SDK_INT >= 29 && window?.isNavigationBarContrastEnforced == true
282+
set(value) {
283+
if (Build.VERSION.SDK_INT >= 29) {
284+
window?.isNavigationBarContrastEnforced = value
285+
}
286+
}
207287

208288
init {
209289
val window = window ?: error("Dialog has no window")
@@ -283,9 +363,20 @@ private class BottomSheetDialogWrapper(
283363
)
284364
}
285365

286-
private fun setNavigationBarColor(color: Color) {
287-
if (color != Color.Unspecified) {
288-
window!!.navigationBarColor = color.toArgb()
366+
private fun setNavigationBarProperties(properties: NavigationBarProperties) {
367+
with(properties) {
368+
navigationBarDarkContentEnabled = darkIcons
369+
isNavigationBarContrastEnforced = navigationBarContrastEnforced
370+
371+
window?.navigationBarColor = when {
372+
darkIcons && windowInsetsController?.isAppearanceLightNavigationBars != true -> {
373+
// If we're set to use dark icons, but our windowInsetsController call didn't
374+
// succeed (usually due to API level), we instead transform the color to maintain
375+
// contrast
376+
transformColorForLightContent(color)
377+
}
378+
else -> color
379+
}.toArgb()
289380
}
290381
}
291382

@@ -308,7 +399,7 @@ private class BottomSheetDialogWrapper(
308399
setSecurePolicy(properties.securePolicy)
309400
setLayoutDirection(layoutDirection)
310401
setCanceledOnTouchOutside(properties.dismissOnClickOutside)
311-
setNavigationBarColor(properties.navigationBarColor)
402+
setNavigationBarProperties(properties.navigationBarProperties)
312403
dismissWithAnimation = properties.dismissWithAnimation
313404
}
314405

0 commit comments

Comments
 (0)