Skip to content

Commit bbb8afb

Browse files
committed
Add navigation bar component in demo app
1 parent 6395a49 commit bbb8afb

File tree

13 files changed

+331
-37
lines changed

13 files changed

+331
-37
lines changed

NOTICE.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ app/src/alpha/res/drawable-v24/ic_launcher_background.xml
1212
app/src/alpha/res/drawable/ic_launcher_foreground.xml
1313
app/src/beta/res/drawable-v24/ic_launcher_background.xml
1414
app/src/beta/res/drawable/ic_launcher_foreground.xml
15+
app/src/main/res/drawable/ic_avatar.xml
1516
app/src/main/res/drawable/ic_border.xml
1617
app/src/main/res/drawable/ic_chevron_down.xml
1718
app/src/main/res/drawable/ic_component_atom.xml
@@ -20,14 +21,19 @@ app/src/main/res/drawable/ic_design_token_figma.xml
2021
app/src/main/res/drawable/ic_dimension.xml
2122
app/src/main/res/drawable/ic_filter_effects.xml
2223
app/src/main/res/drawable/ic_heart.xml
24+
app/src/main/res/drawable/ic_home.xml
2325
app/src/main/res/drawable/ic_info.xml
2426
app/src/main/res/drawable/ic_layers.xml
2527
app/src/main/res/drawable/ic_menu_grid.xml
28+
app/src/main/res/drawable/ic_notification_alert.xml
2629
app/src/main/res/drawable/ic_palette.xml
30+
app/src/main/res/drawable/ic_settings.xml
31+
app/src/main/res/drawable/ic_shop_store.xml
2732
app/src/main/res/drawable/ic_solar_palette.xml
2833
app/src/main/res/drawable/ic_typography.xml
2934
app/src/main/res/drawable/ic_ui_dark_mode.xml
3035
app/src/main/res/drawable/ic_ui_light_mode.xml
36+
app/src/main/res/drawable/il_components_empty.xml
3137
app/src/main/res/drawable/il_components_divider.xml
3238
app/src/main/res/drawable/il_components_divider_dark.xml
3339
app/src/main/res/drawable/il_opacity_union.xml

app/src/main/java/com/orange/ouds/app/ui/BottomBar.kt

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,56 +31,45 @@ import androidx.compose.foundation.layout.navigationBars
3131
import androidx.compose.foundation.layout.only
3232
import androidx.compose.foundation.layout.windowInsetsBottomHeight
3333
import androidx.compose.foundation.layout.windowInsetsPadding
34-
import androidx.compose.material3.Icon
3534
import androidx.compose.material3.MaterialTheme
36-
import androidx.compose.material3.NavigationBar
37-
import androidx.compose.material3.NavigationBarItem
38-
import androidx.compose.material3.Text
3935
import androidx.compose.runtime.Composable
4036
import androidx.compose.runtime.getValue
4137
import androidx.compose.ui.Modifier
38+
import androidx.compose.ui.graphics.Color
4239
import androidx.compose.ui.res.painterResource
4340
import androidx.compose.ui.res.stringResource
44-
import androidx.compose.ui.text.style.TextOverflow
4541
import androidx.compose.ui.tooling.preview.PreviewLightDark
4642
import com.orange.ouds.app.R
43+
import com.orange.ouds.core.component.OudsNavigationBar
44+
import com.orange.ouds.core.component.OudsNavigationBarItem
4745
import com.orange.ouds.core.theme.OudsTheme
4846
import com.orange.ouds.core.utilities.OudsPreview
4947

