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.model.data.Topic
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.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import javax.inject.Inject
import kotlin.properties.ReadOnlyProperty
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Rule
import org.junit.Test
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.
@ -93,15 +93,25 @@ class NavigationTest {
ReadOnlyProperty<Any, String> { _, _ -> activity.getString(resId) }
// 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 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 appName by composeTestRule.stringResource(R.string.app_name)
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 brand by composeTestRule.stringResource(SettingsR.string.feature_settings_brand_android)
private val ok by composeTestRule.stringResource(SettingsR.string.feature_settings_dismiss_dialog_button_text)
private val settings by composeTestRule.stringResource(
SettingsR.string.feature_settings_top_app_bar_action_icon_description,
)
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
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.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import javax.inject.Inject
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import javax.inject.Inject
/**
* 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.util.TestNetworkMonitor
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.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@ -42,9 +45,6 @@ import kotlinx.coroutines.test.runTest
import kotlinx.datetime.TimeZone
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
/**
* Tests [NiaAppState].
@ -167,7 +167,9 @@ class NiaAppStateTest {
}
@Test
fun niaAppState_whenNetworkMonitorIsOffline_StateIsOffline() = runTest(UnconfinedTestDispatcher()) {
fun niaAppState_whenNetworkMonitorIsOffline_StateIsOffline() = runTest(
UnconfinedTestDispatcher(),
) {
composeTestRule.setContent {
state = NiaAppState(
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.rememberNiaAppState
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject
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].
*/
@Composable
private fun shouldUseAndroidTheme(
uiState: MainActivityUiState,
): Boolean = when (uiState) {
private fun shouldUseAndroidTheme(uiState: MainActivityUiState): Boolean = when (uiState) {
Loading -> false
is Success -> when (uiState.userData.themeBrand) {
ThemeBrand.DEFAULT -> false
@ -186,9 +184,7 @@ private fun shouldUseAndroidTheme(
* Returns `true` if the dynamic color is disabled, as a function of the [uiState].
*/
@Composable
private fun shouldDisableDynamicTheming(
uiState: MainActivityUiState,
): Boolean = when (uiState) {
private fun shouldDisableDynamicTheming(uiState: MainActivityUiState): Boolean = when (uiState) {
Loading -> false
is Success -> !uiState.userData.useDynamicColor
}
@ -198,9 +194,7 @@ private fun shouldDisableDynamicTheming(
* current system context.
*/
@Composable
private fun shouldUseDarkTheme(
uiState: MainActivityUiState,
): Boolean = when (uiState) {
private fun shouldUseDarkTheme(uiState: MainActivityUiState): Boolean = when (uiState) {
Loading -> isSystemInDarkTheme()
is Success -> when (uiState.userData.darkThemeConfig) {
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.model.data.UserData
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
@HiltViewModel
class MainActivityViewModel @Inject constructor(

@ -42,8 +42,6 @@ object JankStatsModule {
fun providesWindow(activity: Activity): Window = activity.window
@Provides
fun providesJankStats(
window: Window,
frameListener: OnFrameListener,
): JankStats = JankStats.createAndTrack(window, frameListener)
fun providesJankStats(window: Window, 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(
modifier = Modifier.semantics {
@ -271,24 +273,23 @@ private fun NiaBottomBar(
}
}
private fun Modifier.notificationDot(): Modifier =
composed {
val tertiaryColor = MaterialTheme.colorScheme.tertiary
drawWithContent {
drawContent()
drawCircle(
tertiaryColor,
radius = 5.dp.toPx(),
// This is based on the dimensions of the NavigationBar's "indicator pill";
// however, its parameters are private, so we must depend on them implicitly
// (NavigationBarTokens.ActiveIndicatorWidth = 64.dp)
center = center + Offset(
64.dp.toPx() * .45f,
32.dp.toPx() * -.45f - 6.dp.toPx(),
),
)
}
private fun Modifier.notificationDot(): Modifier = composed {
val tertiaryColor = MaterialTheme.colorScheme.tertiary
drawWithContent {
drawContent()
drawCircle(
tertiaryColor,
radius = 5.dp.toPx(),
// This is based on the dimensions of the NavigationBar's "indicator pill";
// however, its parameters are private, so we must depend on them implicitly
// (NavigationBarTokens.ActiveIndicatorWidth = 64.dp)
center = center + Offset(
64.dp.toPx() * .45f,
32.dp.toPx() * -.45f - 6.dp.toPx(),
),
)
}
}
private fun NavDestination?.isTopLevelDestinationInHierarchy(destination: TopLevelDestination) =
this?.hierarchy?.any {

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

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

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

@ -19,10 +19,10 @@ package com.google.samples.apps.nowinandroid.util
import android.util.Log
import androidx.profileinstaller.ProfileVerifier
import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.guava.await
import kotlinx.coroutines.launch
import javax.inject.Inject
/**
* 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.HiltAndroidTest
import dagger.hilt.android.testing.HiltTestApplication
import java.util.TimeZone
import javax.inject.Inject
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.Before
@ -56,8 +58,6 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
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.

@ -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.NiaTheme
import com.google.samples.apps.nowinandroid.core.designsystem.theme.TintTheme
import kotlin.test.assertEquals
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
/**
* Tests [NiaTheme] using different combinations of the theme mode parameters:

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

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

@ -86,10 +86,7 @@ fun RowScope.NiaNavigationBarItem(
* [NavigationBarItem]s.
*/
@Composable
fun NiaNavigationBar(
modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit,
) {
fun NiaNavigationBar(modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit) {
NavigationBar(
modifier = modifier,
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.
*/
@Composable
fun NiaTabRow(
selectedTabIndex: Int,
modifier: Modifier = Modifier,
tabs: @Composable () -> Unit,
) {
fun NiaTabRow(selectedTabIndex: Int, modifier: Modifier = Modifier, tabs: @Composable () -> Unit) {
TabRow(
selectedTabIndex = selectedTabIndex,
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)
// 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.unpackFloat1
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.min
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
@ -108,9 +108,7 @@ private val ScrollbarTrack.size
/**
* Returns the position of the scrollbar thumb on the track as a percentage
*/
private fun ScrollbarTrack.thumbPosition(
dimension: Float,
): Float = max(
private fun ScrollbarTrack.thumbPosition(dimension: Float): Float = max(
a = min(
a = dimension / size,
b = 1f,
@ -149,10 +147,7 @@ private value class ScrollbarTrack(
* @param thumbMovedPercent the distance the thumb has traveled as a percentage of total
* track size.
*/
fun scrollbarStateValue(
thumbSizePercent: Float,
thumbMovedPercent: Float,
) = ScrollbarStateValue(
fun scrollbarStateValue(thumbSizePercent: Float, thumbMovedPercent: Float) = ScrollbarStateValue(
packFloats(
val1 = thumbSizePercent,
val2 = thumbMovedPercent,

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

@ -33,24 +33,22 @@ import kotlin.math.roundToInt
* @param itemsAvailable the amount of items in the list.
*/
@Composable
fun LazyListState.rememberDraggableScroller(
itemsAvailable: Int,
): (Float) -> Unit = rememberDraggableScroller(
itemsAvailable = itemsAvailable,
scroll = ::scrollToItem,
)
fun LazyListState.rememberDraggableScroller(itemsAvailable: Int): (Float) -> Unit =
rememberDraggableScroller(
itemsAvailable = itemsAvailable,
scroll = ::scrollToItem,
)
/**
* Remembers a function to react to [Scrollbar] thumb position displacements for a [LazyGridState]
* @param itemsAvailable the amount of items in the grid.
*/
@Composable
fun LazyGridState.rememberDraggableScroller(
itemsAvailable: Int,
): (Float) -> Unit = rememberDraggableScroller(
itemsAvailable = itemsAvailable,
scroll = ::scrollToItem,
)
fun LazyGridState.rememberDraggableScroller(itemsAvailable: Int): (Float) -> Unit =
rememberDraggableScroller(
itemsAvailable = itemsAvailable,
scroll = ::scrollToItem,
)
/**
* Remembers a function to react to [Scrollbar] thumb position displacements for a
@ -58,12 +56,11 @@ fun LazyGridState.rememberDraggableScroller(
* @param itemsAvailable the amount of items in the staggered grid.
*/
@Composable
fun LazyStaggeredGridState.rememberDraggableScroller(
itemsAvailable: Int,
): (Float) -> Unit = rememberDraggableScroller(
itemsAvailable = itemsAvailable,
scroll = ::scrollToItem,
)
fun LazyStaggeredGridState.rememberDraggableScroller(itemsAvailable: Int): (Float) -> Unit =
rememberDraggableScroller(
itemsAvailable = itemsAvailable,
scroll = ::scrollToItem,
)
/**
* Generic function to react to [Scrollbar] thumb displacements in a lazy layout.

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

@ -54,9 +54,7 @@ internal class SystemTrayNotifier @Inject constructor(
@ApplicationContext private val context: Context,
) : Notifier {
override fun postNewsNotifications(
newsResources: List<NewsResource>,
) = with(context) {
override fun postNewsNotifications(newsResources: List<NewsResource>) = with(context) {
if (checkSelfPermission(this, permission.POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
return
}
@ -150,20 +148,19 @@ private fun Context.ensureNotificationChannelExists() {
NotificationManagerCompat.from(this).createNotificationChannel(channel)
}
private fun Context.newsPendingIntent(
newsResource: NewsResource,
): PendingIntent? = PendingIntent.getActivity(
this,
NEWS_NOTIFICATION_REQUEST_CODE,
Intent().apply {
action = Intent.ACTION_VIEW
data = newsResource.newsDeepLinkUri()
component = ComponentName(
packageName,
TARGET_ACTIVITY_NAME,
)
},
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
)
private fun Context.newsPendingIntent(newsResource: NewsResource): PendingIntent? =
PendingIntent.getActivity(
this,
NEWS_NOTIFICATION_REQUEST_CODE,
Intent().apply {
action = Intent.ACTION_VIEW
data = newsResource.newsDeepLinkUri()
component = ComponentName(
packageName,
TARGET_ACTIVITY_NAME,
)
},
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
)
private fun NewsResource.newsDeepLinkUri() = "$DEEP_LINK_SCHEME_AND_HOST/$FOR_YOU_PATH/$id".toUri()

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

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

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

@ -37,15 +37,19 @@ class TestSearchContentsRepository : SearchContentsRepository {
combine(cachedTopics, cachedNewsResources) { topics, news ->
SearchResult(
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 {
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
fun addTopics(topics: List<Topic>) = cachedTopics.update { it + topics }

@ -70,8 +70,10 @@ class SearchScreenTest {
@Before
fun setup() {
composeTestRule.activity.apply {
clearSearchContentDesc = getString(R.string.feature_search_clear_search_text_content_desc)
clearRecentSearchesContentDesc = getString(R.string.feature_search_clear_recent_searches_content_desc)
clearSearchContentDesc =
getString(R.string.feature_search_clear_search_text_content_desc)
clearRecentSearchesContentDesc =
getString(R.string.feature_search_clear_recent_searches_content_desc)
followButtonContentDesc =
getString(string.core_ui_interests_card_follow_button_content_desc)
unfollowButtonContentDesc =
@ -79,7 +81,8 @@ class SearchScreenTest {
topicsString = getString(R.string.feature_search_topics)
updatesString = getString(R.string.feature_search_updates)
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)
}
}

@ -180,7 +180,8 @@ internal fun SearchScreen(
onSearchQueryChanged(it)
onSearchTriggered(it)
},
recentSearchQueries = recentSearchesUiState.recentQueries.map { it.query },
recentSearchQueries =
recentSearchesUiState.recentQueries.map { it.query },
)
}
} else {
@ -202,15 +203,13 @@ internal fun SearchScreen(
}
@Composable
fun EmptySearchResultBody(
searchQuery: String,
onInterestsClick: () -> Unit,
) {
fun EmptySearchResultBody(searchQuery: String, onInterestsClick: () -> Unit) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
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)
Text(
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.model.data.UserSearchResult
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
@ -37,7 +38,6 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class SearchViewModel @Inject constructor(
@ -133,13 +133,12 @@ class SearchViewModel @Inject constructor(
}
}
private fun AnalyticsHelper.logEventSearchTriggered(query: String) =
logEvent(
event = AnalyticsEvent(
type = SEARCH_QUERY,
extras = listOf(element = Param(key = SEARCH_QUERY, value = query)),
),
)
private fun AnalyticsHelper.logEventSearchTriggered(query: String) = logEvent(
event = AnalyticsEvent(
type = SEARCH_QUERY,
extras = listOf(element = Param(key = SEARCH_QUERY, value = query)),
),
)
/** Minimum length where search query is considered as [SearchResultUiState.EmptyQuery] */
private const val SEARCH_QUERY_MIN_LENGTH = 2

@ -24,7 +24,10 @@ import com.google.samples.apps.nowinandroid.feature.search.SearchRoute
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(
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.Loading
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.first
import kotlinx.coroutines.launch
@ -39,8 +41,6 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
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

@ -57,10 +57,7 @@ class TestMethodNameDetector : Detector(), SourceCodeScanner {
private fun JavaContext.isAndroidTest() = Path("androidTest") in file.toPath()
private fun PsiMethod.detectPrefix(
context: JavaContext,
usageInfo: AnnotationUsageInfo,
) {
private fun PsiMethod.detectPrefix(context: JavaContext, usageInfo: AnnotationUsageInfo) {
if (!name.startsWith("test")) return
context.report(
issue = PREFIX,
@ -76,10 +73,7 @@ class TestMethodNameDetector : Detector(), SourceCodeScanner {
)
}
private fun PsiMethod.detectFormat(
context: JavaContext,
usageInfo: AnnotationUsageInfo,
) {
private fun PsiMethod.detectFormat(context: JavaContext, usageInfo: AnnotationUsageInfo) {
if (!context.isAndroidTest()) return
if ("""[^\W_]+(_[^\W_]+){1,2}""".toRegex().matches(name)) return
context.report(
@ -92,22 +86,19 @@ class TestMethodNameDetector : Detector(), SourceCodeScanner {
companion object {
private fun issue(
id: String,
briefDescription: String,
explanation: String,
): Issue = Issue.create(
id = id,
briefDescription = briefDescription,
explanation = explanation,
category = TESTING,
priority = 5,
severity = WARNING,
implementation = Implementation(
TestMethodNameDetector::class.java,
EnumSet.of(JAVA_FILE, TEST_SOURCES),
),
)
private fun issue(id: String, briefDescription: String, explanation: String): Issue =
Issue.create(
id = id,
briefDescription = briefDescription,
explanation = explanation,
category = TESTING,
priority = 5,
severity = WARNING,
implementation = Implementation(
TestMethodNameDetector::class.java,
EnumSet.of(JAVA_FILE, TEST_SOURCES),
),
)
@JvmField
val PREFIX: Issue = issue(
@ -119,8 +110,10 @@ class TestMethodNameDetector : Detector(), SourceCodeScanner {
@JvmField
val FORMAT: Issue = issue(
id = "TestMethodFormat",
briefDescription = "Test method does not follow the `given_when_then` or `when_then` format",
explanation = "Test method should follow the `given_when_then` or `when_then` format.",
briefDescription = "Test method does not follow " +
"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 dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import kotlin.test.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
@HiltAndroidTest
class SyncWorkerTest {

@ -34,7 +34,5 @@ abstract class SyncModule {
): SyncManager
@Binds
internal abstract fun bindsSyncSubscriber(
syncSubscriber: StubSyncSubscriber,
): SyncSubscriber
internal abstract fun bindsSyncSubscriber(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.workers.SyncWorker
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.map
import javax.inject.Inject
/**
* [SyncManager] backed by [WorkInfo] from [WorkManager]

@ -19,10 +19,9 @@ 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.AnalyticsHelper
internal fun AnalyticsHelper.logSyncStarted() =
logEvent(
AnalyticsEvent(type = "network_sync_started"),
)
internal fun AnalyticsHelper.logSyncStarted() = logEvent(
AnalyticsEvent(type = "network_sync_started"),
)
internal fun AnalyticsHelper.logSyncFinished(syncedSuccessfully: Boolean) {
val eventType = if (syncedSuccessfully) "network_sync_successful" else "network_sync_failed"

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

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

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

Loading…
Cancel
Save