Fix merge conflicts

pull/1837/head
Don Turner 3 years ago
commit 49e47685bd

@ -16,6 +16,7 @@
package com.google.samples.apps.nowinandroid.ui package com.google.samples.apps.nowinandroid.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets 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.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.zIndex
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavDestination 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.DrawableResourceIcon
import com.google.samples.apps.nowinandroid.core.designsystem.icon.Icon.ImageVectorIcon 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.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.R as settingsR
import com.google.samples.apps.nowinandroid.feature.settings.SettingsDialog import com.google.samples.apps.nowinandroid.feature.settings.SettingsDialog
import com.google.samples.apps.nowinandroid.navigation.NiaNavHost import com.google.samples.apps.nowinandroid.navigation.NiaNavHost
@ -86,68 +88,26 @@ fun NiaApp(
windowSizeClass = windowSizeClass windowSizeClass = windowSizeClass
), ),
) { ) {
val background: @Composable (@Composable () -> Unit) -> Unit = val shouldShowGradientBackground =
when (appState.currentTopLevelDestination) { appState.currentTopLevelDestination == TopLevelDestination.FOR_YOU
TopLevelDestination.FOR_YOU -> {
content ->
NiaGradientBackground(content = content)
}
else -> { content -> NiaBackground(content = content) }
}
background {
val snackbarHostState = remember { SnackbarHostState() } NiaBackground {
NiaGradientBackground(
Scaffold( gradientColors = if (shouldShowGradientBackground) {
modifier = Modifier.semantics { LocalGradientColors.current
testTagsAsResourceId = true } else {
}, GradientColors()
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) }
)
}
}, },
bottomBar = { ) {
if (appState.shouldShowBottomBar) { val snackbarHostState = remember { SnackbarHostState() }
NiaBottomBar(
destinations = appState.topLevelDestinations,
onNavigateToDestination = appState::navigateToTopLevelDestination,
currentDestination = appState.currentDestination,
modifier = Modifier.testTag("NiaBottomBar")
)
}
}
) { padding ->
val isOffline by appState.isOffline.collectAsStateWithLifecycle() val isOffline by appState.isOffline.collectAsStateWithLifecycle()
// If user is not connected to the internet show a snack bar to inform them. // 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) { LaunchedEffect(isOffline) {
if (isOffline) snackbarHostState.showSnackbar( if (isOffline) snackbarHostState.showSnackbar(
message = notConnected, message = notConnectedMessage,
duration = Indefinite duration = Indefinite
) )
} }
@ -158,37 +118,73 @@ fun NiaApp(
) )
} }
Row( Scaffold(
Modifier modifier = Modifier.semantics {
.fillMaxSize() testTagsAsResourceId = true
.windowInsetsPadding( },
WindowInsets.safeDrawing.only( containerColor = Color.Transparent,
WindowInsetsSides.Horizontal 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()
)
} }
) { padding ->
NiaNavHost( Row(
navController = appState.navController, Modifier
onBackClick = appState::onBackClick, .fillMaxSize()
modifier = Modifier
.padding(padding) .padding(padding)
.consumedWindowInsets(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 Column(Modifier.fillMaxSize()) {
// content doesn't display behind it. // 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) { override fun apply(target: Project) {
with(target) { with(target) {
with(pluginManager) { with(pluginManager) {
apply("org.jetbrains.kotlin.kapt")
apply("dagger.hilt.android.plugin") 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") 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.tooling.preview.Preview
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
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.LocalBackgroundTheme
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
@ -69,23 +70,23 @@ fun NiaBackground(
* of a [Box] within a [Surface]. * of a [Box] within a [Surface].
* *
* @param modifier Modifier to be applied to the background. * @param modifier Modifier to be applied to the background.
* @param topColor The top gradient color to be rendered. * @param gradientColors The gradient colors 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 content The background content. * @param content The background content.
*/ */
@Composable @Composable
fun NiaGradientBackground( fun NiaGradientBackground(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
topColor: Color = LocalGradientColors.current.top, gradientColors: GradientColors = LocalGradientColors.current,
bottomColor: Color = LocalGradientColors.current.bottom,
containerColor: Color = LocalGradientColors.current.container,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
val currentTopColor by rememberUpdatedState(topColor) val currentTopColor by rememberUpdatedState(gradientColors.top)
val currentBottomColor by rememberUpdatedState(bottomColor) val currentBottomColor by rememberUpdatedState(gradientColors.bottom)
Surface( Surface(
color = if (containerColor == Color.Unspecified) Color.Transparent else containerColor, color = if (gradientColors.container == Color.Unspecified) {
Color.Transparent
} else {
gradientColors.container
},
modifier = modifier.fillMaxSize() modifier = modifier.fillMaxSize()
) { ) {
Box( Box(

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

@ -22,6 +22,10 @@ import androidx.compose.ui.graphics.Color
/** /**
* A class to model gradient color values for Now in Android. * 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 @Immutable
data class GradientColors( data class GradientColors(

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

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

@ -17,7 +17,6 @@
package com.google.samples.apps.nowinandroid.feature.bookmarks package com.google.samples.apps.nowinandroid.feature.bookmarks
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertHasClickAction import androidx.compose.ui.test.assertHasClickAction
import androidx.compose.ui.test.filter import androidx.compose.ui.test.filter
@ -64,8 +63,6 @@ class BookmarksScreenTest {
@Test @Test
fun feed_whenHasBookmarks_showsBookmarks() { fun feed_whenHasBookmarks_showsBookmarks() {
lateinit var windowSizeClass: WindowSizeClass
composeTestRule.setContent { composeTestRule.setContent {
BookmarksScreen( BookmarksScreen(
feedState = NewsFeedUiState.Success( feedState = NewsFeedUiState.Success(
@ -135,4 +132,26 @@ class BookmarksScreenTest {
assertTrue(removeFromBookmarksCalled) 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 @Preview
@Composable @Composable
fun EmptyStatePreview() { private fun EmptyStatePreview() {
NiaTheme { NiaTheme {
EmptyState() EmptyState()
} }

@ -52,12 +52,12 @@ class InterestsScreenTest {
@Before @Before
fun setup() { fun setup() {
composeTestRule.activity.apply { composeTestRule.activity.apply {
interestsLoading = getString(R.string.interests_loading) interestsLoading = getString(R.string.loading)
interestsEmptyHeader = getString(R.string.interests_empty_header) interestsEmptyHeader = getString(R.string.empty_header)
interestsTopicCardFollowButton = interestsTopicCardFollowButton =
getString(R.string.interests_card_follow_button_content_desc) getString(R.string.card_follow_button_content_desc)
interestsTopicCardUnfollowButton = 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( Icon(
imageVector = NiaIcons.Add, imageVector = NiaIcons.Add,
contentDescription = stringResource( contentDescription = stringResource(
id = string.interests_card_follow_button_content_desc id = string.card_follow_button_content_desc
) )
) )
}, },
@ -84,7 +84,7 @@ fun InterestsItem(
Icon( Icon(
imageVector = NiaIcons.Check, imageVector = NiaIcons.Check,
contentDescription = stringResource( 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 -> InterestsUiState.Loading ->
NiaLoadingWheel( NiaLoadingWheel(
modifier = modifier, modifier = modifier,
contentDesc = stringResource(id = R.string.interests_loading), contentDesc = stringResource(id = R.string.loading),
) )
is InterestsUiState.Interests -> is InterestsUiState.Interests ->
TopicsTabContent( TopicsTabContent(
@ -81,7 +81,7 @@ internal fun InterestsScreen(
@Composable @Composable
private fun InterestsEmptyScreen() { private fun InterestsEmptyScreen() {
Text(text = stringResource(id = R.string.interests_empty_header)) Text(text = stringResource(id = R.string.empty_header))
} }
@DevicePreviews @DevicePreviews

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

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

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

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

Loading…
Cancel
Save