Merge "[NiA] Implement top level navigation" into main

pull/2/head
Jolanda Verhoef 3 years ago committed by Gerrit Code Review
commit e28c196a6d

@ -118,8 +118,7 @@ dependencies {
implementation libs.androidx.core.ktx implementation libs.androidx.core.ktx
implementation libs.androidx.appcompat implementation libs.androidx.appcompat
implementation libs.androidx.lifecycle.viewModelCompose implementation libs.androidx.lifecycle.viewModelCompose
implementation libs.androidx.navigation.fragment implementation libs.androidx.navigation.compose
implementation libs.androidx.navigation.ui.ktx
implementation libs.material3 implementation libs.material3
implementation libs.accompanist.insets implementation libs.accompanist.insets
@ -127,6 +126,7 @@ dependencies {
implementation libs.androidx.compose.foundation.layout implementation libs.androidx.compose.foundation.layout
// TODO (M3): Remove this dependency when all components are available // TODO (M3): Remove this dependency when all components are available
implementation libs.androidx.compose.material implementation libs.androidx.compose.material
implementation libs.androidx.compose.material.iconsExtended
implementation libs.androidx.compose.material3 implementation libs.androidx.compose.material3
implementation libs.androidx.compose.ui.tooling implementation libs.androidx.compose.ui.tooling
implementation libs.androidx.compose.ui.util implementation libs.androidx.compose.ui.util
@ -140,10 +140,8 @@ dependencies {
testImplementation libs.androidx.test.core testImplementation libs.androidx.test.core
testImplementation libs.kotlinx.coroutines.test testImplementation libs.kotlinx.coroutines.test
androidTestImplementation libs.junit4
androidTestImplementation libs.androidx.test.core
androidTestImplementation libs.androidx.test.espresso.core androidTestImplementation libs.androidx.test.espresso.core
androidTestImplementation libs.androidx.test.ext.junit androidTestImplementation libs.androidx.test.runner
androidTestImplementation libs.androidx.test.rules androidTestImplementation libs.androidx.test.rules
androidTestImplementation libs.androidx.compose.ui.test androidTestImplementation libs.androidx.compose.ui.test

@ -0,0 +1,176 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.espresso.Espresso
import androidx.test.espresso.NoActivityResumedException
import org.junit.Before
import org.junit.Rule
import org.junit.Test
/**
* Tests all the navigation flows that are handled by the navigation library.
*/
class NavigationTest {
/**
* Using an empty activity to have control of the content that is set.
*/
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@Before
fun setUp() {
// Using targetContext as the Context of the instrumentation code
composeTestRule.setContent {
NiaApp()
}
}
@Test
fun firstScreenIsForYou() {
composeTestRule.forYouDestinationTopMatcher()
.assertExists("Could not find FOR YOU text on first screen after app open")
}
// TODO: implement tests related to navigation & resetting of destinations (b/213307564)
/**
* As per guidelines:
*
* When you select a navigation bar item (one thats not currently selected), the app navigates
* to that destinations screen.
*
* Any prior user interactions and temporary screen states are reset, such as scroll position,
* tab selection, and inline search.
*
* This default behavior can be overridden when needed to improve the user experience. For
* example, an Android app that requires frequent switching between sections can preserve each
* sections state.
*/
// @Test
// fun navigateToUnselectedTabResetsContent1() {
// // GIVEN the user was previously on the Topics destination
// composeTestRule.topicsDestinationTopMatcher().performClick()
// // and scrolled down
// [IMPLEMENT] Match the root scrollable container and scroll down to an item below the fold
// composeTestRule.topicsDestinationTopMatcher()
// .assertDoesNotExist() // verify we scrolled beyond the top
// // and then navigated back to the For You destination
// composeTestRule.forYouDestinationTopMatcher().performClick()
// // WHEN the user presses the Topic navigation bar item
// composeTestRule.topicsDestinationTopMatcher().performClick()
// // THEN the Topics destination shows at the top.
// composeTestRule.topicsDestinationTopMatcher()
// .assertExists("Screen did not correctly reset to the top after re-navigating to it")
// }
// @Test
// fun navigateToUnselectedTabResetsContent2() {
// // GIVEN the user was previously on the Topics destination
// composeTestRule.topicsDestinationTopMatcher().performClick()
// // and navigated to the Topic detail destination
// [IMPLEMENT] Navigate to topic detail destination
// composeTestRule.topicsDestinationTopMatcher()
// .assertDoesNotExist() // verify we are not on topics overview destination any more
// // and then navigated back to the For You destination
// composeTestRule.forYouDestinationTopMatcher().performClick()
// // WHEN the user presses the Topic navigation bar item
// composeTestRule.topicsDestinationTopMatcher().performClick()
// // THEN the Topics destination shows at the top.
// composeTestRule.topicsDestinationTopMatcher()
// .assertExists("Screen did not correctly reset to the top after re-navigating to it")
// }
// @Test
// fun reselectingTabResetsContent1() {
// // GIVEN the user is on the For You destination
// // and has scrolled down
// // WHEN the user taps the For You navigation bar item
// // THEN the For You destination shows at the top of the destination
// }
// @Test
// fun reselectingTabResetsContent2() {
// // GIVEN the user is on the Topics destination
// // and navigates to the Topic Detail destination
// // WHEN the user taps the Topics navigation bar item
// // THEN the Topics destination shows at the top of the destination
// }
/*
* Top level destinations should never show an up affordance.
*/
@Test
fun topLevelDestinationsDoNotShowUpArrow() {
// GIVEN the user is on any of the top level destinations, THEN the Up arrow is not shown.
composeTestRule.onNodeWithContentDescription("Navigate up").assertDoesNotExist()
composeTestRule.onNodeWithText("Episodes").performClick()
composeTestRule.onNodeWithContentDescription("Navigate up").assertDoesNotExist()
composeTestRule.onNodeWithText("Saved").performClick()
composeTestRule.onNodeWithContentDescription("Navigate up").assertDoesNotExist()
composeTestRule.onNodeWithText("Topics").performClick()
composeTestRule.onNodeWithContentDescription("Navigate up").assertDoesNotExist()
}
/*
* There should always be at most one instance of a top-level destination at the same time.
*/
@Test(expected = NoActivityResumedException::class)
fun backFromHomeDestinationQuitsApp() {
// GIVEN the user navigates to the Episodes destination
composeTestRule.onNodeWithText("Episodes").performClick()
// and then navigates to the For you destination
composeTestRule.onNodeWithText("For you").performClick()
// WHEN the user uses the system button/gesture to go back
Espresso.pressBack()
// THEN the app quits
}
/*
* When pressing back from any top level destination except "For you", the app navigates back
* to the "For you" destination, no matter which destinations you visited in between.
*/
@Test
fun backFromDestinationReturnsToForYou() {
// GIVEN the user navigated to the Episodes destination
composeTestRule.onNodeWithText("Episodes").performClick()
// and then navigated to the Topics destination
composeTestRule.onNodeWithText("Topics").performClick()
// WHEN the user uses the system button/gesture to go back,
Espresso.pressBack()
// THEN the app shows the For You destination
composeTestRule.forYouDestinationTopMatcher().assertExists()
}
/*
* Matches an element at the top of the For You destination. Should be updated when the
* destination is implemented.
*/
private fun ComposeTestRule.forYouDestinationTopMatcher() = onNodeWithText("FOR YOU")
/*
* Matches an element at the top of the Topics destination. Should be updated when the
* destination is implemented.
*/
private fun ComposeTestRule.topicsDestinationTopMatcher() = onNodeWithText("TOPICS")
}

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
<!--
~ Copyright 2021 Google LLC ~ Copyright 2021 Google LLC
~ ~
~ Licensed under the Apache License, Version 2.0 (the "License"); ~ Licensed under the Apache License, Version 2.0 (the "License");
@ -23,12 +22,11 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.NowInAndroid"> android:theme="@style/Theme.Nia">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:label="@string/app_name" android:label="@string/app_name">
>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -37,4 +35,4 @@
</activity> </activity>
</application> </application>
</manifest> </manifest>

@ -19,36 +19,17 @@ package com.google.samples.apps.nowinandroid
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.material.MaterialTheme import androidx.core.view.WindowCompat
import androidx.compose.material.Surface import com.google.samples.apps.nowinandroid.ui.NiaApp
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.google.samples.apps.nowinandroid.ui.theme.NowInAndroidTheme
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent {
NowInAndroidTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
Greeting("Android")
}
}
}
}
}
@Composable // Turn off the decor fitting system windows, which allows us to handle insets,
fun Greeting(name: String) { // including IME animations
Text(text = "Hello $name!") WindowCompat.setDecorFitsSystemWindows(window, false)
}
@Preview(showBackground = true) setContent { NiaApp() }
@Composable
fun DefaultPreview() {
NowInAndroidTheme {
Greeting("Android")
} }
} }