5048
@Composable
5149
fun BottomBar(currentRoute: String, navigateToRoute: (String) -> Unit, visible: Boolean = true) {
5250
Column(modifier = Modifier.windowInsetsPadding(WindowInsets.displayCutout.only(WindowInsetsSides.Horizontal))) {
53-
val navigationBarBackgroundColor = OudsTheme.colorScheme.background.secondary //TODO Temporary color. Waiting for Material colors from Maxime.
51+
val systemNavigationBackgroundColor = OudsTheme.colorScheme.background.secondary //TODO Temporary color. Waiting for Material colors from Maxime.
5452
AnimatedVisibility(
5553
visible = visible,
5654
enter = fadeIn(tween(100)),
5755
exit = fadeOut(tween(100))
5856
) {
5957
val items = BottomBarItem.entries.toTypedArray()
60-
NavigationBar(
61-
modifier = Modifier.consumeWindowInsets(WindowInsets.navigationBars),
62-
containerColor = navigationBarBackgroundColor,
63-
content = {
64-
items.forEach { item ->
65-
NavigationBarItem(
66-
selected = currentRoute == item.route,
67-
icon = {
68-
Icon(painterResource(item.iconRes), null)
69-
},
70-
label = {
71-
Text(
72-
text = stringResource(item.titleRes),
73-
maxLines = 1,
74-
overflow = TextOverflow.Ellipsis,
75-
)
76-
},
77-
onClick = { navigateToRoute(item.route) }
78-
)
79-
}
58+
OudsNavigationBar(
59+
modifier = Modifier.consumeWindowInsets(WindowInsets.navigationBars)
60+
) {
61+
items.forEach { item ->
62+
OudsNavigationBarItem(
63+
modifier = Modifier.weight(1f),
64+
selected = currentRoute == item.route,
65+
icon = OudsNavigationBarItem.Icon(painter = painterResource(item.iconRes), ""),
66+
label = stringResource(item.titleRes),
67+
onClick = { navigateToRoute(item.route) }
68+
)
8069
}
81-
)
70+
}
8271
}
83-
val systemNavigationBarBackgroundColor by animateColorAsState(if (visible) navigationBarBackgroundColor else MaterialTheme.colorScheme.surface)
72+
val systemNavigationBarBackgroundColor by animateColorAsState(if (visible) systemNavigationBackgroundColor else MaterialTheme.colorScheme.surface)
8473
Spacer(
8574
modifier = Modifier
8675
.background(systemNavigationBarBackgroundColor)

app/src/main/java/com/orange/ouds/app/ui/components/Component.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.orange.ouds.app.ui.components.checkbox.CheckboxDemoScreen
2121
import com.orange.ouds.app.ui.components.checkbox.CheckboxItemDemoScreen
2222
import com.orange.ouds.app.ui.components.divider.DividerDemoScreen
2323
import com.orange.ouds.app.ui.components.link.LinkDemoScreen
24+
import com.orange.ouds.app.ui.components.navigationbar.NavigationBarDemoScreen
2425
import com.orange.ouds.app.ui.components.radiobutton.RadioButtonDemoScreen
2526
import com.orange.ouds.app.ui.components.radiobutton.RadioButtonItemDemoScreen
2627
import com.orange.ouds.app.ui.components.switch.SwitchDemoScreen
@@ -72,6 +73,13 @@ sealed class Component(
7273
demoScreen = { LinkDemoScreen() }
7374
)
7475

76+
data object NavigationBar : Component(
77+
R.string.app_components_navigationBar_label,
78+
LightDarkResourceId(R.drawable.il_components_empty, R.drawable.il_components_empty),
79+
R.string.app_components_navigationBar_description_text,
80+
demoScreen = { NavigationBarDemoScreen() }
81+
)
82+
7583
data object RadioButton : Component(
7684
R.string.app_components_radioButton_label,
7785
LightDarkResourceId(R.drawable.il_components_radiobutton, R.drawable.il_components_radiobutton_dark),
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Software Name: OUDS Android
3+
* SPDX-FileCopyrightText: Copyright (c) Orange SA
4+
* SPDX-License-Identifier: MIT
5+
*
6+
* This software is distributed under the MIT license,
7+
* the text of which is available at https://opensource.org/license/MIT/
8+
* or see the "LICENSE" file for more details.
9+
*
10+
* Software description: Android library of reusable graphical components
11+
*/
12+
13+
package com.orange.ouds.app.ui.components.navigationbar
14+
15+
import android.content.Context
16+
import androidx.annotation.DrawableRes
17+
import androidx.annotation.StringRes
18+
import androidx.compose.foundation.layout.PaddingValues
19+
import androidx.compose.foundation.layout.padding
20+
import androidx.compose.runtime.Composable
21+
import androidx.compose.runtime.remember
22+
import androidx.compose.ui.Modifier
23+
import androidx.compose.ui.platform.LocalContext
24+
import androidx.compose.ui.res.painterResource
25+
import androidx.compose.ui.res.stringResource
26+
import androidx.compose.ui.tooling.preview.PreviewLightDark
27+
import com.orange.ouds.app.R
28+
import com.orange.ouds.app.ui.components.Component
29+
import com.orange.ouds.app.ui.components.labelArgument
30+
import com.orange.ouds.app.ui.components.navigationbar.NavigationBarDemoState.Companion.MaxNavigationBarItemCount
31+
import com.orange.ouds.app.ui.components.navigationbar.NavigationBarDemoState.Companion.MinNavigationBarItemCount
32+
import com.orange.ouds.app.ui.components.painterArgument
33+
import com.orange.ouds.app.ui.utilities.Code
34+
import com.orange.ouds.app.ui.utilities.composable.CustomizationChoiceChips
35+
import com.orange.ouds.app.ui.utilities.composable.CustomizationSwitchItem
36+
import com.orange.ouds.app.ui.utilities.composable.DemoScreen
37+
import com.orange.ouds.core.component.OudsNavigationBar
38+
import com.orange.ouds.core.component.OudsNavigationBarItem
39+
import com.orange.ouds.core.theme.OudsTheme
40+
import com.orange.ouds.core.utilities.OudsPreview
41+
42+
@Composable
43+
fun NavigationBarDemoScreen() {
44+
val state = rememberNavigationBarDemoState()
45+
val context = LocalContext.current
46+
DemoScreen(
47+
description = stringResource(id = Component.NavigationBar.descriptionRes),
48+
bottomSheetContent = { NavigationBarDemoBottomSheetContent(state = state) },
49+
codeSnippet = { navigationBarDemoCodeSnippet(state = state, context = context) },
50+
demoContent = { NavigationBarDemoContent(state = state) },
51+
demoContentPaddingValues = PaddingValues(horizontal = OudsTheme.spaces.fixed.none)
52+
)
53+
}
54+
55+
@Composable
56+
private fun NavigationBarDemoBottomSheetContent(state: NavigationBarDemoState) {
57+
with(state) {
58+
val itemCountOptions = remember { (MinNavigationBarItemCount..MaxNavigationBarItemCount).toList() }
59+
CustomizationChoiceChips(
60+
modifier = Modifier.padding(top = OudsTheme.spaces.fixed.medium),
61+
label = stringResource(R.string.app_components_navigationBar_itemCount_label),
62+
chipsLabels = itemCountOptions.map { it.toString() },
63+
selectedChipIndex = itemCountOptions.indexOf(itemCount),
64+
onSelectionChange = { id -> itemCount = itemCountOptions[id] }
65+
)
66+
CustomizationSwitchItem(
67+
label = stringResource(R.string.app_components_navigationBar_alwaysShowLabel_label),
68+
checked = alwaysShowLabel,
69+
onCheckedChange = { alwaysShowLabel = it },
70+
)
71+
CustomizationSwitchItem(
72+
label = stringResource(R.string.app_components_navigationBar_lastItemEnabled_label),
73+
checked = lastItemEnabled,
74+
onCheckedChange = { checked ->
75+
// If the last item is selected when the last item is disabled, select the first item
76+
if (!checked && selectedItemId == itemCount - 1) {
77+
selectedItemId = 0
78+
}
79+
lastItemEnabled = checked
80+
},
81+
)
82+
}
83+
}
84+
85+
@Composable
86+
private fun NavigationBarDemoContent(state: NavigationBarDemoState) {
87+
val navigationBarItems = NavigationBarItem.entries
88+
89+
with(state) {
90+
OudsNavigationBar {
91+
navigationBarItems.take(itemCount).forEachIndexed { index, item ->
92+
val label = stringResource(id = item.labelRes)
93+
val isLastItem = index == itemCount - 1
94+
OudsNavigationBarItem(
95+
modifier = Modifier.weight(1f),
96+
selected = selectedItemId == index,
97+
onClick = { selectedItemId = index },
98+
label = label,
99+
icon = OudsNavigationBarItem.Icon(
100+
painter = painterResource(id = item.iconRes),
101+
contentDescription = label
102+
),
103+
alwaysShowLabel = alwaysShowLabel,
104+
enabled = !(isLastItem && !lastItemEnabled)
105+
)
106+
}
107+
}
108+
}
109+
}
110+
111+
private fun Code.Builder.navigationBarDemoCodeSnippet(state: NavigationBarDemoState, context: Context) {
112+
functionCall(OudsNavigationBarItem::class.simpleName.orEmpty()) {
113+
functionCallArgument("items", "listOf") {
114+
NavigationBarItem.entries.take(state.itemCount).forEach { item ->
115+
val label = context.resources.getString(item.labelRes)
116+
functionCallArgument(null, OudsNavigationBarItem::class.simpleName.orEmpty()) {
117+
typedArgument("selected", item == NavigationBarItem.Home)
118+
lambdaArgument("onClick", {})
119+
labelArgument(label)
120+
functionCallArgument(null, OudsNavigationBarItem.Icon::class.simpleName.orEmpty()) {
121+
painterArgument(id = item.iconRes)
122+
}
123+
}
124+
}
125+
}
126+
}
127+
}
128+
129+
enum class NavigationBarItem(@DrawableRes val iconRes: Int, @StringRes val labelRes: Int) {
130+
Home(R.drawable.ic_home, R.string.app_components_navigationBar_homeItem_label),
131+
Shop(R.drawable.ic_shop_store, R.string.app_components_navigationBar_shopItem_label),
132+
Notification(R.drawable.ic_notification_alert, R.string.app_components_navigationBar_notificationItem_label),
133+
Account(R.drawable.ic_avatar, R.string.app_components_navigationBar_accountItem_label),
134+
Settings(R.drawable.ic_settings, R.string.app_components_navigationBar_settingsItem_label),
135+
}
136+
137+
@PreviewLightDark
138+
@Composable
139+
private fun PreviewNavigationBarDemoScreen() = OudsPreview {
140+
NavigationBarDemoScreen()
141+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Software Name: OUDS Android
3+
* SPDX-FileCopyrightText: Copyright (c) Orange SA
4+
* SPDX-License-Identifier: MIT
5+
*
6+
* This software is distributed under the MIT license,
7+
* the text of which is available at https://opensource.org/license/MIT/
8+
* or see the "LICENSE" file for more details.
9+
*
10+
* Software description: Android library of reusable graphical components
11+
*/
12+
13+
package com.orange.ouds.app.ui.components.navigationbar
14+
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.runtime.getValue
17+
import androidx.compose.runtime.mutableIntStateOf
18+
import androidx.compose.runtime.mutableStateOf
19+
import androidx.compose.runtime.saveable.mapSaver
20+
import androidx.compose.runtime.saveable.rememberSaveable
21+
import androidx.compose.runtime.setValue
22+
23+
@Composable
24+
fun rememberNavigationBarDemoState(
25+
itemCount: Int = NavigationBarDemoState.MinNavigationBarItemCount,
26+
selectedItemId: Int = 0,
27+
alwaysShowLabel: Boolean = true,
28+
lastItemEnabled: Boolean = true
29+
) = rememberSaveable(itemCount, alwaysShowLabel, lastItemEnabled, saver = NavigationBarDemoState.Saver) {
30+
NavigationBarDemoState(itemCount, selectedItemId, alwaysShowLabel, lastItemEnabled)
31+
}
32+
33+
class NavigationBarDemoState(
34+
itemCount: Int,
35+
selectedItemId: Int,
36+
alwaysShowLabel: Boolean,
37+
lastItemEnabled: Boolean
38+
) {
39+
companion object {
40+
const val MinNavigationBarItemCount = 3
41+
const val MaxNavigationBarItemCount = 5
42+
43+
val Saver = run {
44+
val itemCountKey = "itemCount"
45+
val selectedItemIdKey = "selectedItemId"
46+
val alwaysShowLabelKey = "alwaysShowLabel"
47+
val lastItemEnabledKey = "lastItemEnabled"
48+
mapSaver(
49+
save = { state ->
50+
mapOf(
51+
itemCountKey to state.itemCount,
52+
selectedItemIdKey to state.selectedItemId,
53+
alwaysShowLabelKey to state.alwaysShowLabel,
54+
lastItemEnabledKey to state.lastItemEnabled
55+
)
56+
},
57+
restore = { map ->
58+
NavigationBarDemoState(
59+
map[itemCountKey] as Int,
60+
map[selectedItemIdKey] as Int,
61+
map[alwaysShowLabelKey] as Boolean,
62+
map[lastItemEnabledKey] as Boolean
63+
)
64+
}
65+
)
66+
}
67+
}
68+
69+
var itemCount: Int by mutableIntStateOf(itemCount)
70+
var selectedItemId: Int by mutableIntStateOf(selectedItemId)
71+
var alwaysShowLabel: Boolean by mutableStateOf(alwaysShowLabel)
72+
var lastItemEnabled: Boolean by mutableStateOf(lastItemEnabled)
73+
74+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:pathData="M15.761,10.138a5.519,5.519 0,0 1,-7.569 0A5.399,5.399 135,0 0,5.378 14.76L5.376,14.76L5.376,19.8A2.4,2.4 0,0 0,7.776 22.2L18.576,22.2L18.576,14.88A5.399,5.399 45,0 0,15.761 10.138ZM16.296,6.12A4.32,4.32 0,1 1,11.976 1.8,4.32 4.32,0 0,1 16.296,6.12Z"
8+
android:fillColor="#000000"/>
9+
</vector>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:pathData="M13.283,3.538l0,0a1.799,1.799 0,0 0,-2.57 0.003l0,-0.001L2.4,12L6,12L6,19.2a1.8,1.8 0,0 0,1.8 1.8L16.2,21a1.8,1.8 0,0 0,1.8 -1.8L18,12L21.6,12ZM13.92,12.9L13.92,18.12L8.7,18.12A0.9,0.9 0,0 1,7.8 17.22L7.8,12L13.02,12A0.9,0.9 0,0 1,13.92 12.9l0,0Z"
8+
android:fillColor="#000000"/>
9+
</vector>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:pathData="M12,22.2A2.4,2.4 0,0 0,14.401 19.8L9.599,19.8A2.4,2.4 0,0 0,12 22.2ZM20.458,18.291L19.2,16.2L19.2,10.248c0,-3.348 -2.3,-6.203 -5.399,-7.016L13.801,3a1.801,1.801 45,0 0,-3.601 0l0,0.227C7.093,4.027 4.8,6.845 4.8,10.2L4.8,16.2l-1.258,2.091A0.6,0.6 0,0 0,4.057 19.2L19.943,19.2A0.6,0.6 0,0 0,20.458 18.291Z"
8+
android:fillColor="#000000"/>
9+
</vector>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:pathData="M19.862,12.012c0,-2.655 2.897,-0.646 1.548,-3.902 -1.35,-3.256 -1.977,0.212 -3.855,-1.665s1.592,-2.505 -1.665,-3.853c-3.257,-1.349 -1.249,1.548 -3.903,1.548 -2.655,0 -0.646,-2.896 -3.903,-1.548s0.212,1.976 -1.665,3.853S3.915,4.853 2.566,8.11C1.217,11.366 4.114,9.357 4.114,12.012S1.217,12.658 2.566,15.914c1.349,3.256 1.977,-0.212 3.854,1.664 1.877,1.877 -1.591,2.506 1.665,3.854s1.248,-1.548 3.903,-1.548c2.654,0 0.645,2.896 3.903,1.548 3.257,-1.349 -0.212,-1.977 1.665,-3.854 1.877,-1.876 2.505,1.592 3.855,-1.664C22.759,12.658 19.862,14.667 19.862,12.012ZM12,7.224c2.651,0 4.8,2.149 4.8,4.8S14.651,16.824 12,16.824 7.2,14.675 7.2,12.024 9.349,7.224 12,7.224Z"
8+
android:fillColor="#000000"/>
9+
</vector>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:pathData="M3,4.8L21,4.8L21,3a0.6,0.6 0,0 0,-0.6 -0.6L3.6,2.4a0.6,0.6 0,0 0,-0.6 0.6l0,1.8ZM20.4,12.6a1.793,1.793 135,0 1,-1.2 -0.459,1.797 1.797,0 0,1 -2.4,0 1.797,1.797 0,0 1,-2.4 0,1.797 1.797,0 0,1 -2.4,0 1.797,1.797 0,0 1,-2.4 0,1.797 1.797,0 0,1 -2.4,0 1.797,1.797 0,0 1,-2.4 0A1.796,1.796 45,0 1,3 12.497L3,21.6L15.6,21.6L15.6,14.4L19.2,14.4L19.2,21.6l1.8,0L21,12.497A1.792,1.792 0,0 1,20.4 12.6ZM13.8,19.2L4.8,19.2L4.8,14.4L13.8,14.4L13.8,19.2ZM21,5.4L3,5.4L2.4,10.8a1.2,1.2 0,0 0,2.4 0,1.2 1.2,0 0,0 2.4,0 1.2,1.2 0,0 0,2.4 0,1.2 1.2,0 0,0 2.4,0 1.2,1.2 0,0 0,2.4 0,1.2 1.2,0 0,0 2.4,0 1.2,1.2 0,0 0,2.4 0,1.2 1.2,0 0,0 2.4,0Z"
8+
android:fillColor="#000000"/>
9+
</vector>

0 commit comments

Comments
 (0)