Fix compilation warnings: upgrade kotlinx-datetime 0.7.1, migrate deprecated APIs

- Upgrade kotlinx-datetime 0.6.1 → 0.7.1 and migrate to kotlin.time.Instant/Clock
- Migrate TabRow → SecondaryTabRow with TabIndicatorScope API
- Fix LocalDateTime constructors (monthNumber → month, dayOfMonth → day)
- Remove deprecated KoinAndroidContext wrapper, add KOIN_DEFAULT_MODULE KSP arg
- Convert SystemTrayNotifier expression body with return to block body
- Add @param: annotation target prefix for @Dispatcher and @ApplicationScope
- Add @ConsistentCopyVisibility to UserNewsResource data class
- Fix JVM compilation: use correct uiToolingPreview dep, move lifecycle-runtime-compose to commonMain
pull/2064/head
Mercury Li 2 weeks ago
parent 78a9b94b0c
commit be2f1e9812

@ -48,7 +48,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.compose.KoinAndroidContext
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
@ -144,9 +143,7 @@ class MainActivity : ComponentActivity() {
androidTheme = themeSettings.androidTheme,
disableDynamicTheming = themeSettings.disableDynamicTheming,
) {
KoinAndroidContext {
NiaApp(appState)
}
NiaApp(appState)
}
}
}