@ -0,0 +1,159 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AutoStories
import androidx.compose.material.icons.filled.Bookmarks
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Upcoming
import androidx.compose.material.icons.outlined.AutoStories
import androidx.compose.material.icons.outlined.Bookmarks
import androidx.compose.material.icons.outlined.FavoriteBorder
import androidx.compose.material.icons.outlined.Upcoming
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.google.accompanist.insets.ProvideWindowInsets
import com.google.accompanist.insets.systemBarsPadding
import com.google.samples.apps.nowinandroid.R
import com.google.samples.apps.nowinandroid.ui.theme.NiaTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NiaApp() {
NiaTheme {
ProvideWindowInsets {
val navController = rememberNavController()
val navigationActions = remember(navController) {
NiaNavigationActions(navController)
}
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute =
navBackStackEntry?.destination?.route ?: NiaDestinations.FOR_YOU_ROUTE
Scaffold(
modifier = Modifier,
bottomBar = {
// TODO: Only show on small screens
NiABottomBar(navigationActions, currentRoute)
},
) { padding ->
Surface(Modifier.fillMaxSize()) {
NiaNavGraph(
navController = navController,
modifier = Modifier
.systemBarsPadding(bottom = false)
.padding(padding)
)
}
}
}
}
}
@Composable
private fun NiABottomBar(
navigationActions: NiaNavigationActions,
currentRoute: String
) {
// Wrap the navigation bar in a surface so the color behind the system
// navigation is equal to the container color of the navigation bar.
Surface(color = MaterialTheme.colorScheme.surface) {
NavigationBar(
modifier = Modifier.systemBarsPadding(top = false),
tonalElevation = 0.dp
) {
TOP_LEVEL_DESTINATIONS.forEach { dst ->
val selected = currentRoute == dst.route
NavigationBarItem(
selected = selected,
onClick = {
navigationActions.navigateToTopLevelDestination(dst.route)
},
icon = {
Icon(
if (selected) dst.selectedIcon else dst.unselectedIcon,
contentDescription = null
)
},
label = { Text(stringResource(dst.iconTextId)) }
)
}
}
}
}
private sealed class Destination(
val route: String,
val selectedIcon: ImageVector,
val unselectedIcon: ImageVector,
@StringRes val iconTextId: Int
) {
object ForYou : Destination(
route = NiaDestinations.FOR_YOU_ROUTE,
selectedIcon = Icons.Filled.Upcoming,
unselectedIcon = Icons.Outlined.Upcoming,
iconTextId = R.string.for_you
)
object Episodes : Destination(
route = NiaDestinations.EPISODES_ROUTE,
selectedIcon = Icons.Filled.AutoStories,
unselectedIcon = Icons.Outlined.AutoStories,
iconTextId = R.string.episodes
)
object Saved : Destination(
route = NiaDestinations.SAVED_ROUTE,
selectedIcon = Icons.Filled.Bookmarks,
unselectedIcon = Icons.Outlined.Bookmarks,
iconTextId = R.string.saved
)
object Topics : Destination(
route = NiaDestinations.TOPICS_ROUTE,
selectedIcon = Icons.Filled.Favorite,
unselectedIcon = Icons.Outlined.FavoriteBorder,
iconTextId = R.string.topics
)
}
private val TOP_LEVEL_DESTINATIONS = listOf(
Destination.ForYou,
Destination.Episodes,
Destination.Saved,
Destination.Topics
)

