Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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() }
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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,
Expand All @@ -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)
},
Expand All @@ -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
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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,
Expand Down
1 change: 1 addition & 0 deletions core/ui/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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)
}
}
}

Expand Down