pull/2118/merge
Saad Khan 5 days ago committed by GitHub
commit ef23329093
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -65,6 +65,7 @@ import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.ui.NavDisplay
import com.google.samples.apps.nowinandroid.R
import com.google.samples.apps.nowinandroid.core.designsystem.component.LocalSnackbarHostState
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.NiaNavigationSuiteScaffold
@ -74,7 +75,6 @@ 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.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

@ -65,9 +65,9 @@ import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourc
import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeUserDataRepository
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.LocalSnackbarHostState
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.uitesthiltmanifest.HiltComponentActivity
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest

@ -38,9 +38,9 @@ import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourc
import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeUserDataRepository
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.LocalSnackbarHostState
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.uitesthiltmanifest.HiltComponentActivity
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest

@ -0,0 +1,22 @@
/*
* Copyright 2024 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.designsystem.component
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.compositionLocalOf
val LocalSnackbarHostState = compositionLocalOf { SnackbarHostState() }

@ -16,8 +16,10 @@
package com.google.samples.apps.nowinandroid.core.ui
import android.content.ActivityNotFoundException
import android.content.Context
import android.net.Uri
import android.util.Log
import androidx.annotation.ColorInt
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
@ -29,16 +31,20 @@ import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper
import com.google.samples.apps.nowinandroid.core.designsystem.component.LocalSnackbarHostState
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import kotlinx.coroutines.launch
/**
* An extension on [LazyListScope] defining a feed with news resources.
@ -62,6 +68,9 @@ fun LazyStaggeredGridScope.newsFeed(
val context = LocalContext.current
val analyticsHelper = LocalAnalyticsHelper.current
val backgroundColor = MaterialTheme.colorScheme.background.toArgb()
val snackbarHostState = LocalSnackbarHostState.current
val coroutineScope = rememberCoroutineScope()
val noBrowserMessage = stringResource(R.string.core_ui_no_browser_available)
NewsResourceCardExpanded(
userNewsResource = userNewsResource,
@ -71,7 +80,11 @@ fun LazyStaggeredGridScope.newsFeed(
analyticsHelper.logNewsResourceOpened(
newsResourceId = userNewsResource.id,
)
launchCustomChromeTab(context, Uri.parse(userNewsResource.url), backgroundColor)
if (!launchCustomChromeTab(context, Uri.parse(userNewsResource.url), backgroundColor)) {
coroutineScope.launch {
snackbarHostState.showSnackbar(noBrowserMessage)
}
}
onNewsResourceViewed(userNewsResource.id)
},
@ -92,14 +105,25 @@ fun LazyStaggeredGridScope.newsFeed(
}
}
fun launchCustomChromeTab(context: Context, uri: Uri, @ColorInt toolbarColor: Int) {
/**
* Launches a Chrome Custom Tab for the given [uri].
*
* @return `true` if the URL was launched successfully, `false` if no browser activity was found.
*/
fun launchCustomChromeTab(context: Context, uri: Uri, @ColorInt toolbarColor: Int): Boolean {
val customTabBarColor = CustomTabColorSchemeParams.Builder()
.setToolbarColor(toolbarColor).build()
val customTabsIntent = CustomTabsIntent.Builder()
.setDefaultColorSchemeParams(customTabBarColor)
.build()
customTabsIntent.launchUrl(context, uri)
return try {
customTabsIntent.launchUrl(context, uri)
true
} catch (e: ActivityNotFoundException) {
Log.e("NewsFeed", "No browser activity found to handle URI: $uri", e)
false
}
}
/**

@ -20,11 +20,15 @@ import android.net.Uri
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper
import com.google.samples.apps.nowinandroid.core.designsystem.component.LocalSnackbarHostState
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import kotlinx.coroutines.launch
/**
* Extension function for displaying a [List] of [NewsResourceCardExpanded] backed by a list of
@ -47,6 +51,9 @@ fun LazyListScope.userNewsResourceCardItems(
val backgroundColor = MaterialTheme.colorScheme.background.toArgb()
val context = LocalContext.current
val analyticsHelper = LocalAnalyticsHelper.current
val snackbarHostState = LocalSnackbarHostState.current
val coroutineScope = rememberCoroutineScope()
val noBrowserMessage = stringResource(R.string.core_ui_no_browser_available)
NewsResourceCardExpanded(
userNewsResource = userNewsResource,
@ -57,7 +64,11 @@ fun LazyListScope.userNewsResourceCardItems(
analyticsHelper.logNewsResourceOpened(
newsResourceId = userNewsResource.id,
)
launchCustomChromeTab(context, resourceUrl, backgroundColor)
if (!launchCustomChromeTab(context, resourceUrl, backgroundColor)) {
coroutineScope.launch {
snackbarHostState.showSnackbar(noBrowserMessage)
}
}
onNewsResourceViewed(userNewsResource.id)
},
onTopicClick = onTopicClick,

@ -31,4 +31,5 @@
<string name="core_ui_interests_card_unfollow_button_content_desc">Unfollow interest</string>
<string name="core_ui_feed_sharing">Feed sharing</string>
<string name="core_ui_feed_sharing_data">%1$s: %2$s</string>
<string name="core_ui_no_browser_available">No browser available to open link</string>
</resources>

@ -17,11 +17,10 @@
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.designsystem.component.LocalSnackbarHostState
import com.google.samples.apps.nowinandroid.core.navigation.Navigator
import com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation.BookmarksNavKey
import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.BookmarksScreen
@ -42,8 +41,3 @@ fun EntryProviderScope<NavKey>.bookmarksEntry(navigator: Navigator) {
)
}
}
// TODO: Why is this here?
val LocalSnackbarHostState = compositionLocalOf<SnackbarHostState> {
error("SnackbarHostState state should be initialized at runtime")
}

@ -86,6 +86,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionStatus.Denied
import com.google.accompanist.permissions.rememberPermissionState
import com.google.samples.apps.nowinandroid.core.designsystem.component.DynamicAsyncImage
import com.google.samples.apps.nowinandroid.core.designsystem.component.LocalSnackbarHostState
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaButton
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaOverlayLoadingWheel
@ -104,6 +105,7 @@ import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParam
import com.google.samples.apps.nowinandroid.core.ui.launchCustomChromeTab
import com.google.samples.apps.nowinandroid.core.ui.newsFeed
import com.google.samples.apps.nowinandroid.feature.foryou.api.R
import com.google.samples.apps.nowinandroid.core.ui.R as UiR
@Composable
fun ForYouScreen(
@ -469,16 +471,21 @@ private fun DeepLinkEffect(
) {
val context = LocalContext.current
val backgroundColor = MaterialTheme.colorScheme.background.toArgb()
val snackbarHostState = LocalSnackbarHostState.current
val noBrowserMessage = stringResource(UiR.string.core_ui_no_browser_available)
LaunchedEffect(userNewsResource) {
if (userNewsResource == null) return@LaunchedEffect
if (!userNewsResource.hasBeenViewed) onDeepLinkOpened(userNewsResource.id)
launchCustomChromeTab(
val launched = launchCustomChromeTab(
context = context,
uri = Uri.parse(userNewsResource.url),
toolbarColor = backgroundColor,
)
if (!launched) {
snackbarHostState.showSnackbar(noBrowserMessage)
}
}
}

Loading…
Cancel
Save