@ -0,0 +1,57 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
/**
* Top-level navigation graph. Navigation is organized as explained at
* https://d.android.com/jetpack/compose/nav-adaptive
*
* The navigation graph defined in this file defines the different top level routes. Navigation
* within each route is handled using state and Back Handlers.
*/
@Composable
fun NiaNavGraph(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
startDestination: String = NiaDestinations.FOR_YOU_ROUTE
) {
NavHost(
navController = navController,
startDestination = startDestination,
) {
composable(NiaDestinations.FOR_YOU_ROUTE) {
Text("FOR YOU", modifier)
}
composable(NiaDestinations.EPISODES_ROUTE) {
Text("EPISODES", modifier)
}
composable(NiaDestinations.SAVED_ROUTE) {
Text("SAVED", modifier)
}
composable(NiaDestinations.TOPICS_ROUTE) {
Text("TOPICS", modifier)
}
}
}

@ -0,0 +1,43 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui
import androidx.navigation.NavHostController
/**
* Routes for the different destinations in the application. Each of these destinations can contain
* one or more screens (based on the window size). Navigation from one screen to the next within a
* single destination will be handled directly in Compose, not using the Navigation component.
*/
object NiaDestinations {
const val FOR_YOU_ROUTE = "for_you"
const val EPISODES_ROUTE = "episodes"
const val SAVED_ROUTE = "saved"
const val TOPICS_ROUTE = "topics"
}
/**
* Models the navigation actions in the app.
*/
class NiaNavigationActions(private val navController: NavHostController) {
fun navigateToTopLevelDestination(route: String) {
navController.navigate(route) {
launchSingleTop = true
navController.graph.startDestinationRoute?.let { popUpTo(it) }
}
}
}

