Skip to content

Commit 6c4dbc5

Browse files
authored
Merge branch 'main' into gradle-actions
2 parents f22b543 + 3141de0 commit 6c4dbc5

File tree

49 files changed

+2863
-3425
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2863
-3425
lines changed

.github/pull_request_template.md

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
1-
_Thanks for submitting a pull request. Please include the following information._
1+
**DO NOT CREATE A PULL REQUEST WITHOUT READING THESE INSTRUCTIONS**
22

3-
**What I have done and why**
3+
## Instructions
4+
Thanks for submitting a pull request. To accept your pull request we need you do a few things:
5+
6+
**If this is your first pull request**
47

5-
_Include a summary of what your pull request contains, and why you have made these changes._
8+
- [Sign the contributors license agreement](https://cla.developers.google.com/)
69

7-
Fixes #<issue_number_goes_here>
10+
**Ensure tests pass and code is formatted correctly**
811

9-
**How I'm testing it**
12+
- Run local tests on the `DemoDebug` variant by running `./gradlew testDemoDebug`
13+
- Fix code formatting: `./gradlew --init-script gradle/init.gradle.kts spotlessApply`
1014

11-
_Choose at least one:_
12-
- Unit tests
13-
- UI tests
14-
- Screenshot tests
15-
- N/A _(provide justification)_
15+
**Add a description**
1616

17-
**Do tests pass?**
18-
- [ ] Run local tests on `DemoDebug` variant: `./gradlew testDemoDebug`
19-
- [ ] Check formatting: `./gradlew --init-script gradle/init.gradle.kts spotlessApply`
17+
We need to know what you've done and why you've done it. Include a summary of what your pull request contains, and why you have made these changes. Include links to any relevant issues which it fixes.
2018

21-
**Is this your first pull request?**
22-
- [ ] [Sign the CLA](https://cla.developers.google.com/)
23-
- [ ] Run `./tools/setup.sh`
24-
- [ ] Import the code formatting style as explained in [the setup script](/tools/setup.sh#L40).
19+
[Here's an example](https://github.com/android/nowinandroid/pull/1257).
2520

21+
**NOW DELETE THIS LINE AND EVERYTHING ABOVE IT**
22+
23+
**What I have done and why**
2624

25+
\<add your PR description here\>

.github/workflows/Build.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,13 @@ jobs:
128128
name: local-test-results
129129
path: '**/build/test-results/test*UnitTest/**.xml'
130130

131+
- name: Upload screenshot results (PNG)
132+
if: always()
133+
uses: actions/upload-artifact@v4
134+
with:
135+
name: screenshot-test-results
136+
path: '**/build/outputs/roborazzi/*_compare.png'
137+
131138
- name: Check lint
132139
run: ./gradlew :app:lintProdRelease :app-nia-catalog:lintRelease :lint:lint
133140

.github/workflows/Release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jobs:
1919
ls /dev/kvm
2020
2121
- name: Checkout
22+
2223
uses: actions/checkout@v4
2324

2425
- name: Copy CI gradle.properties

app-nia-catalog/dependencies/releaseRuntimeClasspath.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
androidx.activity:activity-compose:1.8.0
2-
androidx.activity:activity-ktx:1.8.0
3-
androidx.activity:activity:1.8.0
1+
androidx.activity:activity-compose:1.8.2
2+
androidx.activity:activity-ktx:1.8.2
3+
androidx.activity:activity:1.8.2
44
androidx.annotation:annotation-experimental:1.4.0
55
androidx.annotation:annotation-jvm:1.7.1
66
androidx.annotation:annotation:1.7.1

app/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,10 @@ dependencies {
113113

114114
testImplementation(projects.core.dataTest)
115115
testImplementation(projects.core.testing)
116+
testImplementation(projects.sync.syncTest)
116117
testImplementation(libs.androidx.compose.ui.test)
118+
testImplementation(libs.androidx.work.testing)
117119
testImplementation(libs.hilt.android.testing)
118-
testImplementation(libs.work.testing)
119120

120121
testDemoImplementation(libs.robolectric)
121122
testDemoImplementation(libs.roborazzi)

app/dependencies/prodReleaseRuntimeClasspath.txt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
androidx.activity:activity-compose:1.8.0
2-
androidx.activity:activity-ktx:1.8.0
3-
androidx.activity:activity:1.8.0
1+
androidx.activity:activity-compose:1.8.2
2+
androidx.activity:activity-ktx:1.8.2
3+
androidx.activity:activity:1.8.2
44
androidx.annotation:annotation-experimental:1.4.0
55
androidx.annotation:annotation-jvm:1.8.0-beta01
66
androidx.annotation:annotation:1.8.0-beta01
@@ -17,10 +17,10 @@ androidx.compose.animation:animation-android:1.7.0-alpha06
1717
androidx.compose.animation:animation-core-android:1.7.0-alpha06
1818
androidx.compose.animation:animation-core:1.7.0-alpha06
1919
androidx.compose.animation:animation:1.7.0-alpha06
20-
androidx.compose.foundation:foundation-android:1.6.3
21-
androidx.compose.foundation:foundation-layout-android:1.6.3
22-
androidx.compose.foundation:foundation-layout:1.6.3
23-
androidx.compose.foundation:foundation:1.6.3
20+
androidx.compose.foundation:foundation-android:1.7.0-alpha06
21+
androidx.compose.foundation:foundation-layout-android:1.7.0-alpha06
22+
androidx.compose.foundation:foundation-layout:1.7.0-alpha06
23+
androidx.compose.foundation:foundation:1.7.0-alpha06
2424
androidx.compose.material3.adaptive:adaptive-android:1.0.0-alpha10
2525
androidx.compose.material3.adaptive:adaptive-layout-android:1.0.0-alpha10
2626
androidx.compose.material3.adaptive:adaptive-layout:1.0.0-alpha10
@@ -103,11 +103,11 @@ androidx.lifecycle:lifecycle-viewmodel:2.8.0-alpha04
103103
androidx.loader:loader:1.0.0
104104
androidx.localbroadcastmanager:localbroadcastmanager:1.0.0
105105
androidx.metrics:metrics-performance:1.0.0-alpha04
106-
androidx.navigation:navigation-common-ktx:2.7.4
107-
androidx.navigation:navigation-common:2.7.4
108-
androidx.navigation:navigation-compose:2.7.4
109-
androidx.navigation:navigation-runtime-ktx:2.7.4
110-
androidx.navigation:navigation-runtime:2.7.4
106+
androidx.navigation:navigation-common-ktx:2.8.0-alpha06
107+
androidx.navigation:navigation-common:2.8.0-alpha06
108+
androidx.navigation:navigation-compose:2.8.0-alpha06
109+
androidx.navigation:navigation-runtime-ktx:2.8.0-alpha06
110+
androidx.navigation:navigation-runtime:2.8.0-alpha06
111111
androidx.print:print:1.0.0
112112
androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05
113113
androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05
@@ -188,8 +188,8 @@ com.google.guava:failureaccess:1.0.1
188188
com.google.guava:guava:31.1-android
189189
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
190190
com.google.j2objc:j2objc-annotations:1.3
191-
com.google.protobuf:protobuf-javalite:3.25.2
192-
com.google.protobuf:protobuf-kotlin-lite:3.25.2
191+
com.google.protobuf:protobuf-javalite:4.26.0
192+
com.google.protobuf:protobuf-kotlin-lite:4.26.0
193193
com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0
194194
com.squareup.okhttp3:logging-interceptor:4.12.0
195195
com.squareup.okhttp3:okhttp:4.12.0

app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.google.samples.apps.nowinandroid.ui
1818

19+
import androidx.compose.foundation.layout.Box
1920
import androidx.compose.foundation.layout.Column
2021
import androidx.compose.foundation.layout.Row
2122
import androidx.compose.foundation.layout.WindowInsets
@@ -199,23 +200,26 @@ internal fun NiaApp(
199200
)
200201
}
201202

202-
NiaNavHost(
203-
appState = appState,
204-
onShowSnackbar = { message, action ->
205-
snackbarHostState.showSnackbar(
206-
message = message,
207-
actionLabel = action,
208-
duration = Short,
209-
) == ActionPerformed
210-
},
203+
Box(
211204
modifier = if (shouldShowTopAppBar) {
212205
Modifier.consumeWindowInsets(
213206
WindowInsets.safeDrawing.only(WindowInsetsSides.Top),
214207
)
215208
} else {
216209
Modifier
217210
},
218-
)
211+
) {
212+
NiaNavHost(
213+
appState = appState,
214+
onShowSnackbar = { message, action ->
215+
snackbarHostState.showSnackbar(
216+
message = message,
217+
actionLabel = action,
218+
duration = Short,
219+
) == ActionPerformed
220+
},
221+
)
222+
}
219223
}
220224

221225
// TODO: We may want to add padding or spacer when the snackbar is shown so that

app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/Interests2PaneViewModel.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import javax.inject.Inject
2727
class Interests2PaneViewModel @Inject constructor(
2828
private val savedStateHandle: SavedStateHandle,
2929
) : ViewModel() {
30-
val selectedTopicId: StateFlow<String?> = savedStateHandle.getStateFlow(TOPIC_ID_ARG, null)
30+
val selectedTopicId: StateFlow<String?> =
31+
savedStateHandle.getStateFlow(TOPIC_ID_ARG, savedStateHandle[TOPIC_ID_ARG])
3132

3233
fun onTopicClick(topicId: String?) {
3334
savedStateHandle[TOPIC_ID_ARG] = topicId

app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt

Lines changed: 61 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,21 @@ package com.google.samples.apps.nowinandroid.ui.interests2pane
1818

1919
import androidx.activity.compose.BackHandler
2020
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
21+
import androidx.compose.material3.adaptive.layout.AnimatedPane
2122
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
2223
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
2324
import androidx.compose.material3.adaptive.layout.PaneAdaptedValue
25+
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem
2426
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
2527
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
2628
import androidx.compose.runtime.Composable
27-
import androidx.compose.runtime.LaunchedEffect
2829
import androidx.compose.runtime.getValue
30+
import androidx.compose.runtime.key
31+
import androidx.compose.runtime.mutableStateOf
32+
import androidx.compose.runtime.remember
33+
import androidx.compose.runtime.saveable.Saver
34+
import androidx.compose.runtime.saveable.rememberSaveable
35+
import androidx.compose.runtime.setValue
2936
import androidx.hilt.navigation.compose.hiltViewModel
3037
import androidx.lifecycle.compose.collectAsStateWithLifecycle
3138
import androidx.navigation.NavGraphBuilder
@@ -39,8 +46,10 @@ import com.google.samples.apps.nowinandroid.feature.interests.navigation.INTERES
3946
import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_ARG
4047
import com.google.samples.apps.nowinandroid.feature.topic.TopicDetailPlaceholder
4148
import com.google.samples.apps.nowinandroid.feature.topic.navigation.TOPIC_ROUTE
49+
import com.google.samples.apps.nowinandroid.feature.topic.navigation.createTopicRoute
4250
import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic
4351
import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicScreen
52+
import java.util.UUID
4453

4554
private const val DETAIL_PANE_NAVHOST_ROUTE = "detail_pane_route"
4655

@@ -76,17 +85,42 @@ internal fun InterestsListDetailScreen(
7685
selectedTopicId: String?,
7786
onTopicClick: (String) -> Unit,
7887
) {
79-
val listDetailNavigator = rememberListDetailPaneScaffoldNavigator()
88+
val listDetailNavigator = rememberListDetailPaneScaffoldNavigator(
89+
initialDestinationHistory = listOfNotNull(
90+
ThreePaneScaffoldDestinationItem(ListDetailPaneScaffoldRole.List),
91+
ThreePaneScaffoldDestinationItem<Nothing>(ListDetailPaneScaffoldRole.Detail).takeIf {
92+
selectedTopicId != null
93+
},
94+
),
95+
)
8096
BackHandler(listDetailNavigator.canNavigateBack()) {
8197
listDetailNavigator.navigateBack()
8298
}
8399

84-
val nestedNavController = rememberNavController()
100+
var nestedNavHostStartDestination by remember {
101+
mutableStateOf(selectedTopicId?.let(::createTopicRoute) ?: TOPIC_ROUTE)
102+
}
103+
var nestedNavKey by rememberSaveable(
104+
stateSaver = Saver({ it.toString() }, UUID::fromString),
105+
) {
106+
mutableStateOf(UUID.randomUUID())
107+
}
108+
val nestedNavController = key(nestedNavKey) {
109+
rememberNavController()
110+
}
85111

86112
fun onTopicClickShowDetailPane(topicId: String) {
87113
onTopicClick(topicId)
88-
nestedNavController.navigateToTopic(topicId) {
89-
popUpTo(DETAIL_PANE_NAVHOST_ROUTE)
114+
if (listDetailNavigator.isDetailPaneVisible()) {
115+
// If the detail pane was visible, then use the nestedNavController navigate call
116+
// directly
117+
nestedNavController.navigateToTopic(topicId) {
118+
popUpTo(DETAIL_PANE_NAVHOST_ROUTE)
119+
}
120+
} else {
121+
// Otherwise, recreate the NavHost entirely, and start at the new destination
122+
nestedNavHostStartDestination = createTopicRoute(topicId)
123+
nestedNavKey = UUID.randomUUID()
90124
}
91125
listDetailNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
92126
}
@@ -95,34 +129,34 @@ internal fun InterestsListDetailScreen(
95129
value = listDetailNavigator.scaffoldValue,
96130
directive = listDetailNavigator.scaffoldDirective,
97131
listPane = {
98-
InterestsRoute(
99-
onTopicClick = ::onTopicClickShowDetailPane,
100-
highlightSelectedTopic = listDetailNavigator.isDetailPaneVisible(),
101-
)
102-
},
103-
detailPane = {
104-
NavHost(
105-
navController = nestedNavController,
106-
startDestination = TOPIC_ROUTE,
107-
route = DETAIL_PANE_NAVHOST_ROUTE,
108-
) {
109-
topicScreen(
110-
showBackButton = !listDetailNavigator.isListPaneVisible(),
111-
onBackClick = listDetailNavigator::navigateBack,
132+
AnimatedPane {
133+
InterestsRoute(
112134
onTopicClick = ::onTopicClickShowDetailPane,
135+
highlightSelectedTopic = listDetailNavigator.isDetailPaneVisible(),
113136
)
114-
composable(route = TOPIC_ROUTE) {
115-
TopicDetailPlaceholder()
137+
}
138+
},
139+
detailPane = {
140+
AnimatedPane {
141+
key(nestedNavKey) {
142+
NavHost(
143+
navController = nestedNavController,
144+
startDestination = nestedNavHostStartDestination,
145+
route = DETAIL_PANE_NAVHOST_ROUTE,
146+
) {
147+
topicScreen(
148+
showBackButton = !listDetailNavigator.isListPaneVisible(),
149+
onBackClick = listDetailNavigator::navigateBack,
150+
onTopicClick = ::onTopicClickShowDetailPane,
151+
)
152+
composable(route = TOPIC_ROUTE) {
153+
TopicDetailPlaceholder()
154+
}
155+
}
116156
}
117157
}
118158
},
119159
)
120-
LaunchedEffect(Unit) {
121-
if (selectedTopicId != null) {
122-
// Initial topic ID was provided when navigating to Interests, so show its details.
123-
onTopicClickShowDetailPane(selectedTopicId)
124-
}
125-
}
126160
}
127161

128162
@OptIn(ExperimentalMaterial3AdaptiveApi::class)

app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package com.google.samples.apps.nowinandroid.ui
1818

19-
import android.util.Log
2019
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
2120
import androidx.compose.material3.windowsizeclass.WindowSizeClass
2221
import androidx.compose.runtime.CompositionLocalProvider
@@ -28,10 +27,6 @@ import androidx.compose.ui.test.onRoot
2827
import androidx.compose.ui.unit.Dp
2928
import androidx.compose.ui.unit.DpSize
3029
import androidx.compose.ui.unit.dp
31-
import androidx.test.platform.app.InstrumentationRegistry
32-
import androidx.work.Configuration
33-
import androidx.work.testing.SynchronousExecutor
34-
import androidx.work.testing.WorkManagerTestInitHelper
3530
import com.github.takahirom.roborazzi.captureRoboImage
3631
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
3732
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
@@ -109,17 +104,6 @@ class NiaAppScreenSizesScreenshotTests {
109104

110105
@Before
111106
fun setup() {
112-
val config = Configuration.Builder()
113-
.setMinimumLoggingLevel(Log.DEBUG)
114-
.setExecutor(SynchronousExecutor())
115-
.build()
116-
117-
// Initialize WorkManager for instrumentation tests.
118-
WorkManagerTestInitHelper.initializeTestWorkManager(
119-
InstrumentationRegistry.getInstrumentation().context,
120-
config,
121-
)
122-
123107
hiltRule.inject()
124108

125109
// Configure user data

0 commit comments

Comments
 (0)