Android Setup
Hub
Requires SDK 4.13.0 or later
The Hub (previously known as the Communication Hub) is an embeddable app area that includes an inbox of posts and other features. It is intended to be embedded within a tab bar item in the app, and includes support for badging the tab bar item when new content is available.
Hub
This feature is distinct from the legacy "Inbox" feature. The legacy Inbox (InboxActivity et al) is deprecated and will be removed in a future release.
Configuration Change
The title and accentColor parameters were previously documented here. These are now configured server-side, so remove them from your client code.
Jetpack Compose
To embed the Hub composable as a new tab in a typical Jetpack Compose Navigation Bar, use a NavigationBarItem:
import io.rover.sdk.notifications.communicationhub.ui.Hub
enum class SelectedTab {
/* ... other tab definitions ... */
RoverHub,
}
@Composable
fun MyApp() {
var selectedTab: SelectedTab by remember { mutableStateOf(SelectedTab.RoverHub) }
MaterialTheme(
// ... ensure a Material 3 theme is configured in your app ...
) {
Scaffold(
modifier = Modifier.fillMaxSize(),
bottomBar = {
NavigationBar() {
/* ... other tab button items ... */
NavigationBarItem(
selected = selectedTab == SelectedTab.RoverHub,
icon = {
// observe the Rover badge count
val badgeText by Rover.shared.roverBadge.newBadge.collectAsState()
// use a BadgedBox to display the badge if it is non-null
BadgedBox(
badge = {
badgeText?.let { text ->
Badge { Text(text) }
}
}
) {
// Use an "Inbox" filled icon from Google's Material Icons. You will need to do the standard Google procedure for adding the Material icon to your app. https://fonts.google.com/
Icon(painter = painterResource(id = R.drawable.inboxfilled48), contentDescription = "For You", modifier = Modifier.size(24.dp))
}
},
label = {
Text("For You", maxLines = 1)
},
onClick = {
selectedTab = SelectedTab.RoverHub
}
)
}
}
) {
when (selectedTab) {
/* ... other tab content ... */
SelectedTab.RoverHub -> {
Hub(
// any custom jetpack compose modifiers:
modifier = Modifier
)
}
}
}
}
Theme configuration for WebView in Dark Mode
Even though the Rover Hub uses Jetpack Compose, Android WebView will only honour dark mode if you have a values-night theme (as used by classic Android views) configured with the isLight property set to false. If you don't do this, posts in the Hub will not be displayed in dark mode.
Ensure you have a values-night/themes.xml file in your app with the following content:
<item name="android:isLightTheme">false</item>
App Icon Badging
Unlike iOS, Android does not support automatic app icon badging due to platform limitations and the way Android app badges work. The badge count functionality shown in the examples above only applies to in-app UI elements like tab bars and navigation items, not the app icon itself.
Compose Theme & Accent Color Configuration
When embedded in a tab, the Hub composable will adopt whatever Material 3 theme it is wrapped in (particularly for tinting controls like hyperlinks with the primary/accent color), just as other composables do. By wrapping a Material 3 Jetpack Compose theme around your Compose UI content, this will happen automatically for an embedded Hub.
However, when the Hub is launched from a deep link, this theme provided by UI context is not available. Therefore, in order for the Hub UI to be properly styled, Material 3 color schemes need to be supplied globally to Rover for it to use when it launches a standalone activity with the Hub composable.
Typically, as part of your Material 3 Theme, you will have defined a lightScheme and darkScheme in your app.
You can set these to Rover's global lightColorScheme and darkColorScheme properties to ensure that the Hub is presented in the correct theme when launched from a deep link:
Rover.shared.lightColorScheme = lightScheme
Rover.shared.darkColorScheme = darkScheme
Classic Android Views
To use the Hub in a classic Android Views app with an MDC BottomNavigationView, and display a badge count on the Hub tab, perform the following steps.
- Create a Fragment to host the Hub composable:
import io.rover.sdk.notifications.communicationhub.ui.Hub
class HubFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Use ComposeView to host the Hub composable
return ComposeView(requireContext()).apply {
setContent {
// configure a Material 3 theme as you would normally do here. Rover's Hub will adopt it, particularly for the primary (or accent) color.
MaterialTheme {
Hub(
// any custom jetpack compose modifiers:
modifier = Modifier
)
}
}
}
}
}
- In your Activity, set up the BottomNavigationView and observe the badge count:
class MainActivity : AppCompatActivity() {
private var badgeJob: Job? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) // Your layout with a FrameLayout and BottomNavigationView
// Set initial fragment
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
// where "HomeFragment" is the view for your default tab
.replace(R.id.fragment_container, HomeFragment())
.commit()
}
val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_navigation)
// Set up badge for Hub tab
val hubMenuItemId = R.id.tab_hub
// Observe badge count using a coroutine
badgeJob = lifecycleScope.launch {
// Import kotlinx.coroutines.flow.collectLatest
io.rover.sdk.core.Rover.shared.roverBadge.newBadge.collect { badgeText ->
val badge = bottomNav.getOrCreateBadge(hubMenuItemId)
if (badgeText.isNullOrEmpty() || badgeText == "0") {
// Remove badge if no unread
bottomNav.removeBadge(hubMenuItemId)
} else {
badge.isVisible = true
badge.number = badgeText.toIntOrNull() ?: 0
}
}
}
bottomNav.setOnItemSelectedListener { item ->
when (item.itemId) {
/* ... other tab views ... */
R.id.tab_hub -> {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, HubFragment())
.commit()
true
}
// ... handle other tabs ...
else -> false
}
}
}
override fun onDestroy() {
super.onDestroy()
badgeJob?.cancel()
}
}
- In your res/layout/activity_main.xml, you should have something like:
<LinearLayout ... >
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
... />
</LinearLayout>
- In your res/menu/bottom_nav_menu.xml, define the menu items:
<menu>
<item
android:id="@+id/tab_home"
android:icon="@drawable/ic_home"
android:title="Home"/>
<item
android:id="@+id/tab_hub"
android:icon="@drawable/ic_inbox"
android:title="For You"/>
<!-- ... other tabs ... -->
</menu>
This approach allows you to use the Hub composable in a classic Android Views app with a BottomNavigationView, and display a badge count on the Hub tab that updates automatically.