@ -1,27 +0,0 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(4.dp),
large = RoundedCornerShape(0.dp)
)

@ -16,48 +16,44 @@
package com.google.samples.apps.nowinandroid.ui.theme package com.google.samples.apps.nowinandroid.ui.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material.darkColors import androidx.compose.material3.darkColorScheme
import androidx.compose.material.lightColors import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
private val DarkColorPalette = darkColors( private val DarkColorScheme = darkColorScheme(
primary = Purple200, primary = Purple200,
primaryVariant = Purple700,
secondary = Teal200 secondary = Teal200
) )
private val LightColorPalette = lightColors( private val LightColorScheme = lightColorScheme(
primary = Purple500, primary = Purple500,
primaryVariant = Purple700,
secondary = Teal200 secondary = Teal200
/* Other default colors to override
background = Color.White,
surface = Color.White,
onPrimary = Color.White,
onSecondary = Color.Black,
onBackground = Color.Black,
onSurface = Color.Black,
*/
) )
@Composable @Composable
fun NowInAndroidTheme( fun NiaTheme(
darkTheme: Boolean = isSystemInDarkTheme(), darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable() () -> Unit content: @Composable() () -> Unit
) { ) {
val colors = if (darkTheme) { val colorScheme = when {
DarkColorPalette dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
} else { val context = LocalContext.current
LightColorPalette if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
} }
MaterialTheme( MaterialTheme(
colors = colors, colorScheme = colorScheme,
typography = Typography, typography = NiaTypography,
shapes = Shapes,
content = content content = content
) )
} }

