More updates for device previews

adaptive-previews-device-classes
Eric Schmidt 9 months ago
parent 4be3bb9bb7
commit 04c66f7a72

@ -64,6 +64,10 @@ import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hasRoute import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavDestination.Companion.hierarchy
import com.google.samples.apps.nowinandroid.R import com.google.samples.apps.nowinandroid.R
import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaGradientBackground import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaGradientBackground
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationSuiteScaffold import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationSuiteScaffold
@ -71,9 +75,15 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopAp
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.GradientColors
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.model.data.UserNewsResource
import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews
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
import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.datetime.TimeZone
import kotlin.reflect.KClass import kotlin.reflect.KClass
import com.google.samples.apps.nowinandroid.feature.settings.R as settingsR import com.google.samples.apps.nowinandroid.feature.settings.R as settingsR
@ -281,3 +291,32 @@ private fun NavDestination?.isRouteInHierarchy(route: KClass<*>) =
this?.hierarchy?.any { this?.hierarchy?.any {
it.hasRoute(route) it.hasRoute(route)
} ?: false } ?: false
@DevicePreviews
@Composable
fun NiaAppPreview() {
NiaTheme {
NiaApp(
appState = rememberNiaAppState(
networkMonitor = object : NetworkMonitor {
override val isOnline: Flow<Boolean> = flowOf(true)
},
userNewsResourceRepository = object : UserNewsResourceRepository {
override fun observeAll(query: NewsResourceQuery): Flow<List<UserNewsResource>> =
flowOf(emptyList())
override fun observeAllForFollowedTopics(): Flow<List<UserNewsResource>> =
flowOf(emptyList())
override fun observeAllBookmarked(): Flow<List<UserNewsResource>> =
flowOf(emptyList())
},
timeZoneMonitor = object : TimeZoneMonitor {
override val currentTimeZone: Flow<TimeZone> =
flowOf(TimeZone.currentSystemDefault())
},
),
)
}
}

