Fix merge conflicts

pull/521/head
Don Turner 2 years ago
commit 32bc5c1590

@ -16,6 +16,7 @@
package com.google.samples.apps.nowinandroid.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
@ -49,7 +50,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.zIndex
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavDestination
@ -66,6 +66,8 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopAp
import com.google.samples.apps.nowinandroid.core.designsystem.icon.Icon.DrawableResourceIcon
import com.google.samples.apps.nowinandroid.core.designsystem.icon.Icon.ImageVectorIcon
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.GradientColors
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors
import com.google.samples.apps.nowinandroid.feature.settings.R as settingsR
import com.google.samples.apps.nowinandroid.feature.settings.SettingsDialog
import com.google.samples.apps.nowinandroid.navigation.NiaNavHost
@ -86,68 +88,26 @@ fun NiaApp(
windowSizeClass = windowSizeClass
),
) {
val background: @Composable (@Composable () -> Unit) -> Unit =
when (appState.currentTopLevelDestination) {
TopLevelDestination.FOR_YOU -> {
content ->
NiaGradientBackground(content = content)
}
else -> { content -> NiaBackground(content = content) }
}
background {
val shouldShowGradientBackground =
appState.currentTopLevelDestination == TopLevelDestination.FOR_YOU
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
modifier = Modifier.semantics {
testTagsAsResourceId = true
},
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.onBackground,
contentWindowInsets = WindowInsets(0, 0, 0, 0),
snackbarHost = { SnackbarHost(snackbarHostState) },
topBar = {
// Show the top app bar on top level destinations.
val destination = appState.currentTopLevelDestination
if (destination != null) {
NiaTopAppBar(
// When the nav rail is displayed, the top app bar will, by default
// overlap it. This means that the top most item in the nav rail
// won't be tappable. A workaround is to position the top app bar
// behind the nav rail using zIndex.
modifier = Modifier.zIndex(-1F),
titleRes = destination.titleTextId,
actionIcon = NiaIcons.Settings,
actionIconContentDescription = stringResource(
id = settingsR.string.top_app_bar_action_icon_description
),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = Color.Transparent
),
onActionClick = { appState.setShowSettingsDialog(true) }
)
}
NiaBackground {
NiaGradientBackground(
gradientColors = if (shouldShowGradientBackground) {
LocalGradientColors.current
} else {
GradientColors()
},
bottomBar = {
if (appState.shouldShowBottomBar) {
NiaBottomBar(
destinations = appState.topLevelDestinations,
onNavigateToDestination = appState::navigateToTopLevelDestination,
currentDestination = appState.currentDestination,
modifier = Modifier.testTag("NiaBottomBar")
)
}
}
) { padding ->
) {
val snackbarHostState = remember { SnackbarHostState() }
val isOffline by appState.isOffline.collectAsStateWithLifecycle()
// If user is not connected to the internet show a snack bar to inform them.
val notConnected = stringResource(R.string.not_connected)
val notConnectedMessage = stringResource(R.string.not_connected)
LaunchedEffect(isOffline) {
if (isOffline) snackbarHostState.showSnackbar(
message = notConnected,
message = notConnectedMessage,
duration = Indefinite
)
}
@ -158,37 +118,73 @@ fun NiaApp(
)
}
Row(
Modifier
.fillMaxSize()
.windowInsetsPadding(
WindowInsets.safeDrawing.only(
WindowInsetsSides.Horizontal
Scaffold(
modifier = Modifier.semantics {
testTagsAsResourceId = true
},
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.onBackground,
contentWindowInsets = WindowInsets(0, 0, 0, 0),
snackbarHost = { SnackbarHost(snackbarHostState) },
bottomBar = {
if (appState.shouldShowBottomBar) {
NiaBottomBar(
destinations = appState.topLevelDestinations,
onNavigateToDestination = appState::navigateToTopLevelDestination,
currentDestination = appState.currentDestination,
modifier = Modifier.testTag("NiaBottomBar")
)
)
) {
if (appState.shouldShowNavRail) {
NiaNavRail(
destinations = appState.topLevelDestinations,
onNavigateToDestination = appState::navigateToTopLevelDestination,
currentDestination = appState.currentDestination,
modifier = Modifier
.testTag("NiaNavRail")
.safeDrawingPadding()
)
}
}
NiaNavHost(
navController = appState.navController,
onBackClick = appState::onBackClick,
modifier = Modifier
) { padding ->
Row(
Modifier
.fillMaxSize()
.padding(padding)
.consumedWindowInsets(padding)
)
.windowInsetsPadding(
WindowInsets.safeDrawing.only(
WindowInsetsSides.Horizontal
)
)
) {
if (appState.shouldShowNavRail) {
NiaNavRail(
destinations = appState.topLevelDestinations,
onNavigateToDestination = appState::navigateToTopLevelDestination,
currentDestination = appState.currentDestination,
modifier = Modifier
.testTag("NiaNavRail")
.safeDrawingPadding()
)
}
// TODO: We may want to add padding or spacer when the snackbar is shown so that
// content doesn't display behind it.
Column(Modifier.fillMaxSize()) {
// Show the top app bar on top level destinations.
val destination = appState.currentTopLevelDestination
if (destination != null) {
NiaTopAppBar(
titleRes = destination.titleTextId,
actionIcon = NiaIcons.Settings,
actionIconContentDescription = stringResource(
id = settingsR.string.top_app_bar_action_icon_description
),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = Color.Transparent
),
onActionClick = { appState.setShowSettingsDialog(true) }
)
}
NiaNavHost(
navController = appState.navController,
onBackClick = appState::onBackClick
)
}
// TODO: We may want to add padding or spacer when the snackbar is shown so that
// content doesn't display behind it.
}
}
}
}

@ -27,8 +27,10 @@ class AndroidHiltConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("org.jetbrains.kotlin.kapt")
apply("dagger.hilt.android.plugin")
// KAPT must go last to avoid build warnings.
// See: https://stackoverflow.com/questions/70550883/warning-the-following-options-were-not-recognized-by-any-processor-dagger-f
apply("org.jetbrains.kotlin.kapt")
}
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")

@ -34,6 +34,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.theme.GradientColors
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalBackgroundTheme
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
@ -69,23 +70,23 @@ fun NiaBackground(
* of a [Box] within a [Surface].
*
* @param modifier Modifier to be applied to the background.
* @param topColor The top gradient color to be rendered.
* @param bottomColor The bottom gradient color to be rendered.
* @param containerColor The container color over which the gradient will be rendered.
* @param gradientColors The gradient colors to be rendered.
* @param content The background content.
*/
@Composable
fun NiaGradientBackground(
modifier: Modifier = Modifier,
topColor: Color = LocalGradientColors.current.top,
bottomColor: Color = LocalGradientColors.current.bottom,
containerColor: Color = LocalGradientColors.current.container,
gradientColors: GradientColors = LocalGradientColors.current,
content: @Composable () -> Unit
) {
val currentTopColor by rememberUpdatedState(topColor)
val currentBottomColor by rememberUpdatedState(bottomColor)
val currentTopColor by rememberUpdatedState(gradientColors.top)
val currentBottomColor by rememberUpdatedState(gradientColors.bottom)
Surface(
color = if (containerColor == Color.Unspecified) Color.Transparent else containerColor,
color = if (gradientColors.container == Color.Unspecified) {
Color.Transparent
} else {
gradientColors.container
},
modifier = modifier.fillMaxSize()
) {
Box(

@ -104,7 +104,7 @@ fun NiaTopAppBar(
@OptIn(ExperimentalMaterial3Api::class)
@Preview("Top App Bar")
@Composable
fun NiaTopAppBarPreview() {
private fun NiaTopAppBarPreview() {
NiaTopAppBar(
titleRes = android.R.string.untitled,
navigationIcon = NiaIcons.Search,

@ -22,6 +22,10 @@ import androidx.compose.ui.graphics.Color
/**
* A class to model gradient color values for Now in Android.
*
* @param top The top gradient color to be rendered.
* @param bottom The bottom gradient color to be rendered.
* @param container The container gradient color over which the gradient will be rendered.
*/
@Immutable
data class GradientColors(

@ -106,7 +106,7 @@ sealed interface NewsFeedUiState {
@Preview
@Composable
fun NewsFeedLoadingPreview() {
private fun NewsFeedLoadingPreview() {
NiaTheme {
LazyVerticalGrid(columns = GridCells.Adaptive(300.dp)) {
newsFeed(
@ -120,7 +120,7 @@ fun NewsFeedLoadingPreview() {
@Preview
@Preview(device = Devices.TABLET)
@Composable
fun NewsFeedContentPreview() {
private fun NewsFeedContentPreview() {
NiaTheme {
LazyVerticalGrid(columns = GridCells.Adaptive(300.dp)) {
newsFeed(

@ -215,6 +215,7 @@ fun NewsResourceMetaData(
@Composable
fun NewsResourceLink(
@Suppress("UNUSED_PARAMETER")
newsResource: NewsResource
) {
TODO()
@ -266,7 +267,7 @@ fun NewsResourceTopics(
@Preview("Bookmark Button")
@Composable
fun BookmarkButtonPreview() {
private fun BookmarkButtonPreview() {
NiaTheme {
Surface {
BookmarkButton(isBookmarked = false, onClick = { })
@ -276,7 +277,7 @@ fun BookmarkButtonPreview() {
@Preview("Bookmark Button Bookmarked")
@Composable
fun BookmarkButtonBookmarkedPreview() {
private fun BookmarkButtonBookmarkedPreview() {
NiaTheme {
Surface {
BookmarkButton(isBookmarked = true, onClick = { })
@ -286,7 +287,7 @@ fun BookmarkButtonBookmarkedPreview() {
@Preview("NewsResourceCardExpanded")
@Composable
fun ExpandedNewsResourcePreview() {
private fun ExpandedNewsResourcePreview() {
NiaTheme {
Surface {
NewsResourceCardExpanded(

@ -17,7 +17,6 @@
package com.google.samples.apps.nowinandroid.feature.bookmarks
import androidx.activity.ComponentActivity
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertHasClickAction
import androidx.compose.ui.test.filter
@ -64,8 +63,6 @@ class BookmarksScreenTest {
@Test
fun feed_whenHasBookmarks_showsBookmarks() {
lateinit var windowSizeClass: WindowSizeClass
composeTestRule.setContent {
BookmarksScreen(
feedState = NewsFeedUiState.Success(
@ -135,4 +132,26 @@ class BookmarksScreenTest {
assertTrue(removeFromBookmarksCalled)
}
@Test
fun feed_whenHasNoBookmarks_showsEmptyState() {
composeTestRule.setContent {
BookmarksScreen(
feedState = NewsFeedUiState.Success(emptyList()),
removeFromBookmarks = { }
)
}
composeTestRule
.onNodeWithText(
composeTestRule.activity.getString(R.string.bookmarks_empty_error)
)
.assertExists()
composeTestRule
.onNodeWithText(
composeTestRule.activity.getString(R.string.bookmarks_empty_description)
)
.assertExists()
}
}

@ -192,7 +192,7 @@ private fun BookmarksGridPreview() {
@Preview
@Composable
fun EmptyStatePreview() {
private fun EmptyStatePreview() {
NiaTheme {
EmptyState()
}

@ -52,12 +52,12 @@ class InterestsScreenTest {
@Before
fun setup() {
composeTestRule.activity.apply {
interestsLoading = getString(R.string.interests_loading)
interestsEmptyHeader = getString(R.string.interests_empty_header)
interestsLoading = getString(R.string.loading)
interestsEmptyHeader = getString(R.string.empty_header)
interestsTopicCardFollowButton =
getString(R.string.interests_card_follow_button_content_desc)
getString(R.string.card_follow_button_content_desc)
interestsTopicCardUnfollowButton =
getString(R.string.interests_card_unfollow_button_content_desc)
getString(R.string.card_unfollow_button_content_desc)
}
}

@ -76,7 +76,7 @@ fun InterestsItem(
Icon(
imageVector = NiaIcons.Add,
contentDescription = stringResource(
id = string.interests_card_follow_button_content_desc
id = string.card_follow_button_content_desc
)
)
},
@ -84,7 +84,7 @@ fun InterestsItem(
Icon(
imageVector = NiaIcons.Check,
contentDescription = stringResource(
id = string.interests_card_unfollow_button_content_desc
id = string.card_unfollow_button_content_desc
)
)
}

@ -65,7 +65,7 @@ internal fun InterestsScreen(
InterestsUiState.Loading ->
NiaLoadingWheel(
modifier = modifier,
contentDesc = stringResource(id = R.string.interests_loading),
contentDesc = stringResource(id = R.string.loading),
)
is InterestsUiState.Interests ->
TopicsTabContent(
@ -81,7 +81,7 @@ internal fun InterestsScreen(
@Composable
private fun InterestsEmptyScreen() {
Text(text = stringResource(id = R.string.interests_empty_header))
Text(text = stringResource(id = R.string.empty_header))
}
@DevicePreviews

@ -15,13 +15,12 @@
limitations under the License.
-->
<resources>
<!-- TODO: Remove the redundant "interests" prefix -->
<string name="interests">Interests</string>
<string name="interests_loading">Loading data</string>
<string name="interests_empty_header">"No available data"</string>
<string name="interests_card_follow_button_content_desc">Follow interest button</string>
<string name="interests_card_unfollow_button_content_desc">Unfollow interest button</string>
<string name="interests_top_app_bar_title">Interests</string>
<string name="interests_top_app_bar_action_menu">Menu</string>
<string name="interests_top_app_bar_action_seearch">Search</string>
<string name="loading">Loading data</string>
<string name="empty_header">"No available data"</string>
<string name="card_follow_button_content_desc">Follow interest button</string>
<string name="card_unfollow_button_content_desc">Unfollow interest button</string>
<string name="top_app_bar_title">Interests</string>
<string name="top_app_bar_action_menu">Menu</string>
<string name="top_app_bar_action_search">Search</string>
</resources>

@ -255,7 +255,7 @@ private fun TextLink(text: String, url: String) {
@Preview
@Composable
fun PreviewSettingsDialog() {
private fun PreviewSettingsDialog() {
NiaTheme {
SettingsDialog(
onDismiss = {},
@ -273,7 +273,7 @@ fun PreviewSettingsDialog() {
@Preview
@Composable
fun PreviewSettingsDialogLoading() {
private fun PreviewSettingsDialogLoading() {
NiaTheme {
SettingsDialog(
onDismiss = {},

@ -45,7 +45,6 @@ class TopicViewModel @Inject constructor(
stringDecoder: StringDecoder,
private val userDataRepository: UserDataRepository,
topicsRepository: TopicsRepository,
// newsRepository: NewsRepository,
getSaveableNewsResources: GetUserNewsResourcesUseCase
) : ViewModel() {
@ -97,13 +96,13 @@ private fun topicUiState(
.map { it.followedTopics }
// Observe topic information
val topic: Flow<Topic> = topicsRepository.getTopic(
val topicStream: Flow<Topic> = topicsRepository.getTopic(
id = topicId
)
return combine(
followedTopicIds,
topic,
topicStream,
::Pair
)
.asResult()
@ -135,7 +134,7 @@ private fun newsUiState(
userDataRepository: UserDataRepository,
): Flow<NewsUiState> {
// Observe news
val news: Flow<List<UserNewsResource>> = getSaveableNewsResources(
val newsStream: Flow<List<UserNewsResource>> = getSaveableNewsResources(
filterTopicIds = setOf(element = topicId),
)
@ -144,7 +143,7 @@ private fun newsUiState(
.map { it.bookmarkedNewsResources }
return combine(
news,
newsStream,
bookmark,
::Pair
)
@ -152,7 +151,7 @@ private fun newsUiState(
.map { newsToBookmarksResult ->
when (newsToBookmarksResult) {
is Result.Success -> {
val (news, bookmarks) = newsToBookmarksResult.data
val news = newsToBookmarksResult.data.first
NewsUiState.Success(news)
}
is Result.Loading -> {

@ -1,12 +1,12 @@
[versions]
accompanist = "0.28.0"
androidDesugarJdkLibs = "1.2.0"
androidDesugarJdkLibs = "1.2.2"
androidGradlePlugin = "7.3.1"
androidxActivity = "1.6.1"
androidxAppCompat = "1.5.1"
androidxBrowser = "1.4.0"
androidxComposeBom = "2022.11.00"
androidxComposeCompiler = "1.3.2"
androidxComposeBom = "2022.12.00"
androidxComposeCompiler = "1.4.0-alpha02"
androidxComposeRuntimeTracing = "1.0.0-alpha01"
androidxCore = "1.9.0"
androidxCoreSplashscreen = "1.0.0"
@ -17,7 +17,7 @@ androidxLifecycle = "2.6.0-alpha03"
androidxMacroBenchmark = "1.1.1"
androidxMetrics = "1.0.0-alpha03"
androidxNavigation = "2.5.3"
androidxProfileinstaller = "1.2.0"
androidxProfileinstaller = "1.2.1"
androidxStartup = "1.1.1"
androidxTestCore = "1.5.0"
androidxTestExt = "1.1.4"
@ -32,18 +32,18 @@ hilt = "2.44.2"
hiltExt = "1.0.0"
jacoco = "0.8.7"
junit4 = "4.13.2"
kotlin = "1.7.20"
kotlin = "1.7.21"
kotlinxCoroutines = "1.6.4"
kotlinxDatetime = "0.4.0"
kotlinxSerializationJson = "1.4.1"
ksp = "1.7.21-1.0.8"
lint = "30.3.1"
okhttp = "4.10.0"
protobuf = "3.21.9"
protobuf = "3.21.12"
protobufPlugin = "0.8.19"
retrofit = "2.9.0"
retrofitKotlinxSerializationJson = "0.8.0"
room = "2.5.0-beta02"
room = "2.5.0-rc01"
secrets = "2.0.1"
turbine = "0.12.1"

Loading…
Cancel
Save