diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt index bfaa27fa62..7df8fc5d0a 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt @@ -74,7 +74,8 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.GradientColo import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors import com.google.samples.apps.nowinandroid.core.navigation.Navigator import com.google.samples.apps.nowinandroid.core.navigation.toEntries -import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.navigation.LocalSnackbarHostState +import com.google.samples.apps.nowinandroid.core.ui.LocalIsOffline +import com.google.samples.apps.nowinandroid.core.ui.LocalSnackbarHostState import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.navigation.bookmarksEntry import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouNavKey import com.google.samples.apps.nowinandroid.feature.foryou.impl.navigation.forYouEntry @@ -117,7 +118,10 @@ fun NiaApp( ) } } - CompositionLocalProvider(LocalSnackbarHostState provides snackbarHostState) { + CompositionLocalProvider( + LocalSnackbarHostState provides snackbarHostState, + LocalIsOffline provides isOffline, + ) { NiaApp( appState = appState, diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt index 3c76101933..6452a7eec6 100644 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt @@ -67,7 +67,7 @@ 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.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions -import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.navigation.LocalSnackbarHostState +import com.google.samples.apps.nowinandroid.core.ui.LocalSnackbarHostState import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt index 75dc6baa74..a12a6cca7a 100644 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt @@ -40,7 +40,7 @@ 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.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions -import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.navigation.LocalSnackbarHostState +import com.google.samples.apps.nowinandroid.core.ui.LocalSnackbarHostState import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalIsOffline.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalIsOffline.kt new file mode 100644 index 0000000000..a055e78864 --- /dev/null +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalIsOffline.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2026 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.core.ui + +import androidx.compose.runtime.compositionLocalOf + +val LocalIsOffline = compositionLocalOf { false } diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalSnackbarHostState.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalSnackbarHostState.kt new file mode 100644 index 0000000000..9487fb97f9 --- /dev/null +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalSnackbarHostState.kt @@ -0,0 +1,24 @@ +/* + * 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.core.ui + +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.compositionLocalOf + +val LocalSnackbarHostState = compositionLocalOf { + error("SnackbarHostState should be initialized at runtime") +} diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt index 0b7b9f570d..37d4d5a3ac 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt @@ -18,6 +18,7 @@ package com.google.samples.apps.nowinandroid.core.ui import android.content.Context import android.net.Uri +import android.widget.Toast import androidx.annotation.ColorInt import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabsIntent @@ -62,18 +63,26 @@ fun LazyStaggeredGridScope.newsFeed( val context = LocalContext.current val analyticsHelper = LocalAnalyticsHelper.current val backgroundColor = MaterialTheme.colorScheme.background.toArgb() + val isOffline = LocalIsOffline.current NewsResourceCardExpanded( userNewsResource = userNewsResource, isBookmarked = userNewsResource.isSaved, onClick = { - onExpandedCardClick() - analyticsHelper.logNewsResourceOpened( - newsResourceId = userNewsResource.id, - ) - launchCustomChromeTab(context, Uri.parse(userNewsResource.url), backgroundColor) - - onNewsResourceViewed(userNewsResource.id) + if (isOffline) { + Toast.makeText( + context, + context.getString(R.string.core_ui_not_connected), + Toast.LENGTH_SHORT, + ).show() + } else { + onExpandedCardClick() + analyticsHelper.logNewsResourceOpened( + newsResourceId = userNewsResource.id, + ) + launchCustomChromeTab(context, Uri.parse(userNewsResource.url), backgroundColor) + onNewsResourceViewed(userNewsResource.id) + } }, hasBeenViewed = userNewsResource.hasBeenViewed, onToggleBookmark = { diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardList.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardList.kt index ea1c09d013..0609614208 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardList.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardList.kt @@ -17,6 +17,7 @@ package com.google.samples.apps.nowinandroid.core.ui import android.net.Uri +import android.widget.Toast import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.items import androidx.compose.material3.MaterialTheme @@ -47,6 +48,7 @@ fun LazyListScope.userNewsResourceCardItems( val backgroundColor = MaterialTheme.colorScheme.background.toArgb() val context = LocalContext.current val analyticsHelper = LocalAnalyticsHelper.current + val isOffline = LocalIsOffline.current NewsResourceCardExpanded( userNewsResource = userNewsResource, @@ -54,11 +56,19 @@ fun LazyListScope.userNewsResourceCardItems( hasBeenViewed = userNewsResource.hasBeenViewed, onToggleBookmark = { onToggleBookmark(userNewsResource) }, onClick = { - analyticsHelper.logNewsResourceOpened( - newsResourceId = userNewsResource.id, - ) - launchCustomChromeTab(context, resourceUrl, backgroundColor) - onNewsResourceViewed(userNewsResource.id) + if (isOffline) { + Toast.makeText( + context, + context.getString(R.string.core_ui_not_connected), + Toast.LENGTH_SHORT, + ).show() + } else { + analyticsHelper.logNewsResourceOpened( + newsResourceId = userNewsResource.id, + ) + launchCustomChromeTab(context, resourceUrl, backgroundColor) + onNewsResourceViewed(userNewsResource.id) + } }, onTopicClick = onTopicClick, modifier = itemModifier, diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index a97746a9c4..33050c5a01 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -31,4 +31,5 @@ Unfollow interest Feed sharing %1$s: %2$s + Can\'t open, no internet connection diff --git a/feature/bookmarks/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/navigation/BookmarksEntryProvider.kt b/feature/bookmarks/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/navigation/BookmarksEntryProvider.kt index 4679656517..747d0ff5d1 100644 --- a/feature/bookmarks/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/navigation/BookmarksEntryProvider.kt +++ b/feature/bookmarks/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/navigation/BookmarksEntryProvider.kt @@ -17,12 +17,11 @@ package com.google.samples.apps.nowinandroid.feature.bookmarks.impl.navigation import androidx.compose.material3.SnackbarDuration.Short -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult.ActionPerformed -import androidx.compose.runtime.compositionLocalOf import androidx.navigation3.runtime.EntryProviderScope import androidx.navigation3.runtime.NavKey import com.google.samples.apps.nowinandroid.core.navigation.Navigator +import com.google.samples.apps.nowinandroid.core.ui.LocalSnackbarHostState import com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation.BookmarksNavKey import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.BookmarksScreen import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic @@ -42,8 +41,3 @@ fun EntryProviderScope.bookmarksEntry(navigator: Navigator) { ) } } - -// TODO: Why is this here? -val LocalSnackbarHostState = compositionLocalOf { - error("SnackbarHostState state should be initialized at runtime") -}