@ -48,7 +48,7 @@ import kotlinx.coroutines.launch
* @see androidx.profileinstaller.ProfileVerifier.CompilationStatus.ResultCode
*/
class ProfileVerifierLogger(
@ApplicationScope private val scope: CoroutineScope,
@param:ApplicationScope private val scope: CoroutineScope,
) {
companion object {
private const val TAG = "ProfileInstaller"

@ -52,7 +52,7 @@ class CmpFeatureConventionPlugin : Plugin<Project> {
"commonMainImplementation"(libs.findLibrary("koin.compose.viewmodel").get())
"commonMainImplementation"(libs.findLibrary("koin.compose.viewmodel.navigation").get())
"androidMainImplementation"(libs.findLibrary("androidx.lifecycle.runtimeCompose").get())
"commonMainImplementation"(libs.findLibrary("androidx.lifecycle.runtimeCompose").get())
"androidMainImplementation"(libs.findLibrary("androidx.tracing.ktx").get())
"androidInstrumentedTestImplementation"(libs.findLibrary("androidx.compose.ui.test").get())

@ -28,6 +28,10 @@ class KoinConventionPlugin : Plugin<Project> {
apply("com.google.devtools.ksp")
}
extensions.configure(com.google.devtools.ksp.gradle.KspExtension::class.java) {
arg("KOIN_DEFAULT_MODULE", "true")
}
extensions.configure(KotlinMultiplatformExtension::class.java) {
sourceSets.named("commonMain").configure {
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")

@ -17,8 +17,8 @@
package com.google.samples.apps.nowinandroid.core.data.model
import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlin.time.Clock
import kotlin.time.Instant
data class RecentSearchQuery(
val query: String,

@ -22,7 +22,7 @@ import com.google.samples.apps.nowinandroid.core.database.dao.RecentSearchQueryD
import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.datetime.Clock
import kotlin.time.Clock
class DefaultRecentSearchRepository(
private val recentSearchQueryDao: RecentSearchQueryDao,

@ -42,7 +42,7 @@ class DefaultSearchContentsRepository(
private val newsResourceFtsDao: NewsResourceFtsDao,
private val topicDao: TopicDao,
private val topicFtsDao: TopicFtsDao,
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
@param:Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
) : SearchContentsRepository {
override suspend fun populateFtsData() {

@ -26,9 +26,9 @@ import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserData
import com.google.samples.apps.nowinandroid.core.testing.repository.emptyUserData
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import kotlinx.datetime.Instant
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.time.Instant
class CompositeUserNewsResourceRepositoryTest {

@ -23,10 +23,10 @@ import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand.DEFAULT
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.model.data.UserData
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import kotlinx.datetime.Clock
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlin.time.Clock
class UserNewsResourceTest {

@ -21,9 +21,9 @@ import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import com.google.samples.apps.nowinandroid.core.network.model.asExternalModel
import kotlinx.datetime.Instant
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.time.Instant
class NetworkEntityTest {

@ -18,9 +18,9 @@ package com.google.samples.apps.nowinandroid.core.database.model
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import kotlinx.datetime.Instant
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.time.Instant
class PopulatedNewsResourceKtTest {
@Test

@ -25,7 +25,7 @@ import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsRes
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.datetime.Instant
import kotlin.time.Instant
/**
* DAO for [NewsResource] and [NewsResourceEntity] access

@ -23,7 +23,7 @@ import com.google.samples.apps.nowinandroid.core.database.Recent_search_query
import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.datetime.Instant
import kotlin.time.Instant
/**
* DAO for [RecentSearchQueryEntity] access

@ -17,7 +17,7 @@
package com.google.samples.apps.nowinandroid.core.database.model
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import kotlinx.datetime.Instant
import kotlin.time.Instant
/**
* Defines an NiA news resource.

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.database.model
import kotlinx.datetime.Instant
import kotlin.time.Instant
/**
* Defines an database entity that stored recent search queries.

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.database.util
import kotlinx.datetime.Instant
import kotlin.time.Instant
internal class InstantConverter {

@ -25,9 +25,9 @@ import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import kotlinx.datetime.Instant
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.time.Instant
class NewsResourceDaoTest {
private lateinit var newsResourceDao: NewsResourceDao

@ -37,7 +37,7 @@ private const val USER_DATA_KEY = "userData"
@OptIn(ExperimentalSerializationApi::class, ExperimentalSettingsApi::class)
class NiaPreferencesDataSource(
private val settings: Settings,
@Dispatcher(IO) private val dispatcher: CoroutineDispatcher,
@param:Dispatcher(IO) private val dispatcher: CoroutineDispatcher,
) {
// FlowSettings did not support JS, use a workaround instead
// https://github.com/russhwolf/multiplatform-settings/issues/139

@ -20,10 +20,9 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.SecondaryTabRow
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.TabRowDefaults
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@ -72,7 +71,7 @@ fun NiaTab(
}
/**
* Now in Android tab row. Wraps Material 3 [TabRow].
* Now in Android tab row. Wraps Material 3 [SecondaryTabRow].
*
* @param selectedTabIndex The index of the currently selected tab.
* @param modifier Modifier to be applied to the tab row.
@ -85,14 +84,14 @@ fun NiaTabRow(
modifier: Modifier = Modifier,
tabs: @Composable () -> Unit,
) {
TabRow(
SecondaryTabRow(
selectedTabIndex = selectedTabIndex,
modifier = modifier,
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.onSurface,
indicator = { tabPositions ->
indicator = {
TabRowDefaults.SecondaryIndicator(
modifier = Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex]),
modifier = Modifier.tabIndicatorOffset(selectedTabIndex),
height = 2.dp,
color = MaterialTheme.colorScheme.onSurface,
)

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.model.data
import kotlinx.datetime.Instant
import kotlin.time.Instant
/**
* External data layer representation of a fully populated NiA news resource

@ -16,12 +16,13 @@
package com.google.samples.apps.nowinandroid.core.model.data
import kotlinx.datetime.Instant
import kotlin.time.Instant
/**
* A [NewsResource] with additional user information such as whether the user is following the
* news resource's topics and whether they have saved (bookmarked) this news resource.
*/
@ConsistentCopyVisibility
data class UserNewsResource internal constructor(
val id: String,
val title: String,

@ -34,7 +34,7 @@ import org.koin.core.annotation.Single
*/
@Single
class DemoNiaNetworkDataSource(
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
@param:Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
private val networkJson: Json,
) : NiaNetworkDataSource {

@ -17,8 +17,8 @@
package com.google.samples.apps.nowinandroid.core.network.model
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable
import kotlin.time.Instant
/**
* Network representation of [NewsResource] when fetched from /newsresources

@ -21,6 +21,7 @@ import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.runTest
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.Month
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlinx.serialization.json.Json
@ -70,8 +71,8 @@ class DemoNiaNetworkDataSourceTest {
headerImageUrl = "https://developer.android.com/images/hero-assets/android-basics-compose.svg",
publishDate = LocalDateTime(
year = 2022,
monthNumber = 5,
dayOfMonth = 4,
month = Month.MAY,
day = 4,
hour = 23,
minute = 0,
second = 0,

@ -51,52 +51,54 @@ internal class SystemTrayNotifier constructor(
override fun postNewsNotifications(
newsResources: List<NewsResource>,
) = with(context) {
if (checkSelfPermission(this, permission.POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
return
}
) {
with(context) {
if (checkSelfPermission(this, permission.POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
return
}
val truncatedNewsResources = newsResources.take(MAX_NUM_NOTIFICATIONS)
val truncatedNewsResources = newsResources.take(MAX_NUM_NOTIFICATIONS)
val newsNotifications = truncatedNewsResources.map { newsResource ->
createNewsNotification {
setSmallIcon(
com.google.samples.apps.nowinandroid.core.common.R.drawable.core_common_ic_nia_notification,
val newsNotifications = truncatedNewsResources.map { newsResource ->
createNewsNotification {
setSmallIcon(
com.google.samples.apps.nowinandroid.core.common.R.drawable.core_common_ic_nia_notification,
)
.setContentTitle(newsResource.title)
.setContentText(newsResource.content)
.setContentIntent(newsPendingIntent(newsResource))
.setGroup(NEWS_NOTIFICATION_GROUP)
.setAutoCancel(true)
}
}
val summaryNotification = createNewsNotification {
val title = getString(
R.string.core_notifications_news_notification_group_summary,
truncatedNewsResources.size,
)
.setContentTitle(newsResource.title)
.setContentText(newsResource.content)
.setContentIntent(newsPendingIntent(newsResource))
setContentTitle(title)
.setContentText(title)
.setSmallIcon(
com.google.samples.apps.nowinandroid.core.common.R.drawable.core_common_ic_nia_notification,
)
// Build summary info into InboxStyle template.
.setStyle(newsNotificationStyle(truncatedNewsResources, title))
.setGroup(NEWS_NOTIFICATION_GROUP)
.setGroupSummary(true)
.setAutoCancel(true)
.build()
}
}
val summaryNotification = createNewsNotification {
val title = getString(
R.string.core_notifications_news_notification_group_summary,
truncatedNewsResources.size,
)
setContentTitle(title)
.setContentText(title)
.setSmallIcon(
com.google.samples.apps.nowinandroid.core.common.R.drawable.core_common_ic_nia_notification,
)
// Build summary info into InboxStyle template.
.setStyle(newsNotificationStyle(truncatedNewsResources, title))
.setGroup(NEWS_NOTIFICATION_GROUP)
.setGroupSummary(true)
.setAutoCancel(true)
.build()
}
// Send the notifications
val notificationManager = NotificationManagerCompat.from(this)
newsNotifications.forEachIndexed { index, notification ->
notificationManager.notify(
truncatedNewsResources[index].id.hashCode(),
notification,
)
// Send the notifications
val notificationManager = NotificationManagerCompat.from(this)
newsNotifications.forEachIndexed { index, notification ->
notificationManager.notify(
truncatedNewsResources[index].id.hashCode(),
notification,
)
}
notificationManager.notify(NEWS_NOTIFICATION_SUMMARY_ID, summaryNotification)
}
notificationManager.notify(NEWS_NOTIFICATION_SUMMARY_ID, summaryNotification)
}
/**

@ -19,7 +19,7 @@
package com.google.samples.apps.nowinandroid.core.testing.data
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import kotlinx.datetime.Instant
import kotlin.time.Instant
val newsResourcesTestData: List<NewsResource> = listOf(
NewsResource(

@ -23,10 +23,11 @@ import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand
import com.google.samples.apps.nowinandroid.core.model.data.UserData
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.Month
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlin.time.Instant
val userNewsResourcesTestData: List<UserNewsResource> = UserData(
bookmarkedNewsResources = setOf("1", "4"),
@ -47,8 +48,8 @@ val userNewsResourcesTestData: List<UserNewsResource> = UserData(
headerImageUrl = "https://developer.android.com/images/hero-assets/android-basics-compose.svg",
publishDate = LocalDateTime(
year = 2022,
monthNumber = 5,
dayOfMonth = 4,
month = Month.MAY,
day = 4,
hour = 23,
minute = 0,
second = 0,

@ -38,7 +38,7 @@ kotlin {
implementation(libs.coil.compose)
implementation(libs.jetbrains.compose.material3)
implementation(libs.jetbrains.compose.components.resources)
implementation(libs.jetbrains.compose.components.uiToolingPreview)
implementation(libs.jetbrains.compose.uiToolingPreview)
}
androidInstrumentedTest.dependencies {
implementation(projects.core.testing)

@ -67,7 +67,6 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import kotlinx.datetime.Instant
import nowinandroid.core.ui.generated.resources.Res
import nowinandroid.core.ui.generated.resources.core_ui_bookmark
import nowinandroid.core.ui.generated.resources.core_ui_card_meta_data_text
@ -79,6 +78,7 @@ import nowinandroid.core.ui.generated.resources.core_ui_topic_chip_content_descr
import nowinandroid.core.ui.generated.resources.core_ui_unbookmark
import nowinandroid.core.ui.generated.resources.core_ui_unread_resource_dot_content_description
import org.jetbrains.compose.resources.stringResource
import kotlin.time.Instant
/**
* [NewsResource] card used on the following screens: For You, Saved

@ -26,10 +26,11 @@ import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.model.data.UserData
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.newsResources
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.Month
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlin.time.Instant
/**
* This [PreviewParameterProvider](https://developer.android.com/reference/kotlin/androidx/compose/ui/tooling/preview/PreviewParameterProvider)
@ -89,8 +90,8 @@ object PreviewParameterData {
headerImageUrl = "https://developer.android.com/images/hero-assets/android-basics-compose.svg",
publishDate = LocalDateTime(
year = 2022,
monthNumber = 5,
dayOfMonth = 4,
month = Month.MAY,
day = 4,
hour = 23,
minute = 0,
second = 0,

@ -41,13 +41,13 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import kotlinx.datetime.Instant
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.time.Instant
/**
* To learn more about how this test handles Flows created with stateIn, see

@ -33,7 +33,6 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import kotlinx.datetime.Instant
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -41,6 +40,7 @@ import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlin.time.Instant
/**
* To learn more about how this test handles Flows created with stateIn, see

@ -31,7 +31,7 @@ dependencyGuard = "0.5.0"
jacoco = "0.8.12"
kotlin = "2.3.10"
kotlinxCoroutines = "1.10.2"
kotlinxDatetime = "0.6.1"
kotlinxDatetime = "0.7.1"
kotlinxSerializationJson = "1.8.0"
ksp = "2.3.5"
logback = "1.5.15"

Loading…
Cancel
Save