@ -35,7 +35,7 @@ dependencies {
testImplementation(libs.hilt.android.testing) testImplementation(libs.hilt.android.testing)
testImplementation(libs.robolectric) testImplementation(libs.robolectric)
testImplementation(projects.core.testing) testImplementation(projects.core.testing)
testDemoImplementation(projects.core.screenshotTesting) testImplementation(projects.core.screenshotTesting)
androidTestImplementation(libs.bundles.androidx.compose.ui.test) androidTestImplementation(libs.bundles.androidx.compose.ui.test)
androidTestImplementation(projects.core.testing) androidTestImplementation(projects.core.testing)

@ -16,7 +16,6 @@
package com.google.samples.apps.nowinandroid.feature.foryou package com.google.samples.apps.nowinandroid.feature.foryou
import android.net.Uri
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.Build.VERSION_CODES import android.os.Build.VERSION_CODES
import androidx.activity.compose.ReportDrawnWhen import androidx.activity.compose.ReportDrawnWhen
@ -103,31 +102,48 @@ import com.google.samples.apps.nowinandroid.core.ui.TrackScrollJank
import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider
import com.google.samples.apps.nowinandroid.core.ui.launchCustomChromeTab import com.google.samples.apps.nowinandroid.core.ui.launchCustomChromeTab
import com.google.samples.apps.nowinandroid.core.ui.newsFeed import com.google.samples.apps.nowinandroid.core.ui.newsFeed
import androidx.core.net.toUri
@Composable @Composable
internal fun ForYouScreen( internal fun ForYouScreen(
onTopicClick: (String) -> Unit, onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
viewModel: ForYouViewModel = hiltViewModel(), viewModel: ForYouViewModel? = if (LocalInspectionMode.current) null else hiltViewModel(),
) { ) {
val onboardingUiState by viewModel.onboardingUiState.collectAsStateWithLifecycle() if (viewModel != null) {
val feedState by viewModel.feedState.collectAsStateWithLifecycle() val onboardingUiState by viewModel.onboardingUiState.collectAsStateWithLifecycle()
val isSyncing by viewModel.isSyncing.collectAsStateWithLifecycle() val feedState by viewModel.feedState.collectAsStateWithLifecycle()
val deepLinkedUserNewsResource by viewModel.deepLinkedNewsResource.collectAsStateWithLifecycle() val isSyncing by viewModel.isSyncing.collectAsStateWithLifecycle()
val deepLinkedUserNewsResource by viewModel.deepLinkedNewsResource.collectAsStateWithLifecycle()
ForYouScreen( ForYouScreen(
isSyncing = isSyncing, isSyncing = isSyncing,
onboardingUiState = onboardingUiState, onboardingUiState = onboardingUiState,
feedState = feedState, feedState = feedState,
deepLinkedUserNewsResource = deepLinkedUserNewsResource, deepLinkedUserNewsResource = deepLinkedUserNewsResource,
onTopicCheckedChanged = viewModel::updateTopicSelection, onTopicCheckedChanged = viewModel::updateTopicSelection,
onDeepLinkOpened = viewModel::onDeepLinkOpened, onDeepLinkOpened = viewModel::onDeepLinkOpened,
onTopicClick = onTopicClick, onTopicClick = onTopicClick,
saveFollowedTopics = viewModel::dismissOnboarding, saveFollowedTopics = viewModel::dismissOnboarding,
onNewsResourcesCheckedChanged = viewModel::updateNewsResourceSaved, onNewsResourcesCheckedChanged = viewModel::updateNewsResourceSaved,
onNewsResourceViewed = { viewModel.setNewsResourceViewed(it, true) }, onNewsResourceViewed = { viewModel.setNewsResourceViewed(it, true) },
modifier = modifier, modifier = modifier,
) )
} else {
ForYouScreen(
isSyncing = false,
onboardingUiState = OnboardingUiState.NotShown,
feedState = NewsFeedUiState.Success(emptyList()),
deepLinkedUserNewsResource = null,
onTopicCheckedChanged = { _, _ -> },
onDeepLinkOpened = { },
onTopicClick = onTopicClick,
saveFollowedTopics = { },
onNewsResourcesCheckedChanged = { _, _ -> },
onNewsResourceViewed = { },
modifier = modifier,
)
}
} }
@Composable @Composable
@ -184,7 +200,7 @@ internal fun ForYouScreen(
), ),
) )
layout(placeable.width, placeable.height) { layout(placeable.width, placeable.height) {
placeable.place(0, 0) placeable.place(-16.dp.roundToPx(), 0)
} }
}, },
) )
@ -300,11 +316,12 @@ private fun LazyStaggeredGridScope.onboarding(
enabled = onboardingUiState.isDismissable, enabled = onboardingUiState.isDismissable,
modifier = Modifier modifier = Modifier
.padding(horizontal = 24.dp) .padding(horizontal = 24.dp)
.widthIn(364.dp) .widthIn(max = 320.dp)
.fillMaxWidth(), .fillMaxWidth(),
) { ) {
Text( Text(
text = stringResource(R.string.feature_foryou_done), text = stringResource(R.string.feature_foryou_done),
color = if (onboardingUiState.isDismissable) androidx.compose.ui.graphics.Color.Unspecified else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f),
) )
} }
} }
@ -475,7 +492,7 @@ private fun DeepLinkEffect(
launchCustomChromeTab( launchCustomChromeTab(
context = context, context = context,
uri = Uri.parse(userNewsResource.url), uri = userNewsResource.url.toUri(),
toolbarColor = backgroundColor, toolbarColor = backgroundColor,
) )
} }
@ -617,3 +634,24 @@ fun ForYouScreenPopulatedAndLoading(
) )
} }
} }
@DevicePreviews
@Composable
fun ForYouScreenNotPopulatedOnboarding() {
NiaTheme {
ForYouScreen(
isSyncing = true,
onboardingUiState = OnboardingUiState.Shown(
topics = emptyList(),
),
feedState = NewsFeedUiState.Loading,
deepLinkedUserNewsResource = null,
onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
onNewsResourceViewed = {},
onTopicClick = {},
onDeepLinkOpened = {},
)
}
}
Loading…
Cancel
Save