@ -16,29 +16,6 @@
package com.google.samples.apps.nowinandroid.ui.theme package com.google.samples.apps.nowinandroid.ui.theme
import androidx.compose.material.Typography import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with val NiaTypography = Typography()
val Typography = Typography(
body1 = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
)
/* Other default text styles to override
button = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.W500,
fontSize = 14.sp
),
caption = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 12.sp
)
*/
)

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
<!--
~ Copyright 2021 Google LLC ~ Copyright 2021 Google LLC
~ ~
~ Licensed under the Apache License, Version 2.0 (the "License"); ~ Licensed under the Apache License, Version 2.0 (the "License");
@ -14,11 +13,13 @@
~ See the License for the specific language governing permissions and ~ See the License for the specific language governing permissions and
~ limitations under the License. ~ limitations under the License.
--> -->
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<!-- Base application theme. -->
<style name="Theme.NowInAndroid" parent="@android:style/Theme.Material"> <!-- Our dark theme -->
<item name="android:statusBarColor">@android:color/transparent</item> <style name="Theme.Nia" parent="Platform.Theme.Nia">
<item name="android:navigationBarColor">@android:color/transparent</item> <item name="colorPrimary">@color/purple_200</item>
<!-- Customize your theme here. --> <item name="colorPrimaryDark">@color/purple_700</item>
<item name="colorAccent">@color/teal_200</item>
</style> </style>
</resources>
</resources>

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2021 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<style name="Platform.Theme.Nia" parent="Theme.Material3.DayNight">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
</style>
</resources>

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2021 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<style name="Platform.Theme.Nia" parent="Theme.Material3.DayNight">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowLightNavigationBar">?attr/isLightTheme</item>
</style>
</resources>

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
<!--
~ Copyright 2021 Google LLC ~ Copyright 2021 Google LLC
~ ~
~ Licensed under the Apache License, Version 2.0 (the "License"); ~ Licensed under the Apache License, Version 2.0 (the "License");
@ -20,6 +19,7 @@
<color name="purple_700">#FF3700B3</color> <color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color> <color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color> <color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color> <!-- Status bar -->
</resources> <color name="black30">#4D000000</color>
</resources>

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
<!--
~ Copyright 2021 Google LLC ~ Copyright 2021 Google LLC
~ ~
~ Licensed under the Apache License, Version 2.0 (the "License"); ~ Licensed under the Apache License, Version 2.0 (the "License");
@ -16,4 +15,8 @@
--> -->
<resources> <resources>
<string name="app_name">Now in Android</string> <string name="app_name">Now in Android</string>
</resources> <string name="for_you">For you</string>
<string name="episodes">Episodes</string>
<string name="saved">Saved</string>
<string name="topics">Topics</string>
</resources>

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
<!--
~ Copyright 2021 Google LLC ~ Copyright 2021 Google LLC
~ ~
~ Licensed under the Apache License, Version 2.0 (the "License"); ~ Licensed under the Apache License, Version 2.0 (the "License");
@ -14,11 +13,29 @@
~ See the License for the specific language governing permissions and ~ See the License for the specific language governing permissions and
~ limitations under the License. ~ limitations under the License.
--> -->
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<!-- Base application theme. -->
<style name="Theme.NowInAndroid" parent="@android:style/Theme.Material.Light"> <!-- Allows us to override platform level specific attributes in their
<!-- Customize your theme here. --> respective values-vXX folder. -->
<item name="android:statusBarColor">@android:color/transparent</item> <style name="Platform.Theme.Nia" parent="Theme.Material3.DayNight">
<item name="android:navigationBarColor">@android:color/transparent</item> <item name="android:statusBarColor">@color/black30</item>
</style> </style>
</resources>
<!-- The actual theme we use. This varies for light theme (here),
and values-night for dark theme. -->
<!-- TODO Change colors here and in values-night when implementing M3 theme -->
<style name="Theme.Nia" parent="Platform.Theme.Nia">
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryDark">@color/purple_700</item>
<item name="colorAccent">@color/teal_200</item>
</style>
<style name="Theme.Nia.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="Theme.Nia.AppBarOverlay" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar" />
<style name="Theme.Nia.PopupOverlay" parent="ThemeOverlay.MaterialComponents.Light" />
</resources>

@ -9,7 +9,7 @@ androidxComposeMaterial3 = "1.0.0-alpha01"
androidxCore = "1.7.0" androidxCore = "1.7.0"
androidxEspresso = "3.3.0" androidxEspresso = "3.3.0"
androidxLifecycle = "2.4.0" androidxLifecycle = "2.4.0"
androidxNavigation = "2.3.5" androidxNavigation = "2.4.0-rc01"
androidxTest = "1.4.0" androidxTest = "1.4.0"
androidxTestExt = "1.1.2" androidxTestExt = "1.1.2"
jacoco = "0.8.7" jacoco = "0.8.7"
@ -32,6 +32,7 @@ androidx-activity-compose = { group = "androidx.activity", name = "activity-comp
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" }
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "androidxCompose" } androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "androidxCompose" }
androidx-compose-material = { group = "androidx.compose.material", name = "material", version.ref = "androidxCompose" } androidx-compose-material = { group = "androidx.compose.material", name = "material", version.ref = "androidxCompose" }
androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "androidxCompose" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "androidxComposeMaterial3" } androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "androidxComposeMaterial3" }
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "androidxCompose" } androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "androidxCompose" }
androidx-compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "androidxCompose" } androidx-compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "androidxCompose" }
@ -41,12 +42,11 @@ androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-toolin
androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util", version.ref = "androidxCompose" } androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util", version.ref = "androidxCompose" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" }
androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" }
androidx-navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "androidxNavigation" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" }
androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "androidxNavigation" }
androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTest" } androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTest" }
androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidxTest" }
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidxTestExt" }
androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidxEspresso" } androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidxEspresso" }
androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidxTest" }
androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidxTest" }
junit4 = { group = "junit", name = "junit", version.ref = "junit4" } junit4 = { group = "junit", name = "junit", version.ref = "junit4" }
kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
kotlin-serializationPlugin = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" } kotlin-serializationPlugin = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" }

Loading…
Cancel
Save