|
|
@ -44,16 +44,20 @@ import androidx.compose.runtime.getValue
|
|
|
|
import androidx.compose.runtime.remember
|
|
|
|
import androidx.compose.runtime.remember
|
|
|
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
|
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
|
|
|
|
import androidx.compose.ui.draw.drawWithContent
|
|
|
|
|
|
|
|
import androidx.compose.ui.geometry.Offset
|
|
|
|
import androidx.compose.ui.graphics.Color
|
|
|
|
import androidx.compose.ui.graphics.Color
|
|
|
|
import androidx.compose.ui.platform.testTag
|
|
|
|
import androidx.compose.ui.platform.testTag
|
|
|
|
import androidx.compose.ui.res.painterResource
|
|
|
|
import androidx.compose.ui.res.painterResource
|
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
|
import androidx.compose.ui.semantics.semantics
|
|
|
|
import androidx.compose.ui.semantics.semantics
|
|
|
|
import androidx.compose.ui.semantics.testTagsAsResourceId
|
|
|
|
import androidx.compose.ui.semantics.testTagsAsResourceId
|
|
|
|
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
|
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
|
|
import androidx.navigation.NavDestination
|
|
|
|
import androidx.navigation.NavDestination
|
|
|
|
import androidx.navigation.NavDestination.Companion.hierarchy
|
|
|
|
import androidx.navigation.NavDestination.Companion.hierarchy
|
|
|
|
import com.google.samples.apps.nowinandroid.R
|
|
|
|
import com.google.samples.apps.nowinandroid.R
|
|
|
|
|
|
|
|
import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository
|
|
|
|
import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
|
|
|
|
import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
|
|
|
|
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground
|
|
|
|
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground
|
|
|
|
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaGradientBackground
|
|
|
|
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaGradientBackground
|
|
|
@ -81,9 +85,11 @@ import com.google.samples.apps.nowinandroid.feature.settings.R as settingsR
|
|
|
|
fun NiaApp(
|
|
|
|
fun NiaApp(
|
|
|
|
windowSizeClass: WindowSizeClass,
|
|
|
|
windowSizeClass: WindowSizeClass,
|
|
|
|
networkMonitor: NetworkMonitor,
|
|
|
|
networkMonitor: NetworkMonitor,
|
|
|
|
|
|
|
|
userNewsResourceRepository: UserNewsResourceRepository,
|
|
|
|
appState: NiaAppState = rememberNiaAppState(
|
|
|
|
appState: NiaAppState = rememberNiaAppState(
|
|
|
|
networkMonitor = networkMonitor,
|
|
|
|
networkMonitor = networkMonitor,
|
|
|
|
windowSizeClass = windowSizeClass,
|
|
|
|
windowSizeClass = windowSizeClass,
|
|
|
|
|
|
|
|
userNewsResourceRepository = userNewsResourceRepository,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
val shouldShowGradientBackground =
|
|
|
|
val shouldShowGradientBackground =
|
|
|
@ -128,8 +134,10 @@ fun NiaApp(
|
|
|
|
snackbarHost = { SnackbarHost(snackbarHostState) },
|
|
|
|
snackbarHost = { SnackbarHost(snackbarHostState) },
|
|
|
|
bottomBar = {
|
|
|
|
bottomBar = {
|
|
|
|
if (appState.shouldShowBottomBar) {
|
|
|
|
if (appState.shouldShowBottomBar) {
|
|
|
|
|
|
|
|
val unreadDestinations by appState.topLevelDestinationsWithUnreadResources.collectAsStateWithLifecycle()
|
|
|
|
NiaBottomBar(
|
|
|
|
NiaBottomBar(
|
|
|
|
destinations = appState.topLevelDestinations,
|
|
|
|
destinations = appState.topLevelDestinations,
|
|
|
|
|
|
|
|
destinationsWithUnreadResources = unreadDestinations,
|
|
|
|
onNavigateToDestination = appState::navigateToTopLevelDestination,
|
|
|
|
onNavigateToDestination = appState::navigateToTopLevelDestination,
|
|
|
|
currentDestination = appState.currentDestination,
|
|
|
|
currentDestination = appState.currentDestination,
|
|
|
|
modifier = Modifier.testTag("NiaBottomBar"),
|
|
|
|
modifier = Modifier.testTag("NiaBottomBar"),
|
|
|
@ -211,6 +219,7 @@ private fun NiaNavRail(
|
|
|
|
imageVector = icon.imageVector,
|
|
|
|
imageVector = icon.imageVector,
|
|
|
|
contentDescription = null,
|
|
|
|
contentDescription = null,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
is DrawableResourceIcon -> Icon(
|
|
|
|
is DrawableResourceIcon -> Icon(
|
|
|
|
painter = painterResource(id = icon.id),
|
|
|
|
painter = painterResource(id = icon.id),
|
|
|
|
contentDescription = null,
|
|
|
|
contentDescription = null,
|
|
|
@ -218,6 +227,7 @@ private fun NiaNavRail(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
label = { Text(stringResource(destination.iconTextId)) },
|
|
|
|
label = { Text(stringResource(destination.iconTextId)) },
|
|
|
|
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -226,6 +236,7 @@ private fun NiaNavRail(
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
private fun NiaBottomBar(
|
|
|
|
private fun NiaBottomBar(
|
|
|
|
destinations: List<TopLevelDestination>,
|
|
|
|
destinations: List<TopLevelDestination>,
|
|
|
|
|
|
|
|
destinationsWithUnreadResources: Set<TopLevelDestination>,
|
|
|
|
onNavigateToDestination: (TopLevelDestination) -> Unit,
|
|
|
|
onNavigateToDestination: (TopLevelDestination) -> Unit,
|
|
|
|
currentDestination: NavDestination?,
|
|
|
|
currentDestination: NavDestination?,
|
|
|
|
modifier: Modifier = Modifier,
|
|
|
|
modifier: Modifier = Modifier,
|
|
|
@ -234,6 +245,7 @@ private fun NiaBottomBar(
|
|
|
|
modifier = modifier,
|
|
|
|
modifier = modifier,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
destinations.forEach { destination ->
|
|
|
|
destinations.forEach { destination ->
|
|
|
|
|
|
|
|
val hasUnread = destinationsWithUnreadResources.contains(destination)
|
|
|
|
val selected = currentDestination.isTopLevelDestinationInHierarchy(destination)
|
|
|
|
val selected = currentDestination.isTopLevelDestinationInHierarchy(destination)
|
|
|
|
NiaNavigationBarItem(
|
|
|
|
NiaNavigationBarItem(
|
|
|
|
selected = selected,
|
|
|
|
selected = selected,
|
|
|
@ -257,11 +269,31 @@ private fun NiaBottomBar(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
label = { Text(stringResource(destination.iconTextId)) },
|
|
|
|
label = { Text(stringResource(destination.iconTextId)) },
|
|
|
|
|
|
|
|
modifier = if (hasUnread) notificationDot() else Modifier,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
|
|
|
|
private fun notificationDot(): Modifier {
|
|
|
|
|
|
|
|
val tertiaryColor = MaterialTheme.colorScheme.tertiary
|
|
|
|
|
|
|
|
return Modifier.drawWithContent {
|
|
|
|
|
|
|
|
drawContent()
|
|
|
|
|
|
|
|
drawCircle(
|
|
|
|
|
|
|
|
tertiaryColor,
|
|
|
|
|
|
|
|
radius = 5.dp.toPx(),
|
|
|
|
|
|
|
|
// This is based on the dimensions of the NavigationBar's "indicator pill";
|
|
|
|
|
|
|
|
// however, its parameters are private, so we must depend on them implicitly
|
|
|
|
|
|
|
|
// (NavigationBarTokens.ActiveIndicatorWidth = 64.dp)
|
|
|
|
|
|
|
|
center = center + Offset(
|
|
|
|
|
|
|
|
64.dp.toPx() * .45f,
|
|
|
|
|
|
|
|
32.dp.toPx() * -.45f - 6.dp.toPx(),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun NavDestination?.isTopLevelDestinationInHierarchy(destination: TopLevelDestination) =
|
|
|
|
private fun NavDestination?.isTopLevelDestinationInHierarchy(destination: TopLevelDestination) =
|
|
|
|
this?.hierarchy?.any {
|
|
|
|
this?.hierarchy?.any {
|
|
|
|
it.route?.contains(destination.name, true) ?: false
|
|
|
|
it.route?.contains(destination.name, true) ?: false
|
|
|
|