diff --git a/FluentUI.Demo/src/main/AndroidManifest.xml b/FluentUI.Demo/src/main/AndroidManifest.xml
index 3742eec6..1330ec8d 100644
--- a/FluentUI.Demo/src/main/AndroidManifest.xml
+++ b/FluentUI.Demo/src/main/AndroidManifest.xml
@@ -27,49 +27,51 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/Demos.kt b/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/Demos.kt
index 40bb4d7e..c1c4ffef 100644
--- a/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/Demos.kt
+++ b/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/Demos.kt
@@ -9,42 +9,44 @@ import com.microsoft.fluentuidemo.demos.*
import java.util.*
import kotlin.reflect.KClass
+const val V2AVATAR = "V2 Avatar"
+const val V2AVATAR_CAROUSEL = "V2 Avatar Carousel"
+const val V2AVATAR_GROUP = "V2 Avatar Group"
+const val V2Badge = "V2 Badge"
+const val V2BASIC_CONTROLS = "V2 Basic Controls"
+const val V2BASIC_INPUTS = "V2 Basic Inputs"
+const val V2BOTTOM_SHEET = "V2 BottomSheet"
+const val V2CONTEXTUAL_COMMAND_BAR = "V2 ContextualCommandBar"
+const val V2DRAWER = "V2 Drawer"
+const val V2LIST_ITEM = "V2 ListItem"
+const val V2PERSONA = "V2 Persona"
+const val V2PERSONA_CHIP = "V2 PersonaChip"
+const val V2PERSONA_LIST = "V2 PersonaList"
+const val V2PROGRESS = "V2 Progress"
+const val V2SEARCHBAR = "V2 SearchBar"
+const val V2SEGMENTED_CONTROL = "V2 SegmentedControl"
+const val V2TABBAR = "V2 TabBar"
const val ACTION_BAR_LAYOUT = "ActionBarLayout"
const val APP_BAR_LAYOUT = "AppBarLayout"
const val V2APP_BAR_LAYOUT = "V2 AppBarLayout"
const val AVATAR_VIEW = "AvatarView"
const val AVATAR_GROUP_VIEW = "AvatarGroupView"
const val BASIC_INPUTS = "Basic Inputs"
-const val V2AVATAR = "V2 Avatar"
-const val V2AVATAR_CAROUSEL = "V2 Avatar Carousel"
-const val V2AVATAR_GROUP = "V2 Avatar Group"
-const val V2BASIC_INPUTS = "V2 Basic Inputs"
-const val V2BASIC_CONTROLS = "V2 Basic Controls"
const val BOTTOM_NAVIGATION = "BottomNavigation"
const val BOTTOM_SHEET = "BottomSheet"
const val CALENDAR_VIEW = "CalendarView"
const val CONTEXTUAL_COMMAND_BAR = "ContextualCommandBar"
-const val V2CONTEXTUAL_COMMAND_BAR = "V2 ContextualCommandBar"
const val DATE_TIME_PICKER = "DateTimePicker"
const val DRAWER = "Drawer"
-const val V2DRAWER = "V2 Drawer"
const val LIST_ITEM_VIEW = "ListItemView"
-const val V2LIST_ITEM = "V2 ListItem"
const val PEOPLE_PICKER_VIEW = "PeoplePickerView"
const val PERSISTENT_BOTTOM_SHEET = "PersistentBottomSheet"
-const val V2BOTTOM_SHEET = "V2 BottomSheet"
const val PERSONA_CHIP_VIEW = "PersonaChipView"
-const val V2PERSONA_CHIP = "V2 PersonaChip"
const val PERSONA_LIST_VIEW = "PersonaListView"
-const val V2PERSONA_LIST = "V2 PersonaList"
const val PERSONA_VIEW = "PersonaView"
-const val V2PERSONA = "V2 Persona"
const val POPUP_MENU = "PopupMenu"
const val PROGRESS = "Progress"
-const val V2PROGRESS = "V2 Progress"
const val SNACKBAR = "Snackbar"
-const val V2SEGMENTED_CONTROL = "V2 SegmentedControl"
-const val V2SEARCHBAR = "V2 SearchBar"
const val TAB_LAYOUT = "TabLayout"
const val TEMPLATE_VIEW = "TemplateView"
const val TOOLTIP = "Tooltip"
@@ -55,6 +57,7 @@ val DEMOS = arrayListOf(
Demo(V2AVATAR, V2AvatarActivity::class),
Demo(V2AVATAR_CAROUSEL, V2AvatarCarouselActivity::class),
Demo(V2AVATAR_GROUP, V2AvatarGroupActivity::class),
+ Demo(V2Badge, V2BadgeActivity::class),
Demo(V2BASIC_INPUTS, V2BasicInputsActivity::class),
Demo(V2BASIC_CONTROLS, V2BasicControlsActivity::class),
Demo(V2BOTTOM_SHEET, V2BottomSheetActivity::class),
@@ -67,6 +70,7 @@ val DEMOS = arrayListOf(
Demo(V2PROGRESS, V2ProgressActivity::class),
Demo(V2SEARCHBAR, V2SearchBarActivity::class),
Demo(V2SEGMENTED_CONTROL, V2SegmentedControlActivity::class),
+ Demo(V2TABBAR, V2TabBarActivity::class),
Demo(ACTION_BAR_LAYOUT, ActionBarLayoutActivity::class),
Demo(APP_BAR_LAYOUT, AppBarLayoutActivity::class),
Demo(AVATAR_VIEW, AvatarViewActivity::class),
diff --git a/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2BadgeActivity.kt b/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2BadgeActivity.kt
new file mode 100644
index 00000000..86693451
--- /dev/null
+++ b/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2BadgeActivity.kt
@@ -0,0 +1,111 @@
+package com.microsoft.fluentuidemo.demos
+
+import android.os.Bundle
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.Text
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.unit.dp
+import com.microsoft.fluentui.theme.FluentTheme
+import com.microsoft.fluentui.theme.ThemeMode
+import com.microsoft.fluentui.theme.token.AliasTokens
+import com.microsoft.fluentui.theme.token.controlTokens.BadgeType
+import com.microsoft.fluentui.tokenized.notification.Badge
+import com.microsoft.fluentuidemo.DemoActivity
+import com.microsoft.fluentuidemo.R
+
+
+class V2BadgeActivity : DemoActivity() {
+ override val contentLayoutId: Int
+ get() = R.layout.v2_activity_compose
+ override val contentNeedsScrollableContainer: Boolean
+ get() = false
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val composeHere = findViewById(R.id.compose_here)
+
+ composeHere.setContent {
+ FluentTheme {
+ val title1Font =
+ FluentTheme.aliasTokens.typography[AliasTokens.TypographyTokens.Title1]
+ val title2Font =
+ FluentTheme.aliasTokens.typography[AliasTokens.TypographyTokens.Title2]
+
+ Column(Modifier.background(Color.Gray)) {
+ Text(
+ text = resources.getString(R.string.badge_notification_badge),
+ fontWeight = title1Font.weight,
+ fontSize = title1Font.fontSize.size,
+ lineHeight = title1Font.fontSize.lineHeight,
+ color = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(),
+ modifier = Modifier.padding(8.dp)
+
+ )
+ Row(Modifier.padding(16.dp)) {
+ Text(
+ text = resources.getString(R.string.badge_notification_dot),
+ fontWeight = title2Font.weight,
+ fontSize = title2Font.fontSize.size,
+ lineHeight = title2Font.fontSize.lineHeight,
+ color = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value()
+ )
+ Badge()
+ }
+
+ Row(Modifier.padding(16.dp)) {
+ Text(
+ text = resources.getString(R.string.badge_notification_character),
+ fontWeight = title2Font.weight,
+ fontSize = title2Font.fontSize.size,
+ lineHeight = title2Font.fontSize.lineHeight,
+ color = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value()
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("1", badgeType = BadgeType.Character)
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("2", badgeType = BadgeType.Character)
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("8", badgeType = BadgeType.Character)
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("12", badgeType = BadgeType.Character)
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("123", badgeType = BadgeType.Character)
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("12345678910", badgeType = BadgeType.Character)
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("Badge", badgeType = BadgeType.Character)
+ }
+ Row(Modifier.padding(16.dp)) {
+ Text(
+ text = "List",
+ fontWeight = title2Font.weight,
+ fontSize = title2Font.fontSize.size,
+ lineHeight = title2Font.fontSize.lineHeight,
+ color = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(
+ themeMode = ThemeMode.Auto
+ )
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("1", badgeType = BadgeType.List)
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("2", badgeType = BadgeType.List)
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("8", badgeType = BadgeType.List)
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("12", badgeType = BadgeType.List)
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("123", badgeType = BadgeType.List)
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("12345678910", badgeType = BadgeType.List)
+ Spacer(modifier = Modifier.width(8.dp))
+ Badge("Badge", badgeType = BadgeType.List)
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2TabBarActivity.kt b/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2TabBarActivity.kt
new file mode 100644
index 00000000..7ddc05bf
--- /dev/null
+++ b/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2TabBarActivity.kt
@@ -0,0 +1,215 @@
+package com.microsoft.fluentuidemo.demos
+
+import android.content.Context
+import android.os.Bundle
+import android.widget.Toast
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.*
+import androidx.compose.material.icons.outlined.*
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.unit.dp
+import com.microsoft.fluentui.theme.AppThemeController
+import com.microsoft.fluentui.theme.FluentTheme
+import com.microsoft.fluentui.theme.ThemeMode
+import com.microsoft.fluentui.theme.token.AliasTokens
+import com.microsoft.fluentui.theme.token.controlTokens.BadgeType
+import com.microsoft.fluentui.theme.token.controlTokens.ButtonSize
+import com.microsoft.fluentui.theme.token.controlTokens.ButtonStyle
+import com.microsoft.fluentui.theme.token.controlTokens.TabTextAlignment
+import com.microsoft.fluentui.tokenized.controls.Button
+import com.microsoft.fluentui.tokenized.controls.RadioButton
+import com.microsoft.fluentui.tokenized.listitem.ListItem
+import com.microsoft.fluentui.tokenized.navigation.TabBar
+import com.microsoft.fluentui.tokenized.navigation.TabData
+import com.microsoft.fluentui.tokenized.notification.Badge
+import com.microsoft.fluentuidemo.DemoActivity
+import com.microsoft.fluentuidemo.R
+
+class V2TabBarActivity : DemoActivity() {
+ override val contentLayoutId: Int
+ get() = R.layout.v2_activity_compose
+ override val contentNeedsScrollableContainer: Boolean
+ get() = false
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val composeHere = findViewById(R.id.compose_here)
+ val context = this
+
+ composeHere.setContent {
+ var selectedIndex by rememberSaveable { mutableStateOf(0) }
+ val tabDataList = arrayListOf(
+ TabData(
+ title = resources.getString(R.string.tabBar_home),
+ icon = Icons.Outlined.Home,
+ selectedIcon = Icons.Filled.Home,
+ onClick = {
+ invokeToast(resources.getString(R.string.tabBar_home), context)
+ Toast.makeText(context, "Home Tab Clicked", Toast.LENGTH_SHORT).show()
+ selectedIndex = 0
+ },
+ badge = { Badge() }
+ ),
+ TabData(
+ title = resources.getString(R.string.tabBar_mail),
+ icon = Icons.Outlined.Email,
+ selectedIcon = Icons.Filled.Email,
+ onClick = {
+ invokeToast(resources.getString(R.string.tabBar_mail), context)
+ selectedIndex = 1
+ },
+ badge = { Badge("123+", badgeType = BadgeType.Character) }
+ ),
+ TabData(
+ title = resources.getString(R.string.tabBar_settings),
+ icon = Icons.Outlined.Settings,
+ selectedIcon = Icons.Filled.Settings,
+ onClick = {
+ invokeToast(resources.getString(R.string.tabBar_settings), context)
+ selectedIndex = 2
+ }
+ ),
+ TabData(
+ title = resources.getString(R.string.tabBar_notification),
+ icon = Icons.Outlined.Notifications,
+ selectedIcon = Icons.Filled.Notifications,
+ onClick = {
+ invokeToast(resources.getString(R.string.tabBar_notification), context)
+ selectedIndex = 3
+ },
+ badge = { Badge("10", badgeType = BadgeType.Character) }
+ ),
+ TabData(
+ title = resources.getString(R.string.tabBar_more),
+ icon = Icons.Outlined.List,
+ selectedIcon = Icons.Filled.List,
+ onClick = {
+ invokeToast(resources.getString(R.string.tabBar_more), context)
+ selectedIndex = 4
+ },
+ badge = { Badge() }
+ )
+ )
+ FluentTheme {
+ val content = listOf(0, 1, 2)
+ var selectedOption by rememberSaveable { mutableStateOf(content[0]) }
+ var tabTextAlignment by rememberSaveable { mutableStateOf(TabTextAlignment.VERTICAL) }
+ var tabItemsCount by rememberSaveable { mutableStateOf(5) }
+
+ Column {
+ ListItem.Header(title = resources.getString(R.string.tabBar_text_alignment))
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(10.dp),
+ ) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(
+ text = resources.getString(R.string.tabBar_vertical),
+ modifier = Modifier.weight(1F),
+ color = AppThemeController.aliasTokens.value!!.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(
+ themeMode = ThemeMode.Auto
+ )
+ )
+ RadioButton(
+ selected = (selectedOption == content[0]),
+ onClick = {
+ selectedOption = content[0]
+ tabTextAlignment = TabTextAlignment.VERTICAL
+ }
+ )
+ }
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(
+ text = resources.getString(R.string.tabBar_horizontal),
+ modifier = Modifier.weight(1F),
+ color = AppThemeController.aliasTokens.value!!.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(
+ themeMode = ThemeMode.Auto
+ )
+ )
+ RadioButton(
+ selected = (selectedOption == content[1]),
+ onClick = {
+ selectedOption = content[1]
+ tabTextAlignment = TabTextAlignment.HORIZONTAL
+ }
+ )
+ }
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(
+ text = resources.getString(R.string.tabBar_no_text),
+ modifier = Modifier.weight(1F),
+ color = AppThemeController.aliasTokens.value!!.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(
+ themeMode = ThemeMode.Auto
+ )
+ )
+ RadioButton(
+ selected = (selectedOption == content[2]),
+ onClick = {
+ selectedOption = content[2]
+ tabTextAlignment = TabTextAlignment.NO_TEXT
+ }
+ )
+ }
+
+ }
+ ListItem.Header(title = resources.getString(R.string.tabBar_tab_items),
+ trailingAccessoryView =
+ {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Button(
+ style = ButtonStyle.Button,
+ size = ButtonSize.Medium,
+ text = "+",
+ enabled = tabItemsCount < 5,
+ onClick = { tabItemsCount++ })
+
+ Button(
+ style = ButtonStyle.Button,
+ size = ButtonSize.Medium,
+ text = "-",
+ enabled = tabItemsCount > 1,
+ onClick = { tabItemsCount-- })
+
+ }
+ }
+ )
+ }
+ Column(verticalArrangement = Arrangement.Bottom) {
+ TabBar(
+ tabDataList = tabDataList.take(tabItemsCount),
+ selectedIndex = selectedIndex,
+ tabTextAlignment = tabTextAlignment
+ )
+ }
+ }
+ }
+ }
+
+ private fun invokeToast(string: String, context: Context) {
+ Toast.makeText(context, "$string Tab Clicked", Toast.LENGTH_SHORT).show()
+ }
+}
\ No newline at end of file
diff --git a/FluentUI.Demo/src/main/res/values/strings.xml b/FluentUI.Demo/src/main/res/values/strings.xml
index 0c5aa880..b0fb94e0 100644
--- a/FluentUI.Demo/src/main/res/values/strings.xml
+++ b/FluentUI.Demo/src/main/res/values/strings.xml
@@ -101,6 +101,13 @@
AvatarView at index %d clicked
+
+
+ Notification Badge
+ Dot
+ List
+ Character
+
Photos
@@ -643,6 +650,19 @@
Danger style
+
+
+ Home
+ Mail
+ Settings
+ Notification
+ More
+ Text Alignment
+ Vertical
+ Horizontal
+ No Text
+ Tab Items
+
Title
diff --git a/FluentUI/build.gradle b/FluentUI/build.gradle
index c2e65d77..ca243c5c 100644
--- a/FluentUI/build.gradle
+++ b/FluentUI/build.gradle
@@ -37,6 +37,7 @@ dependencies {
api project(':fluentui_icons')
api project(':fluentui_listitem')
api project(':fluentui_menus')
+ api project(':fluentui_notification')
api project(':fluentui_others')
api project(':fluentui_peoplepicker')
api project(':fluentui_persona')
diff --git a/config.gradle b/config.gradle
index b12903c0..4efd7fe3 100644
--- a/config.gradle
+++ b/config.gradle
@@ -26,6 +26,7 @@ project.ext.fluentui_peoplepicker_versionid = '0.0.25'
project.ext.fluentui_persona_versionid = '0.1.4'
project.ext.fluentui_progress_versionid = '0.1.1'
project.ext.fluentui_icons_versionid = '0.1.2'
+project.ext.fluentui_notification_versionid = '0.1.0'
project.ext.FluentUI_versionid = '0.1.15'
project.ext.fluentui_calendar_version_code = 25
project.ext.fluentui_controls_version_code = 8
@@ -42,6 +43,7 @@ project.ext.fluentui_peoplepicker_version_code = 25
project.ext.fluentui_persona_version_code = 30
project.ext.fluentui_progress_version_code = 26
project.ext.fluentui_icons_version_code = 3
+project.ext.fluentui_notification_version_code = 1
project.ext.FluentUI_version_code = 50
project.ext.license_type = 'MIT License'
project.ext.license_url = 'https://github.com/microsoft/fluentui-android/blob/master/LICENSE'
diff --git a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/ControlTokens.kt b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/ControlTokens.kt
index db8edde6..df466517 100644
--- a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/ControlTokens.kt
+++ b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/ControlTokens.kt
@@ -19,6 +19,7 @@ class ControlTokens {
Avatar,
AvatarCarousel,
AvatarGroup,
+ Badge,
BottomSheet,
Button,
CheckBox,
@@ -38,6 +39,8 @@ class ControlTokens {
SearchBarPersonaChip,
SearchBar,
Shimmer,
+ TabBar,
+ TabBarTabItem,
TabItem,
ToggleSwitch
}
@@ -49,6 +52,7 @@ class ControlTokens {
ControlType.Avatar -> AvatarTokens()
ControlType.AvatarCarousel -> AvatarCarouselTokens()
ControlType.AvatarGroup -> AvatarGroupTokens()
+ ControlType.Badge -> BadgeTokens()
ControlType.BottomSheet -> BottomSheetTokens()
ControlType.Button -> ButtonTokens()
ControlType.CheckBox -> CheckBoxTokens()
@@ -68,6 +72,8 @@ class ControlTokens {
ControlType.SearchBarPersonaChip -> SearchBarPersonaChipTokens()
ControlType.SearchBar -> SearchBarTokens()
ControlType.Shimmer -> ShimmerTokens()
+ ControlType.TabBar -> TabBarTokens()
+ ControlType.TabBarTabItem -> TabBarTabItemsTokens()
ControlType.TabItem -> TabItemTokens()
ControlType.ToggleSwitch -> ToggleSwitchTokens()
}
diff --git a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/BadgeTokens.kt b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/BadgeTokens.kt
new file mode 100644
index 00000000..8251ec4b
--- /dev/null
+++ b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/BadgeTokens.kt
@@ -0,0 +1,65 @@
+package com.microsoft.fluentui.theme.token.controlTokens
+
+import android.os.Parcelable
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.microsoft.fluentui.theme.FluentTheme
+import com.microsoft.fluentui.theme.token.*
+import kotlinx.parcelize.Parcelize
+
+enum class BadgeType {
+ Character,
+ List
+}
+
+class BadgeInfo(val type: BadgeType) : ControlInfo
+
+@Parcelize
+open class BadgeTokens : ControlToken, Parcelable {
+
+ @Composable
+ open fun backgroundColor(badgeInfo: BadgeInfo): Color {
+ return FluentTheme.aliasTokens.errorAndStatusColor[AliasTokens.ErrorAndStatusColorTokens.DangerBackground2].value()
+ }
+
+ @Composable
+ open fun textColor(badgeInfo: BadgeInfo): Color {
+ return FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.ForegroundLightStatic].value()
+ }
+
+ @Composable
+ open fun typography(badgeInfo: BadgeInfo): FontInfo {
+ return when (badgeInfo.type) {
+ BadgeType.Character -> FluentTheme.aliasTokens.typography[AliasTokens.TypographyTokens.Caption2]
+ BadgeType.List -> FluentTheme.aliasTokens.typography[AliasTokens.TypographyTokens.Caption1Strong]
+ }
+ }
+
+ @Composable
+ open fun padding(badgeInfo: BadgeInfo): PaddingValues {
+ return when (badgeInfo.type) {
+ BadgeType.Character -> PaddingValues(
+ horizontal = GlobalTokens.size(GlobalTokens.SizeTokens.Size60) + borderStroke(
+ badgeInfo
+ ).width
+ )
+ BadgeType.List -> PaddingValues(
+ start = GlobalTokens.size(GlobalTokens.SizeTokens.Size80) + borderStroke(badgeInfo).width,
+ end = GlobalTokens.size(GlobalTokens.SizeTokens.Size80) + borderStroke(badgeInfo).width,
+ top = 3.dp + borderStroke(badgeInfo).width,
+ bottom = 3.dp + borderStroke(badgeInfo).width
+ )
+ }
+ }
+
+ @Composable
+ open fun borderStroke(badgeInfo: BadgeInfo): BorderStroke {
+ return BorderStroke(
+ GlobalTokens.strokeWidth(GlobalTokens.StrokeWidthTokens.StrokeWidth20),
+ FluentTheme.aliasTokens.neutralStrokeColor[AliasTokens.NeutralStrokeColorTokens.StrokeFocus1].value()
+ )
+ }
+}
\ No newline at end of file
diff --git a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/TabBarTabItemTokens.kt b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/TabBarTabItemTokens.kt
new file mode 100644
index 00000000..0eba6d7d
--- /dev/null
+++ b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/TabBarTabItemTokens.kt
@@ -0,0 +1,33 @@
+package com.microsoft.fluentui.theme.token.controlTokens
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import com.microsoft.fluentui.theme.FluentTheme
+import com.microsoft.fluentui.theme.token.AliasTokens
+import com.microsoft.fluentui.theme.token.StateColor
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+open class TabBarTabItemsTokens : TabItemTokens() {
+
+ @Composable
+ override fun backgroundColor(tabItemInfo: TabItemInfo): Color {
+ return FluentTheme.aliasTokens.neutralBackgroundColor[AliasTokens.NeutralBackgroundColorTokens.Background1].value()
+ }
+
+ @Composable
+ override fun iconColor(tabItemInfo: TabItemInfo): StateColor {
+ return StateColor(
+ rest = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground2].value(),
+ selected = FluentTheme.aliasTokens.brandForegroundColor[AliasTokens.BrandForegroundColorTokens.BrandForeground1].value()
+ )
+ }
+
+ @Composable
+ override fun textColor(tabItemInfo: TabItemInfo): StateColor {
+ return StateColor(
+ rest = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground2].value(),
+ selected = FluentTheme.aliasTokens.brandForegroundColor[AliasTokens.BrandForegroundColorTokens.BrandForeground1].value()
+ )
+ }
+}
\ No newline at end of file
diff --git a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/TabBarTokens.kt b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/TabBarTokens.kt
new file mode 100644
index 00000000..f65c36d4
--- /dev/null
+++ b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/TabBarTokens.kt
@@ -0,0 +1,28 @@
+package com.microsoft.fluentui.theme.token.controlTokens
+
+import android.os.Parcelable
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.Dp
+import com.microsoft.fluentui.theme.FluentTheme
+import com.microsoft.fluentui.theme.token.AliasTokens
+import com.microsoft.fluentui.theme.token.ControlInfo
+import com.microsoft.fluentui.theme.token.ControlToken
+import com.microsoft.fluentui.theme.token.GlobalTokens
+import kotlinx.parcelize.Parcelize
+
+class TabBarInfo : ControlInfo
+
+@Parcelize
+open class TabBarTokens : ControlToken, Parcelable {
+
+ @Composable
+ open fun topBorderColor(tabBarInfo: TabBarInfo): Color {
+ return FluentTheme.aliasTokens.neutralStrokeColor[AliasTokens.NeutralStrokeColorTokens.Stroke2].value()
+ }
+
+ @Composable
+ open fun topBorderWidth(tabBarInfo: TabBarInfo): Dp {
+ return GlobalTokens.strokeWidth(GlobalTokens.StrokeWidthTokens.StrokeWidth05)
+ }
+}
\ No newline at end of file
diff --git a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/TabItemTokens.kt b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/TabItemTokens.kt
index a3c5a6bc..3000775b 100644
--- a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/TabItemTokens.kt
+++ b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/TabItemTokens.kt
@@ -30,7 +30,7 @@ open class TabItemTokens : ControlToken, Parcelable {
}
@Composable
- open fun background(tabItemInfo: TabItemInfo): Color {
+ open fun backgroundColor(tabItemInfo: TabItemInfo): Color {
return FluentTheme.aliasTokens.neutralBackgroundColor[AliasTokens.NeutralBackgroundColorTokens.Background1].value(
FluentTheme.themeMode
)
diff --git a/fluentui_listitem/build.gradle b/fluentui_listitem/build.gradle
index 26a24761..9a7f9a6f 100644
--- a/fluentui_listitem/build.gradle
+++ b/fluentui_listitem/build.gradle
@@ -59,7 +59,7 @@ dependencies {
implementation("androidx.compose.material:material:$composeVersion")
implementation("androidx.compose.runtime:runtime:$composeVersion")
implementation("androidx.compose.ui:ui:$composeVersion")
-
+ implementation "androidx.constraintlayout:constraintlayout-compose:$constraintLayoutComposeVersion"
}
task sourceJar(type: Jar) {
diff --git a/fluentui_listitem/src/main/java/com/microsoft/fluentui/tokenized/contentBuilder/ListContentBuilder.kt b/fluentui_listitem/src/main/java/com/microsoft/fluentui/tokenized/contentBuilder/ListContentBuilder.kt
index b975bdc2..4ce53fd2 100644
--- a/fluentui_listitem/src/main/java/com/microsoft/fluentui/tokenized/contentBuilder/ListContentBuilder.kt
+++ b/fluentui_listitem/src/main/java/com/microsoft/fluentui/tokenized/contentBuilder/ListContentBuilder.kt
@@ -31,7 +31,7 @@ data class ItemData(
var enabled: Boolean = true,
var onClick: () -> Unit,
var accessory: @Composable (() -> Unit)? = null,
- var icon: ImageVector? = null
+ var icon: ImageVector
)
// marker interface
@@ -174,7 +174,7 @@ class ListContentBuilder {
Layout(
modifier = Modifier
.background(
- token.background(
+ token.backgroundColor(
TabItemInfo(
TabTextAlignment.VERTICAL,
FluentStyle.Brand
@@ -250,7 +250,7 @@ class ListContentBuilder {
LazyRow(
state = rowLazyListState, modifier = Modifier
.background(
- token.background(
+ token.backgroundColor(
TabItemInfo(
TabTextAlignment.VERTICAL,
FluentStyle.Brand
diff --git a/fluentui_listitem/src/main/java/com/microsoft/fluentui/tokenized/listitem/ListItem.kt b/fluentui_listitem/src/main/java/com/microsoft/fluentui/tokenized/listitem/ListItem.kt
index 9bf63334..a6074e08 100644
--- a/fluentui_listitem/src/main/java/com/microsoft/fluentui/tokenized/listitem/ListItem.kt
+++ b/fluentui_listitem/src/main/java/com/microsoft/fluentui/tokenized/listitem/ListItem.kt
@@ -786,7 +786,7 @@ object ListItem {
.focusable(false)
) {
Row(
- modifier
+ Modifier
.fillMaxWidth()
.heightIn(min = cellHeight)
.background(backgroundColor)
@@ -811,7 +811,7 @@ object ListItem {
if (accessoryTextTitle != null) {
Text(text = accessoryTextTitle,
- modifier
+ Modifier
.padding(end = horizontalPadding, bottom = verticalPadding)
.clickable(
role = Role.Button,
diff --git a/fluentui_listitem/src/main/java/com/microsoft/fluentui/tokenized/tabItem/TabItem.kt b/fluentui_listitem/src/main/java/com/microsoft/fluentui/tokenized/tabItem/TabItem.kt
index 6fe09459..88fee9fa 100644
--- a/fluentui_listitem/src/main/java/com/microsoft/fluentui/tokenized/tabItem/TabItem.kt
+++ b/fluentui_listitem/src/main/java/com/microsoft/fluentui/tokenized/tabItem/TabItem.kt
@@ -14,9 +14,15 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.ChainStyle
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.Dimension
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.token.ControlTokens
import com.microsoft.fluentui.theme.token.FluentStyle
@@ -39,13 +45,14 @@ fun getTabItemInfo(): TabItemInfo {
}
@Composable
-internal fun TabItem(
+fun TabItem(
title: String,
modifier: Modifier = Modifier,
- icon: ImageVector? = null,
+ icon: ImageVector,
style: FluentStyle = FluentStyle.Neutral,
textAlignment: TabTextAlignment = TabTextAlignment.VERTICAL,
enabled: Boolean = true,
+ selected: Boolean = false,
fixedWidth: Boolean = false,
onClick: () -> Unit,
accessory: (@Composable () -> Unit)?,
@@ -60,18 +67,20 @@ internal fun TabItem(
LocalTabItemTokens provides token,
LocalTabItemInfo provides TabItemInfo(textAlignment, style)
) {
- val textColor = getTabItemTokens().textColor(tabItemInfo = getTabItemInfo()).getColorByState(
- enabled = enabled,
- selected = false,
- interactionSource = interactionSource
- )
- val iconColor = getTabItemTokens().iconColor(tabItemInfo = getTabItemInfo()).getColorByState(
- enabled = enabled,
- selected = false,
- interactionSource = interactionSource
- )
+ val textColor =
+ getTabItemTokens().textColor(tabItemInfo = getTabItemInfo()).getColorByState(
+ enabled = enabled,
+ selected = selected,
+ interactionSource = interactionSource
+ )
+ val iconColor =
+ getTabItemTokens().iconColor(tabItemInfo = getTabItemInfo()).getColorByState(
+ enabled = enabled,
+ selected = selected,
+ interactionSource = interactionSource
+ )
- val backgroundColor = getTabItemTokens().background(tabItemInfo = getTabItemInfo())
+ val backgroundColor = getTabItemTokens().backgroundColor(tabItemInfo = getTabItemInfo())
val rippleColor = getTabItemTokens().rippleColor(tabItemInfo = getTabItemInfo())
val clickableModifier = Modifier
@@ -90,41 +99,115 @@ internal fun TabItem(
Modifier
}
val iconContent: @Composable () -> Unit = {
- Box(
- contentAlignment = Alignment.Center
- ) {
- if (icon != null) {
- Icon(
- imageVector = icon,
- modifier = Modifier.height(if (textAlignment == TabTextAlignment.NO_TEXT) 28.dp else 24.dp),
- contentDescription = if (textAlignment == TabTextAlignment.NO_TEXT) title else null,
- tint = iconColor
- )
- }
- if (accessory != null) {
- accessory()
- }
- }
+ Icon(
+ imageVector = icon,
+ modifier = Modifier.size(if (textAlignment == TabTextAlignment.NO_TEXT) 28.dp else 24.dp),
+ contentDescription = if (textAlignment == TabTextAlignment.NO_TEXT) title else null,
+ tint = iconColor
+ )
}
+
if (textAlignment == TabTextAlignment.HORIZONTAL) {
- Row(
- horizontalArrangement = Arrangement.Center,
- verticalAlignment = Alignment.CenterVertically,
+ ConstraintLayout(
modifier = modifier
.then(clickableModifier)
.background(backgroundColor)
.padding(top = 8.dp, start = 4.dp, bottom = 4.dp, end = 8.dp)
.then(widthModifier)
- ) {
- iconContent()
- Spacer(modifier = Modifier.width(2.dp))
+ )
+ {
+ val (iconConstrain, textConstrain, badgeConstrain) = createRefs()
+
+ Box(modifier = Modifier.constrainAs(iconConstrain) {
+ start.linkTo(parent.start)
+ end.linkTo(textConstrain.start)
+ }
+ ) {
+ iconContent()
+ }
+
Text(
text = title,
+ modifier = Modifier
+ .constrainAs(textConstrain) {
+ start.linkTo(iconConstrain.end)
+ end.linkTo(badgeConstrain.start)
+ width = Dimension.preferredWrapContent
+ }
+ .padding(start = 8.dp),
color = textColor,
- textAlign = TextAlign.Justify
+ textAlign = TextAlign.Center,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1
+ )
+
+ if (accessory != null) {
+ Box(modifier = Modifier
+ .constrainAs(badgeConstrain) {
+ start.linkTo(textConstrain.end)
+ end.linkTo(parent.end)
+ }
+ ) {
+ accessory()
+ }
+ }
+ createHorizontalChain(
+ iconConstrain,
+ textConstrain,
+ badgeConstrain,
+ chainStyle = ChainStyle.Packed
)
}
} else {
+ val badgeWithIcon: @Composable () -> Unit = {
+ Layout(
+ {
+ Box(
+ modifier = Modifier.layoutId("anchor"),
+ contentAlignment = Alignment.Center
+ ) {
+ iconContent()
+ }
+
+ Box(modifier = Modifier.layoutId("badge")) {
+ if (accessory != null)
+ accessory()
+ }
+
+ }
+ ) { measurables, constraints ->
+ val badgePlaceable = measurables.first { it.layoutId == "badge" }.measure(
+ // Measure with loose constraints for height as we don't want the text to take up more
+ // space than it needs.
+ constraints.copy(minHeight = 0)
+ )
+
+ val anchorPlaceable =
+ measurables.first { it.layoutId == "anchor" }.measure(constraints)
+
+ // Use the width of the badge to infer whether it has any content (based on radius used
+ // in [Badge]) and determine its horizontal offset.
+ val hasContent = badgePlaceable.width > 16.dp.roundToPx()
+ val contentOffset = if (hasContent) -2.dp.roundToPx() else 0
+ val badgeHorizontalOffset = -anchorPlaceable.width / 2 + contentOffset
+ val badgeVerticalOffset = (-4).dp
+
+ val totalWidth = anchorPlaceable.width
+ val totalHeight = anchorPlaceable.height
+
+ layout(
+ totalWidth,
+ totalHeight
+ ) {
+
+ anchorPlaceable.placeRelative(0, 0)
+ val badgeX = anchorPlaceable.width + badgeHorizontalOffset
+ val badgeY = badgeVerticalOffset.roundToPx()
+ badgePlaceable.placeRelative(badgeX, badgeY)
+ }
+ }
+ }
+
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
@@ -134,7 +217,7 @@ internal fun TabItem(
.padding(top = 8.dp, start = 8.dp, bottom = 4.dp, end = 8.dp)
.then(widthModifier)
) {
- iconContent()
+ badgeWithIcon()
if (textAlignment == TabTextAlignment.VERTICAL) {
Spacer(modifier = Modifier.height(2.dp))
Text(
diff --git a/fluentui_notification/.gitignore b/fluentui_notification/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/fluentui_notification/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/fluentui_notification/build.gradle b/fluentui_notification/build.gradle
new file mode 100644
index 00000000..d6c8e285
--- /dev/null
+++ b/fluentui_notification/build.gradle
@@ -0,0 +1,71 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+apply from: '../config.gradle'
+apply from: '../publish.gradle'
+
+android {
+ compileSdkVersion constants.compileSdkVersion
+ defaultConfig {
+ minSdkVersion constants.minSdkVersion
+ targetSdkVersion constants.targetSdkVersion
+ versionCode project.ext.fluentui_notification_version_code
+ versionName project.ext.fluentui_notification_versionid
+ testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
+ }
+ lintOptions {
+ abortOnError false
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ testOptions {
+ unitTests {
+ includeAndroidResources = true
+ }
+ }
+ productFlavors {
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ useIR = true
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion composeVersion
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+ implementation project(':fluentui_core')
+
+ implementation 'androidx.core:core-ktx:1.7.0'
+
+ implementation("androidx.compose.foundation:foundation:$composeVersion")
+ implementation("androidx.compose.material:material:$composeVersion")
+ implementation("androidx.compose.runtime:runtime:$composeVersion")
+ implementation("androidx.compose.ui:ui:$composeVersion")
+
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+}
+
+task sourceJar(type: Jar) {
+ from android.sourceSets.main.java.srcDirs
+ classifier "sources"
+}
+
+project.afterEvaluate {
+ project.ext.publishingFunc('fluentui_notification')
+}
\ No newline at end of file
diff --git a/fluentui_notification/consumer-rules.pro b/fluentui_notification/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/fluentui_notification/proguard-rules.pro b/fluentui_notification/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/fluentui_notification/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/fluentui_notification/src/main/AndroidManifest.xml b/fluentui_notification/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..11d49586
--- /dev/null
+++ b/fluentui_notification/src/main/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/fluentui_notification/src/main/java/com/microsoft/fluentui/tokenized/notification/Badge.kt b/fluentui_notification/src/main/java/com/microsoft/fluentui/tokenized/notification/Badge.kt
new file mode 100644
index 00000000..25b9814e
--- /dev/null
+++ b/fluentui_notification/src/main/java/com/microsoft/fluentui/tokenized/notification/Badge.kt
@@ -0,0 +1,113 @@
+package com.microsoft.fluentui.tokenized.notification
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.LocalTextStyle
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.drawscope.Fill
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.PlatformTextStyle
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.dp
+import com.microsoft.fluentui.theme.FluentTheme
+import com.microsoft.fluentui.theme.token.ControlTokens
+import com.microsoft.fluentui.theme.token.controlTokens.BadgeInfo
+import com.microsoft.fluentui.theme.token.controlTokens.BadgeTokens
+import com.microsoft.fluentui.theme.token.controlTokens.BadgeType
+import com.microsoft.fluentui.util.dpToPx
+
+val LocalBadgeInfo = compositionLocalOf { BadgeInfo(BadgeType.Character) }
+val LocalBadgeToken = compositionLocalOf { BadgeTokens() }
+
+@Composable
+fun getBadgeInfo(): BadgeInfo {
+ return LocalBadgeInfo.current
+}
+
+@Composable
+fun getBadgeTokens(): BadgeTokens {
+ return LocalBadgeToken.current
+}
+
+@OptIn(ExperimentalTextApi::class)
+@Composable
+fun Badge(
+ text: String? = null,
+ modifier: Modifier = Modifier,
+ badgeType: BadgeType = BadgeType.List,
+ badgeTokens: BadgeTokens? = null
+) {
+ val token = badgeTokens
+ ?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.Badge] as BadgeTokens
+ CompositionLocalProvider(
+ LocalBadgeToken provides token,
+ LocalBadgeInfo provides BadgeInfo(badgeType)
+ ) {
+ val background = getBadgeTokens().backgroundColor(badgeInfo = getBadgeInfo())
+ val borderStroke = getBadgeTokens().borderStroke(badgeInfo = getBadgeInfo())
+ if (text.isNullOrEmpty()) {
+ Box(
+ modifier = modifier
+ .defaultMinSize(16.dp, 16.dp)
+ ) {
+ Canvas(
+ modifier = Modifier
+ .padding(start = 5.dp, end = 3.dp, top = 3.dp, bottom = 5.dp)
+ .sizeIn(minWidth = 8.dp, minHeight = 8.dp)
+ ) {
+ drawCircle(
+ brush = borderStroke.brush,
+ radius = dpToPx(borderStroke.width + 4.dp)
+ )
+ drawCircle(
+ color = background,
+ style = Fill,
+ radius = dpToPx(4.dp)
+ )
+ }
+ }
+ } else {
+ val textColor = getBadgeTokens().textColor(badgeInfo = getBadgeInfo())
+ val typography = getBadgeTokens().typography(badgeInfo = getBadgeInfo())
+ val paddingValues = getBadgeTokens().padding(badgeInfo = getBadgeInfo())
+
+ Row(
+ modifier
+ .requiredHeight(if (badgeType == BadgeType.Character) 20.dp else 27.dp)
+ .border(borderStroke.width, borderStroke.brush, CircleShape)
+ .padding(0.5.dp) //TODO to check fix for https://issuetracker.google.com/issues/228985905
+ .background(background, CircleShape)
+ .clip(RoundedCornerShape(100.dp)),
+
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Text(
+ text,
+ modifier = Modifier.padding(paddingValues),
+ style = LocalTextStyle.current.merge(
+ TextStyle(
+ color = textColor,
+ lineHeight = typography.fontSize.lineHeight,
+ fontSize = typography.fontSize.size,
+ fontWeight = typography.weight,
+ platformStyle = PlatformTextStyle(
+ includeFontPadding = false
+ )
+ )
+ )
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/fluentui_notification/src/main/res/values/strings.xml b/fluentui_notification/src/main/res/values/strings.xml
new file mode 100644
index 00000000..a6b3daec
--- /dev/null
+++ b/fluentui_notification/src/main/res/values/strings.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/fluentui_tablayout/build.gradle b/fluentui_tablayout/build.gradle
index f2c803ff..a10ed53e 100644
--- a/fluentui_tablayout/build.gradle
+++ b/fluentui_tablayout/build.gradle
@@ -45,6 +45,7 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':fluentui_core')
implementation project(':fluentui_icons')
+ implementation project(':fluentui_listitem')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "androidx.appcompat:appcompat:$appCompatVersion"
implementation "com.google.android.material:material:$materialVersion"
diff --git a/fluentui_tablayout/src/main/java/com/microsoft/fluentui/tokenized/navigation/TabBar.kt b/fluentui_tablayout/src/main/java/com/microsoft/fluentui/tokenized/navigation/TabBar.kt
new file mode 100644
index 00000000..8a87561a
--- /dev/null
+++ b/fluentui_tablayout/src/main/java/com/microsoft/fluentui/tokenized/navigation/TabBar.kt
@@ -0,0 +1,85 @@
+package com.microsoft.fluentui.tokenized.navigation
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.unit.dp
+import com.microsoft.fluentui.theme.FluentTheme
+import com.microsoft.fluentui.theme.token.ControlTokens
+import com.microsoft.fluentui.theme.token.controlTokens.*
+import com.microsoft.fluentui.tokenized.tabItem.TabItem
+
+
+data class TabData(
+ var title: String,
+ var icon: ImageVector,
+ var selectedIcon: ImageVector = icon,
+ var selected: Boolean = false,
+ var onClick: () -> Unit,
+ var badge: @Composable (() -> Unit)? = null
+)
+
+val LocalTabBarInfo = compositionLocalOf { TabBarInfo() }
+val LocalTabBarToken = compositionLocalOf { TabBarTokens() }
+
+@Composable
+fun getTabBarInfo(): TabBarInfo {
+ return LocalTabBarInfo.current
+}
+
+@Composable
+fun getTabBarToken(): TabBarTokens {
+ return LocalTabBarToken.current
+}
+
+@Composable
+fun TabBar(
+ tabDataList: List,
+ modifier: Modifier = Modifier,
+ selectedIndex: Int = 0,
+ tabTextAlignment: TabTextAlignment = TabTextAlignment.VERTICAL,
+ tabItemTokens: TabBarTabItemsTokens? = null,
+ tabBarTokens: TabBarTokens? = null
+) {
+ val token = tabBarTokens
+ ?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.TabBar] as TabBarTokens
+
+ CompositionLocalProvider(
+ LocalTabBarToken provides token
+ ) {
+ Column {
+ Box(
+ modifier
+ .fillMaxWidth()
+ .height(getTabBarToken().topBorderWidth(tabBarInfo = getTabBarInfo()))
+ .background(color = getTabBarToken().topBorderColor(tabBarInfo = getTabBarInfo()))
+ )
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp)
+ ) {
+ tabDataList.forEachIndexed { index, tabData ->
+ tabData.selected = index == selectedIndex
+ TabItem(
+ title = tabData.title,
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1F),
+ icon = if (tabData.selected) tabData.selectedIcon else tabData.icon,
+ textAlignment = tabTextAlignment,
+ selected = tabData.selected,
+ onClick = tabData.onClick,
+ accessory = tabData.badge,
+ tabItemTokens = tabItemTokens
+ ?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.TabBarTabItem] as TabItemTokens
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index a7068015..7f71e455 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -15,3 +15,4 @@ include ':FluentUI'
include ':fluentui_ccb'
include ':fluentui_controls'
include ':fluentui_icons'
+include ':fluentui_notification'