Fix spotless.

Change-Id: Ia9b44fcd6242ba4c85814f88326e06b88f310d6b
pull/1350/head
Jaehwa Noh 2 years ago
parent 8593927aa0
commit 038a9744cd

@ -38,21 +38,21 @@ import com.google.samples.apps.nowinandroid.R
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPermissionRule import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPermissionRule
import com.google.samples.apps.nowinandroid.feature.bookmarks.R as BookmarksR
import com.google.samples.apps.nowinandroid.feature.foryou.R as FeatureForyouR
import com.google.samples.apps.nowinandroid.feature.search.R as FeatureSearchR
import com.google.samples.apps.nowinandroid.feature.settings.R as SettingsR
import dagger.hilt.android.testing.BindValue import dagger.hilt.android.testing.BindValue
import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import javax.inject.Inject
import kotlin.properties.ReadOnlyProperty
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import javax.inject.Inject
import kotlin.properties.ReadOnlyProperty
import com.google.samples.apps.nowinandroid.feature.bookmarks.R as BookmarksR
import com.google.samples.apps.nowinandroid.feature.foryou.R as FeatureForyouR
import com.google.samples.apps.nowinandroid.feature.search.R as FeatureSearchR
import com.google.samples.apps.nowinandroid.feature.settings.R as SettingsR
/** /**
* Tests all the navigation flows that are handled by the navigation library. * Tests all the navigation flows that are handled by the navigation library.
@ -93,15 +93,25 @@ class NavigationTest {
ReadOnlyProperty<Any, String> { _, _ -> activity.getString(resId) } ReadOnlyProperty<Any, String> { _, _ -> activity.getString(resId) }
// The strings used for matching in these tests // The strings used for matching in these tests
private val navigateUp by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_navigate_up) private val navigateUp by composeTestRule.stringResource(
FeatureForyouR.string.feature_foryou_navigate_up,
)
private val forYou by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_title) private val forYou by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_title)
private val interests by composeTestRule.stringResource(FeatureSearchR.string.feature_search_interests) private val interests by composeTestRule.stringResource(
FeatureSearchR.string.feature_search_interests,
)
private val sampleTopic = "Headlines" private val sampleTopic = "Headlines"
private val appName by composeTestRule.stringResource(R.string.app_name) private val appName by composeTestRule.stringResource(R.string.app_name)
private val saved by composeTestRule.stringResource(BookmarksR.string.feature_bookmarks_title) private val saved by composeTestRule.stringResource(BookmarksR.string.feature_bookmarks_title)
private val settings by composeTestRule.stringResource(SettingsR.string.feature_settings_top_app_bar_action_icon_description) private val settings by composeTestRule.stringResource(
private val brand by composeTestRule.stringResource(SettingsR.string.feature_settings_brand_android) SettingsR.string.feature_settings_top_app_bar_action_icon_description,
private val ok by composeTestRule.stringResource(SettingsR.string.feature_settings_dismiss_dialog_button_text) )
private val brand by composeTestRule.stringResource(
SettingsR.string.feature_settings_brand_android,
)
private val ok by composeTestRule.stringResource(
SettingsR.string.feature_settings_dismiss_dialog_button_text,
)
@Before @Before
fun setup() = hiltRule.inject() fun setup() = hiltRule.inject()

@ -37,11 +37,11 @@ import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActi
import dagger.hilt.android.testing.BindValue import dagger.hilt.android.testing.BindValue
import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import javax.inject.Inject
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import javax.inject.Inject
/** /**
* Tests that the navigation UI is rendered correctly on different screen sizes. * Tests that the navigation UI is rendered correctly on different screen sizes.

@ -35,6 +35,9 @@ import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepo
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
import com.google.samples.apps.nowinandroid.core.testing.util.TestNetworkMonitor import com.google.samples.apps.nowinandroid.core.testing.util.TestNetworkMonitor
import com.google.samples.apps.nowinandroid.core.testing.util.TestTimeZoneMonitor import com.google.samples.apps.nowinandroid.core.testing.util.TestTimeZoneMonitor
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.UnconfinedTestDispatcher
@ -42,9 +45,6 @@ import kotlinx.coroutines.test.runTest
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
/** /**
* Tests [NiaAppState]. * Tests [NiaAppState].
@ -167,7 +167,9 @@ class NiaAppStateTest {
} }
@Test @Test
fun niaAppState_whenNetworkMonitorIsOffline_StateIsOffline() = runTest(UnconfinedTestDispatcher()) { fun niaAppState_whenNetworkMonitorIsOffline_StateIsOffline() = runTest(
UnconfinedTestDispatcher(),
) {
composeTestRule.setContent { composeTestRule.setContent {
state = NiaAppState( state = NiaAppState(
navController = NavHostController(LocalContext.current), navController = NavHostController(LocalContext.current),

@ -51,10 +51,10 @@ import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone
import com.google.samples.apps.nowinandroid.ui.NiaApp import com.google.samples.apps.nowinandroid.ui.NiaApp
import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject
private const val TAG = "MainActivity" private const val TAG = "MainActivity"
@ -172,9 +172,7 @@ class MainActivity : ComponentActivity() {
* Returns `true` if the Android theme should be used, as a function of the [uiState]. * Returns `true` if the Android theme should be used, as a function of the [uiState].
*/ */
@Composable @Composable
private fun shouldUseAndroidTheme( private fun shouldUseAndroidTheme(uiState: MainActivityUiState): Boolean = when (uiState) {
uiState: MainActivityUiState,
): Boolean = when (uiState) {
Loading -> false Loading -> false
is Success -> when (uiState.userData.themeBrand) { is Success -> when (uiState.userData.themeBrand) {
ThemeBrand.DEFAULT -> false ThemeBrand.DEFAULT -> false
@ -186,9 +184,7 @@ private fun shouldUseAndroidTheme(
* Returns `true` if the dynamic color is disabled, as a function of the [uiState]. * Returns `true` if the dynamic color is disabled, as a function of the [uiState].
*/ */
@Composable @Composable
private fun shouldDisableDynamicTheming( private fun shouldDisableDynamicTheming(uiState: MainActivityUiState): Boolean = when (uiState) {
uiState: MainActivityUiState,
): Boolean = when (uiState) {
Loading -> false Loading -> false
is Success -> !uiState.userData.useDynamicColor is Success -> !uiState.userData.useDynamicColor
} }
@ -198,9 +194,7 @@ private fun shouldDisableDynamicTheming(
* current system context. * current system context.
*/ */
@Composable @Composable
private fun shouldUseDarkTheme( private fun shouldUseDarkTheme(uiState: MainActivityUiState): Boolean = when (uiState) {
uiState: MainActivityUiState,
): Boolean = when (uiState) {
Loading -> isSystemInDarkTheme() Loading -> isSystemInDarkTheme()
is Success -> when (uiState.userData.darkThemeConfig) { is Success -> when (uiState.userData.darkThemeConfig) {
DarkThemeConfig.FOLLOW_SYSTEM -> isSystemInDarkTheme() DarkThemeConfig.FOLLOW_SYSTEM -> isSystemInDarkTheme()

@ -23,11 +23,11 @@ import com.google.samples.apps.nowinandroid.MainActivityUiState.Success
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.model.data.UserData import com.google.samples.apps.nowinandroid.core.model.data.UserData
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
@HiltViewModel @HiltViewModel
class MainActivityViewModel @Inject constructor( class MainActivityViewModel @Inject constructor(

@ -42,8 +42,6 @@ object JankStatsModule {
fun providesWindow(activity: Activity): Window = activity.window fun providesWindow(activity: Activity): Window = activity.window
@Provides @Provides
fun providesJankStats( fun providesJankStats(window: Window, frameListener: OnFrameListener): JankStats =
window: Window, JankStats.createAndTrack(window, frameListener)
frameListener: OnFrameListener,
): JankStats = JankStats.createAndTrack(window, frameListener)
} }

@ -114,7 +114,9 @@ fun NiaApp(appState: NiaAppState) {
) )
} }
val unreadDestinations by appState.topLevelDestinationsWithUnreadResources.collectAsStateWithLifecycle() val unreadDestinations by appState
.topLevelDestinationsWithUnreadResources
.collectAsStateWithLifecycle()
Scaffold( Scaffold(
modifier = Modifier.semantics { modifier = Modifier.semantics {
@ -271,8 +273,7 @@ private fun NiaBottomBar(
} }
} }
private fun Modifier.notificationDot(): Modifier = private fun Modifier.notificationDot(): Modifier = composed {
composed {
val tertiaryColor = MaterialTheme.colorScheme.tertiary val tertiaryColor = MaterialTheme.colorScheme.tertiary
drawWithContent { drawWithContent {
drawContent() drawContent()

@ -128,7 +128,9 @@ class NiaAppState(
*/ */
val topLevelDestinationsWithUnreadResources: StateFlow<Set<TopLevelDestination>> = val topLevelDestinationsWithUnreadResources: StateFlow<Set<TopLevelDestination>> =
userNewsResourceRepository.observeAllForFollowedTopics() userNewsResourceRepository.observeAllForFollowedTopics()
.combine(userNewsResourceRepository.observeAllBookmarked()) { forYouNewsResources, bookmarkedNewsResources -> .combine(
userNewsResourceRepository.observeAllBookmarked(),
) { forYouNewsResources, bookmarkedNewsResources ->
setOfNotNull( setOfNotNull(
FOR_YOU.takeIf { forYouNewsResources.any { !it.hasBeenViewed } }, FOR_YOU.takeIf { forYouNewsResources.any { !it.hasBeenViewed } },
BOOKMARKS.takeIf { bookmarkedNewsResources.any { !it.hasBeenViewed } }, BOOKMARKS.takeIf { bookmarkedNewsResources.any { !it.hasBeenViewed } },

@ -20,8 +20,8 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_ARG import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_ARG
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.StateFlow
@HiltViewModel @HiltViewModel
class Interests2PaneViewModel @Inject constructor( class Interests2PaneViewModel @Inject constructor(

@ -60,9 +60,7 @@ fun NavGraphBuilder.interestsListDetailScreen() {
} }
@Composable @Composable
internal fun InterestsListDetailScreen( internal fun InterestsListDetailScreen(viewModel: Interests2PaneViewModel = hiltViewModel()) {
viewModel: Interests2PaneViewModel = hiltViewModel(),
) {
val selectedTopicId by viewModel.selectedTopicId.collectAsStateWithLifecycle() val selectedTopicId by viewModel.selectedTopicId.collectAsStateWithLifecycle()
InterestsListDetailScreen( InterestsListDetailScreen(
selectedTopicId = selectedTopicId, selectedTopicId = selectedTopicId,
@ -72,10 +70,7 @@ internal fun InterestsListDetailScreen(
@OptIn(ExperimentalMaterial3AdaptiveApi::class) @OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable @Composable
internal fun InterestsListDetailScreen( internal fun InterestsListDetailScreen(selectedTopicId: String?, onTopicClick: (String) -> Unit) {
selectedTopicId: String?,
onTopicClick: (String) -> Unit,
) {
val listDetailNavigator = rememberListDetailPaneScaffoldNavigator<Nothing>() val listDetailNavigator = rememberListDetailPaneScaffoldNavigator<Nothing>()
BackHandler(listDetailNavigator.canNavigateBack()) { BackHandler(listDetailNavigator.canNavigateBack()) {
listDetailNavigator.navigateBack() listDetailNavigator.navigateBack()

@ -19,10 +19,10 @@ package com.google.samples.apps.nowinandroid.util
import android.util.Log import android.util.Log
import androidx.profileinstaller.ProfileVerifier import androidx.profileinstaller.ProfileVerifier
import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.guava.await import kotlinx.coroutines.guava.await
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject
/** /**
* Logs the app's Baseline Profile Compilation Status using [ProfileVerifier]. * Logs the app's Baseline Profile Compilation Status using [ProfileVerifier].

@ -45,6 +45,8 @@ import dagger.hilt.android.testing.BindValue
import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.HiltTestApplication import dagger.hilt.android.testing.HiltTestApplication
import java.util.TimeZone
import javax.inject.Inject
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Before import org.junit.Before
@ -56,8 +58,6 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode import org.robolectric.annotation.GraphicsMode
import org.robolectric.annotation.LooperMode import org.robolectric.annotation.LooperMode
import java.util.TimeZone
import javax.inject.Inject
/** /**
* Tests that the navigation UI is rendered correctly on different screen sizes. * Tests that the navigation UI is rendered correctly on different screen sizes.

@ -42,9 +42,9 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradien
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalTintTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalTintTheme
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.designsystem.theme.TintTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.TintTheme
import kotlin.test.assertEquals
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals
/** /**
* Tests [NiaTheme] using different combinations of the theme mode parameters: * Tests [NiaTheme] using different combinations of the theme mode parameters:

@ -48,10 +48,7 @@ import kotlin.math.tan
* @param content The background content. * @param content The background content.
*/ */
@Composable @Composable
fun NiaBackground( fun NiaBackground(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
val color = LocalBackgroundTheme.current.color val color = LocalBackgroundTheme.current.color
val tonalElevation = LocalBackgroundTheme.current.tonalElevation val tonalElevation = LocalBackgroundTheme.current.tonalElevation
Surface( Surface(

@ -51,10 +51,7 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun NiaLoadingWheel( fun NiaLoadingWheel(contentDesc: String, modifier: Modifier = Modifier) {
contentDesc: String,
modifier: Modifier = Modifier,
) {
val infiniteTransition = rememberInfiniteTransition(label = "wheel transition") val infiniteTransition = rememberInfiniteTransition(label = "wheel transition")
// Specifies the float animation for slowly drawing out the lines on entering // Specifies the float animation for slowly drawing out the lines on entering
@ -132,10 +129,7 @@ fun NiaLoadingWheel(
} }
@Composable @Composable
fun NiaOverlayLoadingWheel( fun NiaOverlayLoadingWheel(contentDesc: String, modifier: Modifier = Modifier) {
contentDesc: String,
modifier: Modifier = Modifier,
) {
Surface( Surface(
shape = RoundedCornerShape(60.dp), shape = RoundedCornerShape(60.dp),
shadowElevation = 8.dp, shadowElevation = 8.dp,

@ -86,10 +86,7 @@ fun RowScope.NiaNavigationBarItem(
* [NavigationBarItem]s. * [NavigationBarItem]s.
*/ */
@Composable @Composable
fun NiaNavigationBar( fun NiaNavigationBar(modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit) {
modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit,
) {
NavigationBar( NavigationBar(
modifier = modifier, modifier = modifier,
contentColor = NiaNavigationDefaults.navigationContentColor(), contentColor = NiaNavigationDefaults.navigationContentColor(),

@ -80,11 +80,7 @@ fun NiaTab(
* inside this lambda will be measured and placed evenly across the row, each taking up equal space. * inside this lambda will be measured and placed evenly across the row, each taking up equal space.
*/ */
@Composable @Composable
fun NiaTabRow( fun NiaTabRow(selectedTabIndex: Int, modifier: Modifier = Modifier, tabs: @Composable () -> Unit) {
selectedTabIndex: Int,
modifier: Modifier = Modifier,
tabs: @Composable () -> Unit,
) {
TabRow( TabRow(
selectedTabIndex = selectedTabIndex, selectedTabIndex = selectedTabIndex,
modifier = modifier, modifier = modifier,

@ -185,7 +185,8 @@ private data class ScrollThumbElement(val colorProducer: ColorProducer) :
} }
} }
private class ScrollThumbNode(var colorProducer: ColorProducer) : DrawModifierNode, Modifier.Node() { private class ScrollThumbNode(var colorProducer: ColorProducer) :
DrawModifierNode, Modifier.Node() {
private val shape = RoundedCornerShape(16.dp) private val shape = RoundedCornerShape(16.dp)
// naive cache outline calculation if size is the same // naive cache outline calculation if size is the same

@ -54,12 +54,12 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.packFloats import androidx.compose.ui.util.packFloats
import androidx.compose.ui.util.unpackFloat1 import androidx.compose.ui.util.unpackFloat1
import androidx.compose.ui.util.unpackFloat2 import androidx.compose.ui.util.unpackFloat2
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.delay
import kotlinx.coroutines.withTimeout
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.delay
import kotlinx.coroutines.withTimeout
/** /**
* The delay between scrolls when a user long presses on the scrollbar track to initiate a scroll * The delay between scrolls when a user long presses on the scrollbar track to initiate a scroll
@ -108,9 +108,7 @@ private val ScrollbarTrack.size
/** /**
* Returns the position of the scrollbar thumb on the track as a percentage * Returns the position of the scrollbar thumb on the track as a percentage
*/ */
private fun ScrollbarTrack.thumbPosition( private fun ScrollbarTrack.thumbPosition(dimension: Float): Float = max(
dimension: Float,
): Float = max(
a = min( a = min(
a = dimension / size, a = dimension / size,
b = 1f, b = 1f,
@ -149,10 +147,7 @@ private value class ScrollbarTrack(
* @param thumbMovedPercent the distance the thumb has traveled as a percentage of total * @param thumbMovedPercent the distance the thumb has traveled as a percentage of total
* track size. * track size.
*/ */
fun scrollbarStateValue( fun scrollbarStateValue(thumbSizePercent: Float, thumbMovedPercent: Float) = ScrollbarStateValue(
thumbSizePercent: Float,
thumbMovedPercent: Float,
) = ScrollbarStateValue(
packFloats( packFloats(
val1 = thumbSizePercent, val1 = thumbSizePercent,
val2 = thumbMovedPercent, val2 = thumbMovedPercent,

@ -27,9 +27,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import kotlin.math.min
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlin.math.min
/** /**
* Calculates a [ScrollbarState] driven by the changes in a [LazyListState]. * Calculates a [ScrollbarState] driven by the changes in a [LazyListState].

@ -33,9 +33,8 @@ import kotlin.math.roundToInt
* @param itemsAvailable the amount of items in the list. * @param itemsAvailable the amount of items in the list.
*/ */
@Composable @Composable
fun LazyListState.rememberDraggableScroller( fun LazyListState.rememberDraggableScroller(itemsAvailable: Int): (Float) -> Unit =
itemsAvailable: Int, rememberDraggableScroller(
): (Float) -> Unit = rememberDraggableScroller(
itemsAvailable = itemsAvailable, itemsAvailable = itemsAvailable,
scroll = ::scrollToItem, scroll = ::scrollToItem,
) )
@ -45,9 +44,8 @@ fun LazyListState.rememberDraggableScroller(
* @param itemsAvailable the amount of items in the grid. * @param itemsAvailable the amount of items in the grid.
*/ */
@Composable @Composable
fun LazyGridState.rememberDraggableScroller( fun LazyGridState.rememberDraggableScroller(itemsAvailable: Int): (Float) -> Unit =
itemsAvailable: Int, rememberDraggableScroller(
): (Float) -> Unit = rememberDraggableScroller(
itemsAvailable = itemsAvailable, itemsAvailable = itemsAvailable,
scroll = ::scrollToItem, scroll = ::scrollToItem,
) )
@ -58,9 +56,8 @@ fun LazyGridState.rememberDraggableScroller(
* @param itemsAvailable the amount of items in the staggered grid. * @param itemsAvailable the amount of items in the staggered grid.
*/ */
@Composable @Composable
fun LazyStaggeredGridState.rememberDraggableScroller( fun LazyStaggeredGridState.rememberDraggableScroller(itemsAvailable: Int): (Float) -> Unit =
itemsAvailable: Int, rememberDraggableScroller(
): (Float) -> Unit = rememberDraggableScroller(
itemsAvailable = itemsAvailable, itemsAvailable = itemsAvailable,
scroll = ::scrollToItem, scroll = ::scrollToItem,
) )

@ -25,7 +25,5 @@ import dagger.hilt.components.SingletonComponent
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
internal abstract class NotificationsModule { internal abstract class NotificationsModule {
@Binds @Binds
abstract fun bindNotifier( abstract fun bindNotifier(notifier: NoOpNotifier): Notifier
notifier: NoOpNotifier,
): Notifier
} }

@ -54,9 +54,7 @@ internal class SystemTrayNotifier @Inject constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
) : Notifier { ) : Notifier {
override fun postNewsNotifications( override fun postNewsNotifications(newsResources: List<NewsResource>) = with(context) {
newsResources: List<NewsResource>,
) = with(context) {
if (checkSelfPermission(this, permission.POST_NOTIFICATIONS) != PERMISSION_GRANTED) { if (checkSelfPermission(this, permission.POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
return return
} }
@ -150,9 +148,8 @@ private fun Context.ensureNotificationChannelExists() {
NotificationManagerCompat.from(this).createNotificationChannel(channel) NotificationManagerCompat.from(this).createNotificationChannel(channel)
} }
private fun Context.newsPendingIntent( private fun Context.newsPendingIntent(newsResource: NewsResource): PendingIntent? =
newsResource: NewsResource, PendingIntent.getActivity(
): PendingIntent? = PendingIntent.getActivity(
this, this,
NEWS_NOTIFICATION_REQUEST_CODE, NEWS_NOTIFICATION_REQUEST_CODE,
Intent().apply { Intent().apply {

@ -25,7 +25,5 @@ import dagger.hilt.components.SingletonComponent
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
internal abstract class NotificationsModule { internal abstract class NotificationsModule {
@Binds @Binds
abstract fun bindNotifier( abstract fun bindNotifier(notifier: SystemTrayNotifier): Notifier
notifier: SystemTrayNotifier,
): Notifier
} }

@ -20,9 +20,9 @@ import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.UnconfinedTestDispatcher
import javax.inject.Singleton
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)

@ -39,7 +39,6 @@ internal object TestDispatchersModule {
@Provides @Provides
@Dispatcher(Default) @Dispatcher(Default)
fun providesDefaultDispatcher( fun providesDefaultDispatcher(testDispatcher: TestDispatcher): CoroutineDispatcher =
testDispatcher: TestDispatcher, testDispatcher
): CoroutineDispatcher = testDispatcher
} }

@ -37,15 +37,19 @@ class TestSearchContentsRepository : SearchContentsRepository {
combine(cachedTopics, cachedNewsResources) { topics, news -> combine(cachedTopics, cachedNewsResources) { topics, news ->
SearchResult( SearchResult(
topics = topics.filter { topics = topics.filter {
searchQuery in it.name || searchQuery in it.shortDescription || searchQuery in it.longDescription searchQuery in it.name ||
searchQuery in it.shortDescription ||
searchQuery in it.longDescription
}, },
newsResources = news.filter { newsResources = news.filter {
searchQuery in it.content || searchQuery in it.title searchQuery in it.content ||
searchQuery in it.title
}, },
) )
} }
override fun getSearchContentsCount(): Flow<Int> = combine(cachedTopics, cachedNewsResources) { topics, news -> topics.size + news.size } override fun getSearchContentsCount(): Flow<Int> =
combine(cachedTopics, cachedNewsResources) { topics, news -> topics.size + news.size }
@TestOnly @TestOnly
fun addTopics(topics: List<Topic>) = cachedTopics.update { it + topics } fun addTopics(topics: List<Topic>) = cachedTopics.update { it + topics }

@ -70,8 +70,10 @@ class SearchScreenTest {
@Before @Before
fun setup() { fun setup() {
composeTestRule.activity.apply { composeTestRule.activity.apply {
clearSearchContentDesc = getString(R.string.feature_search_clear_search_text_content_desc) clearSearchContentDesc =
clearRecentSearchesContentDesc = getString(R.string.feature_search_clear_recent_searches_content_desc) getString(R.string.feature_search_clear_search_text_content_desc)
clearRecentSearchesContentDesc =
getString(R.string.feature_search_clear_recent_searches_content_desc)
followButtonContentDesc = followButtonContentDesc =
getString(string.core_ui_interests_card_follow_button_content_desc) getString(string.core_ui_interests_card_follow_button_content_desc)
unfollowButtonContentDesc = unfollowButtonContentDesc =
@ -79,7 +81,8 @@ class SearchScreenTest {
topicsString = getString(R.string.feature_search_topics) topicsString = getString(R.string.feature_search_topics)
updatesString = getString(R.string.feature_search_updates) updatesString = getString(R.string.feature_search_updates)
tryAnotherSearchString = getString(R.string.feature_search_try_another_search) + tryAnotherSearchString = getString(R.string.feature_search_try_another_search) +
" " + getString(R.string.feature_search_interests) + " " + getString(R.string.feature_search_to_browse_topics) " " + getString(R.string.feature_search_interests) +
" " + getString(R.string.feature_search_to_browse_topics)
searchNotReadyString = getString(R.string.feature_search_not_ready) searchNotReadyString = getString(R.string.feature_search_not_ready)
} }
} }

@ -180,7 +180,8 @@ internal fun SearchScreen(
onSearchQueryChanged(it) onSearchQueryChanged(it)
onSearchTriggered(it) onSearchTriggered(it)
}, },
recentSearchQueries = recentSearchesUiState.recentQueries.map { it.query }, recentSearchQueries =
recentSearchesUiState.recentQueries.map { it.query },
) )
} }
} else { } else {
@ -202,15 +203,13 @@ internal fun SearchScreen(
} }
@Composable @Composable
fun EmptySearchResultBody( fun EmptySearchResultBody(searchQuery: String, onInterestsClick: () -> Unit) {
searchQuery: String,
onInterestsClick: () -> Unit,
) {
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(horizontal = 48.dp), modifier = Modifier.padding(horizontal = 48.dp),
) { ) {
val message = stringResource(id = searchR.string.feature_search_result_not_found, searchQuery) val message =
stringResource(id = searchR.string.feature_search_result_not_found, searchQuery)
val start = message.indexOf(searchQuery) val start = message.indexOf(searchQuery)
Text( Text(
text = AnnotatedString( text = AnnotatedString(

@ -29,6 +29,7 @@ import com.google.samples.apps.nowinandroid.core.domain.GetRecentSearchQueriesUs
import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase
import com.google.samples.apps.nowinandroid.core.model.data.UserSearchResult import com.google.samples.apps.nowinandroid.core.model.data.UserSearchResult
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
@ -37,7 +38,6 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel @HiltViewModel
class SearchViewModel @Inject constructor( class SearchViewModel @Inject constructor(
@ -133,8 +133,7 @@ class SearchViewModel @Inject constructor(
} }
} }
private fun AnalyticsHelper.logEventSearchTriggered(query: String) = private fun AnalyticsHelper.logEventSearchTriggered(query: String) = logEvent(
logEvent(
event = AnalyticsEvent( event = AnalyticsEvent(
type = SEARCH_QUERY, type = SEARCH_QUERY,
extras = listOf(element = Param(key = SEARCH_QUERY, value = query)), extras = listOf(element = Param(key = SEARCH_QUERY, value = query)),

@ -24,7 +24,10 @@ import com.google.samples.apps.nowinandroid.feature.search.SearchRoute
const val SEARCH_ROUTE = "search_route" const val SEARCH_ROUTE = "search_route"
fun NavController.navigateToSearch(navOptions: NavOptions? = null) = navigate(SEARCH_ROUTE, navOptions) fun NavController.navigateToSearch(navOptions: NavOptions? = null) = navigate(
SEARCH_ROUTE,
navOptions,
)
fun NavGraphBuilder.searchScreen( fun NavGraphBuilder.searchScreen(
onBackClick: () -> Unit, onBackClick: () -> Unit,

@ -31,6 +31,8 @@ import com.google.samples.apps.nowinandroid.feature.search.RecentSearchQueriesUi
import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.EmptyQuery import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.EmptyQuery
import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.Loading import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.Loading
import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.SearchNotReady import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.SearchNotReady
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -39,8 +41,6 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
/** /**
* To learn more about how this test handles Flows created with stateIn, see * To learn more about how this test handles Flows created with stateIn, see

@ -57,10 +57,7 @@ class TestMethodNameDetector : Detector(), SourceCodeScanner {
private fun JavaContext.isAndroidTest() = Path("androidTest") in file.toPath() private fun JavaContext.isAndroidTest() = Path("androidTest") in file.toPath()
private fun PsiMethod.detectPrefix( private fun PsiMethod.detectPrefix(context: JavaContext, usageInfo: AnnotationUsageInfo) {
context: JavaContext,
usageInfo: AnnotationUsageInfo,
) {
if (!name.startsWith("test")) return if (!name.startsWith("test")) return
context.report( context.report(
issue = PREFIX, issue = PREFIX,
@ -76,10 +73,7 @@ class TestMethodNameDetector : Detector(), SourceCodeScanner {
) )
} }
private fun PsiMethod.detectFormat( private fun PsiMethod.detectFormat(context: JavaContext, usageInfo: AnnotationUsageInfo) {
context: JavaContext,
usageInfo: AnnotationUsageInfo,
) {
if (!context.isAndroidTest()) return if (!context.isAndroidTest()) return
if ("""[^\W_]+(_[^\W_]+){1,2}""".toRegex().matches(name)) return if ("""[^\W_]+(_[^\W_]+){1,2}""".toRegex().matches(name)) return
context.report( context.report(
@ -92,11 +86,8 @@ class TestMethodNameDetector : Detector(), SourceCodeScanner {
companion object { companion object {
private fun issue( private fun issue(id: String, briefDescription: String, explanation: String): Issue =
id: String, Issue.create(
briefDescription: String,
explanation: String,
): Issue = Issue.create(
id = id, id = id,
briefDescription = briefDescription, briefDescription = briefDescription,
explanation = explanation, explanation = explanation,
@ -119,8 +110,10 @@ class TestMethodNameDetector : Detector(), SourceCodeScanner {
@JvmField @JvmField
val FORMAT: Issue = issue( val FORMAT: Issue = issue(
id = "TestMethodFormat", id = "TestMethodFormat",
briefDescription = "Test method does not follow the `given_when_then` or `when_then` format", briefDescription = "Test method does not follow " +
explanation = "Test method should follow the `given_when_then` or `when_then` format.", "the `given_when_then` or `when_then` format",
explanation = "Test method should follow " +
"the `given_when_then` or `when_then` format.",
) )
} }
} }

@ -25,10 +25,10 @@ import androidx.work.testing.SynchronousExecutor
import androidx.work.testing.WorkManagerTestInitHelper import androidx.work.testing.WorkManagerTestInitHelper
import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import kotlin.test.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals
@HiltAndroidTest @HiltAndroidTest
class SyncWorkerTest { class SyncWorkerTest {

@ -34,7 +34,5 @@ abstract class SyncModule {
): SyncManager ): SyncManager
@Binds @Binds
internal abstract fun bindsSyncSubscriber( internal abstract fun bindsSyncSubscriber(syncSubscriber: StubSyncSubscriber): SyncSubscriber
syncSubscriber: StubSyncSubscriber,
): SyncSubscriber
} }

@ -25,10 +25,10 @@ import com.google.samples.apps.nowinandroid.core.data.util.SyncManager
import com.google.samples.apps.nowinandroid.sync.initializers.SYNC_WORK_NAME import com.google.samples.apps.nowinandroid.sync.initializers.SYNC_WORK_NAME
import com.google.samples.apps.nowinandroid.sync.workers.SyncWorker import com.google.samples.apps.nowinandroid.sync.workers.SyncWorker
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import javax.inject.Inject
/** /**
* [SyncManager] backed by [WorkInfo] from [WorkManager] * [SyncManager] backed by [WorkInfo] from [WorkManager]

@ -19,8 +19,7 @@ package com.google.samples.apps.nowinandroid.sync.workers
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper
internal fun AnalyticsHelper.logSyncStarted() = internal fun AnalyticsHelper.logSyncStarted() = logEvent(
logEvent(
AnalyticsEvent(type = "network_sync_started"), AnalyticsEvent(type = "network_sync_started"),
) )

@ -43,8 +43,7 @@ private const val WORKER_CLASS_NAME = "RouterWorkerDelegateClassName"
* Adds metadata to a WorkRequest to identify what [CoroutineWorker] the [DelegatingWorker] should * Adds metadata to a WorkRequest to identify what [CoroutineWorker] the [DelegatingWorker] should
* delegate to * delegate to
*/ */
internal fun KClass<out CoroutineWorker>.delegatedData() = internal fun KClass<out CoroutineWorker>.delegatedData() = Data.Builder()
Data.Builder()
.putString(WORKER_CLASS_NAME, qualifiedName) .putString(WORKER_CLASS_NAME, qualifiedName)
.build() .build()
@ -72,9 +71,7 @@ class DelegatingWorker(
as? CoroutineWorker as? CoroutineWorker
?: throw IllegalArgumentException("Unable to find appropriate worker") ?: throw IllegalArgumentException("Unable to find appropriate worker")
override suspend fun getForegroundInfo(): ForegroundInfo = override suspend fun getForegroundInfo(): ForegroundInfo = delegateWorker.getForegroundInfo()
delegateWorker.getForegroundInfo()
override suspend fun doWork(): Result = override suspend fun doWork(): Result = delegateWorker.doWork()
delegateWorker.doWork()
} }

@ -60,8 +60,7 @@ internal class SyncWorker @AssistedInject constructor(
private val syncSubscriber: SyncSubscriber, private val syncSubscriber: SyncSubscriber,
) : CoroutineWorker(appContext, workerParams), Synchronizer { ) : CoroutineWorker(appContext, workerParams), Synchronizer {
override suspend fun getForegroundInfo(): ForegroundInfo = override suspend fun getForegroundInfo(): ForegroundInfo = appContext.syncForegroundInfo()
appContext.syncForegroundInfo()
override suspend fun doWork(): Result = withContext(ioDispatcher) { override suspend fun doWork(): Result = withContext(ioDispatcher) {
traceAsync("Sync", 0) { traceAsync("Sync", 0) {

@ -18,8 +18,8 @@ package com.google.samples.apps.nowinandroid.sync.status
import com.google.firebase.messaging.FirebaseMessaging import com.google.firebase.messaging.FirebaseMessaging
import com.google.samples.apps.nowinandroid.sync.initializers.SYNC_TOPIC import com.google.samples.apps.nowinandroid.sync.initializers.SYNC_TOPIC
import kotlinx.coroutines.tasks.await
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.tasks.await
/** /**
* Implementation of [SyncSubscriber] that subscribes to the FCM [SYNC_TOPIC] * Implementation of [SyncSubscriber] that subscribes to the FCM [SYNC_TOPIC]

Loading…
Cancel
Save