diff --git a/core/common/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt b/core/common/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/di/AndroidDispatchersComponent.kt similarity index 72% rename from core/common/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt rename to core/common/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/di/AndroidDispatchersComponent.kt index 6ae0c863d..0421b2b55 100644 --- a/core/common/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt +++ b/core/common/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/di/AndroidDispatchersComponent.kt @@ -16,15 +16,17 @@ package com.google.samples.apps.nowinandroid.core.di +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides @Component -actual abstract class DispatchersComponent { +abstract class AndroidDispatchersComponent : DispatchersComponent() { @Provides - actual fun providesIODispatcher(): IODispatcher = Dispatchers.IO + override fun providesIODispatcher(): @IoDispatcher CoroutineDispatcher = Dispatchers.IO @Provides - actual fun providesDefaultDispatcher(): DefaultDispatcher = Dispatchers.Default + override fun providesDefaultDispatcher(): @DefaultDispatcher CoroutineDispatcher = + Dispatchers.Default } diff --git a/core/common/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/di/CoroutineScopeComponent.kt b/core/common/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/di/CoroutineScopeComponent.kt index abe513134..a10a19c69 100644 --- a/core/common/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/di/CoroutineScopeComponent.kt +++ b/core/common/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/di/CoroutineScopeComponent.kt @@ -16,6 +16,7 @@ package com.google.samples.apps.nowinandroid.core.di +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import me.tatarka.inject.annotations.Component @@ -29,9 +30,12 @@ import me.tatarka.inject.annotations.Scope annotation class ApplicationScope @Component -abstract class CoroutineScopeComponent { +abstract class CoroutineScopeComponent( + @Component val dispatchersComponent: DispatchersComponent, +) { + @DefaultDispatcher abstract val defaultDispatcher: CoroutineDispatcher + @Provides - fun providesCoroutineScope( - dispatcher: DefaultDispatcher, - ): CoroutineScope = CoroutineScope(SupervisorJob() + dispatcher) + fun providesCoroutineScope(): CoroutineScope = + CoroutineScope(SupervisorJob() + defaultDispatcher) } diff --git a/core/common/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatcherQualifier.kt b/core/common/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatcherQualifier.kt new file mode 100644 index 000000000..23cb66353 --- /dev/null +++ b/core/common/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatcherQualifier.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.di + +import me.tatarka.inject.annotations.Qualifier + +@Qualifier +@Target( + AnnotationTarget.PROPERTY_GETTER, + AnnotationTarget.FUNCTION, + AnnotationTarget.VALUE_PARAMETER, + AnnotationTarget.TYPE, + AnnotationTarget.PROPERTY, +) +annotation class IoDispatcher + +@Qualifier +@Target( + AnnotationTarget.PROPERTY_GETTER, + AnnotationTarget.FUNCTION, + AnnotationTarget.VALUE_PARAMETER, + AnnotationTarget.TYPE, + AnnotationTarget.PROPERTY, +) +annotation class MainDispatcher + +@Qualifier +@Target( + AnnotationTarget.PROPERTY_GETTER, + AnnotationTarget.FUNCTION, + AnnotationTarget.VALUE_PARAMETER, + AnnotationTarget.TYPE, + AnnotationTarget.PROPERTY, +) +annotation class DefaultDispatcher diff --git a/core/common/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt b/core/common/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt index 3de817939..95cc4e8be 100644 --- a/core/common/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt +++ b/core/common/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt @@ -19,13 +19,10 @@ package com.google.samples.apps.nowinandroid.core.di import kotlinx.coroutines.CoroutineDispatcher import me.tatarka.inject.annotations.Provides -typealias DefaultDispatcher = CoroutineDispatcher -typealias IODispatcher = CoroutineDispatcher - -expect abstract class DispatchersComponent { +abstract class DispatchersComponent { @Provides - fun providesIODispatcher(): IODispatcher + abstract fun providesIODispatcher(): @IoDispatcher CoroutineDispatcher @Provides - fun providesDefaultDispatcher(): DefaultDispatcher + abstract fun providesDefaultDispatcher(): @DefaultDispatcher CoroutineDispatcher } diff --git a/core/common/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt b/core/common/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/di/JvmDispatchersComponent.kt similarity index 73% rename from core/common/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt rename to core/common/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/di/JvmDispatchersComponent.kt index 6ae0c863d..c6df8a244 100644 --- a/core/common/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt +++ b/core/common/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/di/JvmDispatchersComponent.kt @@ -16,15 +16,16 @@ package com.google.samples.apps.nowinandroid.core.di +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides @Component -actual abstract class DispatchersComponent { +abstract class JvmDispatchersComponent : DispatchersComponent() { @Provides - actual fun providesIODispatcher(): IODispatcher = Dispatchers.IO + override fun providesIODispatcher(): @IoDispatcher CoroutineDispatcher = Dispatchers.IO @Provides - actual fun providesDefaultDispatcher(): DefaultDispatcher = Dispatchers.Default + override fun providesDefaultDispatcher(): @DefaultDispatcher CoroutineDispatcher = Dispatchers.Default } diff --git a/core/common/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt b/core/common/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/di/NativeDispatchersComponent.kt similarity index 70% rename from core/common/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt rename to core/common/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/di/NativeDispatchersComponent.kt index e4edc24a7..16cfd5a91 100644 --- a/core/common/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt +++ b/core/common/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/di/NativeDispatchersComponent.kt @@ -16,17 +16,18 @@ package com.google.samples.apps.nowinandroid.core.di +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides @Component -actual abstract class DispatchersComponent { - - // TODO Provides an actual IODispatcher +abstract class NativeDispatchersComponent : DispatchersComponent() { @Provides - actual fun providesIODispatcher(): IODispatcher = Dispatchers.Default + override fun providesIODispatcher(): @IoDispatcher CoroutineDispatcher = Dispatchers.IO @Provides - actual fun providesDefaultDispatcher(): DefaultDispatcher = Dispatchers.Default + override fun providesDefaultDispatcher(): @DefaultDispatcher CoroutineDispatcher = + Dispatchers.Default } diff --git a/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/AndroidPlatformDependentDataModule.kt b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/AndroidPlatformDependentDataModule.kt new file mode 100644 index 000000000..b42830ae0 --- /dev/null +++ b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/AndroidPlatformDependentDataModule.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.data.di + +import android.app.Application +import com.google.samples.apps.nowinandroid.core.data.util.ConnectivityManagerNetworkMonitor +import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneBroadcastMonitor +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import com.google.samples.apps.nowinandroid.core.di.AndroidApplicationComponent +import com.google.samples.apps.nowinandroid.core.di.CoroutineScopeComponent +import com.google.samples.apps.nowinandroid.core.di.DispatchersComponent +import com.google.samples.apps.nowinandroid.core.di.IoDispatcher +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides + +@Component +abstract class AndroidPlatformDependentDataModule( + @Component val applicationComponent: AndroidApplicationComponent, + @Component val dispatchersComponent: DispatchersComponent, + @Component val coroutineScopeComponent: CoroutineScopeComponent, +) : PlatformDependentDataModule() { + abstract val application: Application + + @IoDispatcher abstract val ioDispatcher: CoroutineDispatcher + abstract val coroutineScope: CoroutineScope + + @Provides + override fun bindsNetworkMonitor(): NetworkMonitor { + return ConnectivityManagerNetworkMonitor( + application, + ioDispatcher, + ) + } + + @Provides + override fun bindsTimeZoneMonitor(): TimeZoneMonitor { + return TimeZoneBroadcastMonitor(application, coroutineScope, ioDispatcher) + } +} diff --git a/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.android.kt b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.android.kt deleted file mode 100644 index 87f01db64..000000000 --- a/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.android.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.data.di - -import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor -import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor -import me.tatarka.inject.annotations.Component -import me.tatarka.inject.annotations.Provides - -@Component -internal actual abstract class PlatformDependentDataModule { - @Provides - internal actual fun bindsNetworkMonitor(): NetworkMonitor { - TODO() - } - - @Provides - internal actual fun bindsTimeZoneMonitor(): TimeZoneMonitor { - TODO() - } -} diff --git a/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneBroadcastMonitor.kt b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneBroadcastMonitor.kt index 27fb21eda..615e803cb 100644 --- a/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneBroadcastMonitor.kt +++ b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneBroadcastMonitor.kt @@ -23,7 +23,7 @@ import android.content.IntentFilter import android.os.Build.VERSION import android.os.Build.VERSION_CODES import androidx.tracing.trace -import com.google.samples.apps.nowinandroid.core.di.IODispatcher +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.SharedFlow @@ -42,7 +42,7 @@ import java.time.ZoneId internal class TimeZoneBroadcastMonitor( private val context: Context, appScope: CoroutineScope, - private val ioDispatcher: IODispatcher, + private val ioDispatcher: CoroutineDispatcher, ) : TimeZoneMonitor { override val currentTimeZone: SharedFlow = diff --git a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt index bb29ee7a0..bad20ac1d 100644 --- a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt @@ -43,7 +43,7 @@ abstract class DataModule { ): NewsRepository = newsRepository @Provides - fun userDataRepository( + fun bindsUserDataRepository( userDataRepository: OfflineFirstUserDataRepository, ): UserDataRepository = userDataRepository diff --git a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.kt index d4c6409b7..3ffa58ecb 100644 --- a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.kt @@ -20,10 +20,10 @@ import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import me.tatarka.inject.annotations.Provides -internal expect abstract class PlatformDependentDataModule { +abstract class PlatformDependentDataModule { @Provides - internal fun bindsNetworkMonitor(): NetworkMonitor + abstract fun bindsNetworkMonitor(): NetworkMonitor @Provides - internal fun bindsTimeZoneMonitor(): TimeZoneMonitor + abstract fun bindsTimeZoneMonitor(): TimeZoneMonitor } diff --git a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt index 59293fa9d..979fd60ec 100644 --- a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt @@ -22,7 +22,7 @@ import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides @Component -internal abstract class UserNewsResourceRepositoryModule { +abstract class UserNewsResourceRepositoryModule { @Provides fun bindsUserNewsResourceRepository( userDataRepository: CompositeUserNewsResourceRepository, diff --git a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt index cfa582586..921b63dba 100644 --- a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt @@ -23,8 +23,9 @@ import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.database.model.asFtsEntity -import com.google.samples.apps.nowinandroid.core.di.IODispatcher +import com.google.samples.apps.nowinandroid.core.di.IoDispatcher import com.google.samples.apps.nowinandroid.core.model.data.SearchResult +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -42,7 +43,7 @@ class DefaultSearchContentsRepository( private val newsResourceFtsDao: NewsResourceFtsDao, private val topicDao: TopicDao, private val topicFtsDao: TopicFtsDao, - private val ioDispatcher: IODispatcher, + @IoDispatcher private val ioDispatcher: CoroutineDispatcher, ) : SearchContentsRepository { override suspend fun populateFtsData() { diff --git a/core/data/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.jvm.kt b/core/data/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/JvmPlatformDependentDataModule.kt similarity index 88% rename from core/data/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.jvm.kt rename to core/data/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/JvmPlatformDependentDataModule.kt index df3acd1a3..e7bce4e73 100644 --- a/core/data/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.jvm.kt +++ b/core/data/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/JvmPlatformDependentDataModule.kt @@ -29,9 +29,9 @@ import me.tatarka.inject.annotations.Provides * Leave empty for now */ @Component -internal actual abstract class PlatformDependentDataModule { +abstract class JvmPlatformDependentDataModule : PlatformDependentDataModule() { @Provides - internal actual fun bindsNetworkMonitor(): NetworkMonitor { + override fun bindsNetworkMonitor(): NetworkMonitor { return object : NetworkMonitor { override val isOnline: Flow get() = flowOf(true) @@ -39,7 +39,7 @@ internal actual abstract class PlatformDependentDataModule { } @Provides - internal actual fun bindsTimeZoneMonitor(): TimeZoneMonitor { + override fun bindsTimeZoneMonitor(): TimeZoneMonitor { return object : TimeZoneMonitor { override val currentTimeZone: Flow get() = flowOf(TimeZone.UTC) diff --git a/core/data/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.native.kt b/core/data/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/NativePlatformDependentDataModule.kt similarity index 87% rename from core/data/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.native.kt rename to core/data/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/NativePlatformDependentDataModule.kt index 1600ad86b..95cc466b3 100644 --- a/core/data/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.native.kt +++ b/core/data/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/NativePlatformDependentDataModule.kt @@ -29,9 +29,9 @@ import me.tatarka.inject.annotations.Provides * Leave empty for now */ @Component -internal actual abstract class PlatformDependentDataModule { +abstract class NativePlatformDependentDataModule : PlatformDependentDataModule() { @Provides - internal actual fun bindsNetworkMonitor(): NetworkMonitor { + override fun bindsNetworkMonitor(): NetworkMonitor { return object : NetworkMonitor { override val isOnline: Flow get() = flowOf(true) @@ -39,7 +39,7 @@ internal actual abstract class PlatformDependentDataModule { } @Provides - internal actual fun bindsTimeZoneMonitor(): TimeZoneMonitor { + override fun bindsTimeZoneMonitor(): TimeZoneMonitor { return object : TimeZoneMonitor { override val currentTimeZone: Flow get() = flowOf(TimeZone.UTC) diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DatabaseModule.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DatabaseModule.kt index 10845d248..d202e5229 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DatabaseModule.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DatabaseModule.kt @@ -23,42 +23,41 @@ import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceFtsDao import com.google.samples.apps.nowinandroid.core.database.dao.RecentSearchQueryDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao -import com.google.samples.apps.nowinandroid.core.di.IODispatcher +import com.google.samples.apps.nowinandroid.core.di.DispatchersComponent +import com.google.samples.apps.nowinandroid.core.di.IoDispatcher +import kotlinx.coroutines.CoroutineDispatcher import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides @Component -internal abstract class DatabaseModule { +abstract class DatabaseModule(@Component val dispatchersComponent: DispatchersComponent) { + @IoDispatcher abstract val dispatcher: CoroutineDispatcher + @Provides fun providesNiaDatabase(driver: SqlDriver): NiaDatabase = NiaDatabase(driver) @Provides fun providesTopicsDao( database: NiaDatabase, - dispatcher: IODispatcher, ): TopicDao = TopicDao(database, dispatcher) @Provides fun providesNewsResourceDao( database: NiaDatabase, - dispatcher: IODispatcher, ): NewsResourceDao = NewsResourceDao(database, dispatcher) @Provides fun providesTopicFtsDao( database: NiaDatabase, - dispatcher: IODispatcher, ): TopicFtsDao = TopicFtsDao(database, dispatcher) @Provides fun providesNewsResourceFtsDao( database: NiaDatabase, - dispatcher: IODispatcher, ): NewsResourceFtsDao = NewsResourceFtsDao(database, dispatcher) @Provides fun providesRecentSearchQueryDao( database: NiaDatabase, - dispatcher: IODispatcher, ): RecentSearchQueryDao = RecentSearchQueryDao(database, dispatcher) } diff --git a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt b/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt index 9cc91745a..d7248081c 100644 --- a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt +++ b/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt @@ -15,8 +15,7 @@ */ package com.google.samples.apps.nowinandroid.core.datastore - -import com.google.samples.apps.nowinandroid.core.di.IODispatcher +import com.google.samples.apps.nowinandroid.core.di.IoDispatcher import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.data.UserData @@ -25,6 +24,7 @@ import com.russhwolf.settings.Settings import com.russhwolf.settings.serialization.decodeValue import com.russhwolf.settings.serialization.decodeValueOrNull import com.russhwolf.settings.serialization.encodeValue +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map @@ -38,7 +38,7 @@ private const val USER_DATA_KEY = "userData" @Inject class NiaPreferencesDataSource( private val settings: Settings, - private val dispatcher: IODispatcher, + @IoDispatcher private val dispatcher: CoroutineDispatcher, ) { // FlowSettings did not support JS, use a workaround instead // https://github.com/russhwolf/multiplatform-settings/issues/139 diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSource.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSource.kt index 78a71a68a..379ebaf16 100644 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSource.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSource.kt @@ -16,13 +16,14 @@ package com.google.samples.apps.nowinandroid.core.network.demo -import com.google.samples.apps.nowinandroid.core.di.IODispatcher +import com.google.samples.apps.nowinandroid.core.di.IoDispatcher import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.assets.NEWS_DATA import com.google.samples.apps.nowinandroid.core.network.assets.TOPICS_DATA import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import me.tatarka.inject.annotations.Inject @@ -31,7 +32,7 @@ import me.tatarka.inject.annotations.Inject * [NiaNetworkDataSource] implementation that provides static news resources to aid development */ class DemoNiaNetworkDataSource @Inject constructor( - private val ioDispatcher: IODispatcher, + @IoDispatcher private val ioDispatcher: CoroutineDispatcher, private val networkJson: Json, ) : NiaNetworkDataSource { diff --git a/feature/bookmarks/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/di/BookmarkComponent.kt b/feature/bookmarks/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/di/BookmarkComponent.kt deleted file mode 100644 index 68853dc22..000000000 --- a/feature/bookmarks/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/di/BookmarkComponent.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.feature.bookmarks.di - -import com.google.samples.apps.nowinandroid.feature.bookmarks.BookmarksViewModel -import me.tatarka.inject.annotations.Component - -@Component -abstract class BookmarkComponent { - abstract val viewModel: BookmarksViewModel -} diff --git a/feature/bookmarks/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/navigation/BookmarksNavigation.kt b/feature/bookmarks/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/navigation/BookmarksNavigation.kt index 64cf40df7..faed38e90 100644 --- a/feature/bookmarks/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/navigation/BookmarksNavigation.kt +++ b/feature/bookmarks/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/navigation/BookmarksNavigation.kt @@ -19,9 +19,6 @@ package com.google.samples.apps.nowinandroid.feature.bookmarks.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions -import androidx.navigation.compose.composable -import com.google.samples.apps.nowinandroid.feature.bookmarks.BookmarksRoute -import com.google.samples.apps.nowinandroid.feature.bookmarks.di.BookmarkComponent const val BOOKMARKS_ROUTE = "bookmarks_route" @@ -31,8 +28,7 @@ fun NavGraphBuilder.bookmarksScreen( onTopicClick: (String) -> Unit, onShowSnackbar: suspend (String, String?) -> Boolean, ) { - val viewModel = BookmarkComponent::class.create().viewModel - composable(route = BOOKMARKS_ROUTE) { - BookmarksRoute(onTopicClick, onShowSnackbar, viewModel) - } +// composable(route = BOOKMARKS_ROUTE) { +// BookmarksRoute(onTopicClick, onShowSnackbar, viewModel) +// } } diff --git a/feature/bookmarks/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt b/feature/bookmarks/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt index 037e9db64..9b8010625 100644 --- a/feature/bookmarks/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt +++ b/feature/bookmarks/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt @@ -20,16 +20,15 @@ import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNe import com.google.samples.apps.nowinandroid.core.testing.data.newsResourcesTestData import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository -import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import kotlin.test.BeforeTest +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs @@ -38,9 +37,6 @@ import kotlin.test.assertIs * https://developer.android.com/kotlin/flow/test#statein */ class BookmarksViewModelTest { - @get:Rule - val dispatcherRule = MainDispatcherRule() - private val userDataRepository = TestUserDataRepository() private val newsRepository = TestNewsRepository() private val userNewsResourceRepository = CompositeUserNewsResourceRepository( @@ -49,7 +45,7 @@ class BookmarksViewModelTest { ) private lateinit var viewModel: BookmarksViewModel - @Before + @BeforeTest fun setup() { viewModel = BookmarksViewModel( userDataRepository = userDataRepository, @@ -75,6 +71,7 @@ class BookmarksViewModelTest { collectJob.cancel() } + @OptIn(ExperimentalCoroutinesApi::class) @Test fun oneBookmark_whenRemoving_removesFromFeed() = runTest { val collectJob = launch(UnconfinedTestDispatcher()) { viewModel.feedUiState.collect() } diff --git a/feature/foryou/build.gradle.kts b/feature/foryou/build.gradle.kts index fd41d9a13..1ac58a500 100644 --- a/feature/foryou/build.gradle.kts +++ b/feature/foryou/build.gradle.kts @@ -15,8 +15,9 @@ */ plugins { - alias(libs.plugins.nowinandroid.android.feature) - alias(libs.plugins.nowinandroid.android.library.compose) + alias(libs.plugins.nowinandroid.cmp.feature) + alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.compose) alias(libs.plugins.nowinandroid.android.library.jacoco) alias(libs.plugins.roborazzi) } @@ -25,16 +26,32 @@ android { namespace = "com.google.samples.apps.nowinandroid.feature.foryou" } -dependencies { - implementation(libs.accompanist.permissions) - implementation(projects.core.data) - implementation(projects.core.domain) - - testImplementation(libs.hilt.android.testing) - testImplementation(libs.robolectric) - testImplementation(projects.core.testing) - testImplementation(projects.core.screenshotTesting) - testDemoImplementation(libs.roborazzi) - - androidTestImplementation(projects.core.testing) +kotlin { + sourceSets { + commonMain.dependencies { + implementation(projects.core.data) + implementation(projects.core.domain) + implementation(compose.material3) + implementation(compose.foundation) + implementation(compose.ui) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) + implementation(libs.coil) + implementation(libs.coil.compose) + } + androidMain.dependencies { + implementation(libs.accompanist.permissions) + } + commonMain.dependencies { + implementation(projects.core.testing) + } + androidUnitTest.dependencies { + implementation(libs.robolectric) + implementation(libs.roborazzi) + implementation(projects.core.screenshotTesting) + } + androidInstrumentedTest.dependencies { + implementation(projects.core.testing) + } + } } diff --git a/feature/foryou/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt b/feature/foryou/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt similarity index 100% rename from feature/foryou/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt rename to feature/foryou/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt diff --git a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt b/feature/foryou/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt similarity index 100% rename from feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt rename to feature/foryou/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt diff --git a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt b/feature/foryou/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt similarity index 100% rename from feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt rename to feature/foryou/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt diff --git a/feature/foryou/src/test/screenshots/ForYouScreenLoading_foldable.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_foldable.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenLoading_foldable.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_foldable.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenLoading_phone.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_phone.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenLoading_phone.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_phone.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenLoading_tablet.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_tablet.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenLoading_tablet.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_tablet.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_foldable.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_foldable.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_foldable.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_foldable.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_phone.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_phone.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_tablet.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_tablet.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_tablet.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_tablet.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_foldable.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_foldable.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_foldable.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_foldable.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_phone.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_phone.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_phone.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_phone.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_tablet.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_tablet.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_tablet.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_tablet.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_foldable.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_foldable.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_foldable.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_foldable.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_phone.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_phone.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone_dark.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_phone_dark.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone_dark.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_phone_dark.png diff --git a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_tablet.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_tablet.png similarity index 100% rename from feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_tablet.png rename to feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_tablet.png diff --git a/feature/foryou/src/main/res/drawable/feature_foryou_ic_icon_placeholder.xml b/feature/foryou/src/commonMain/composeResources/drawable/feature_foryou_ic_icon_placeholder.xml similarity index 100% rename from feature/foryou/src/main/res/drawable/feature_foryou_ic_icon_placeholder.xml rename to feature/foryou/src/commonMain/composeResources/drawable/feature_foryou_ic_icon_placeholder.xml diff --git a/feature/foryou/src/main/res/values/strings.xml b/feature/foryou/src/commonMain/composeResources/values/strings.xml similarity index 100% rename from feature/foryou/src/main/res/values/strings.xml rename to feature/foryou/src/commonMain/composeResources/values/strings.xml diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt b/feature/foryou/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt similarity index 87% rename from feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt rename to feature/foryou/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt index 885020636..c43351862 100644 --- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt +++ b/feature/foryou/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt @@ -16,10 +16,6 @@ package com.google.samples.apps.nowinandroid.feature.foryou -import android.net.Uri -import android.os.Build.VERSION -import android.os.Build.VERSION_CODES -import androidx.activity.compose.ReportDrawnWhen import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut @@ -63,29 +59,18 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.layout.layout -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.max import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.tracing.trace -import com.google.accompanist.permissions.ExperimentalPermissionsApi -import com.google.accompanist.permissions.PermissionStatus.Denied -import com.google.accompanist.permissions.rememberPermissionState +import coil3.ImageLoader +import coil3.compose.LocalPlatformContext import com.google.samples.apps.nowinandroid.core.designsystem.component.DynamicAsyncImage import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton @@ -100,16 +85,22 @@ import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent -import com.google.samples.apps.nowinandroid.core.ui.TrackScrollJank import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider -import com.google.samples.apps.nowinandroid.core.ui.launchCustomChromeTab +import com.google.samples.apps.nowinandroid.core.ui.collectAsStateWithLifecycle import com.google.samples.apps.nowinandroid.core.ui.newsFeed +import nowinandroid.feature.foryou.generated.resources.Res +import nowinandroid.feature.foryou.generated.resources.feature_foryou_done +import nowinandroid.feature.foryou.generated.resources.feature_foryou_loading +import nowinandroid.feature.foryou.generated.resources.feature_foryou_onboarding_guidance_subtitle +import nowinandroid.feature.foryou.generated.resources.feature_foryou_onboarding_guidance_title +import org.jetbrains.compose.resources.stringResource +import org.jetbrains.compose.ui.tooling.preview.PreviewParameter @Composable internal fun ForYouRoute( onTopicClick: (String) -> Unit, modifier: Modifier = Modifier, - viewModel: ForYouViewModel = hiltViewModel(), + viewModel: ForYouViewModel, ) { val onboardingUiState by viewModel.onboardingUiState.collectAsStateWithLifecycle() val feedState by viewModel.feedState.collectAsStateWithLifecycle() @@ -148,8 +139,8 @@ internal fun ForYouScreen( val isOnboardingLoading = onboardingUiState is OnboardingUiState.Loading val isFeedLoading = feedState is NewsFeedUiState.Loading - // This code should be called when the UI is ready for use and relates to Time To Full Display. - ReportDrawnWhen { !isSyncing && !isOnboardingLoading && !isFeedLoading } +// // This code should be called when the UI is ready for use and relates to Time To Full Display. +// ReportDrawnWhen { !isSyncing && !isOnboardingLoading && !isFeedLoading } val itemsAvailable = feedItemsSize(feedState, onboardingUiState) @@ -157,7 +148,7 @@ internal fun ForYouScreen( val scrollbarState = state.scrollbarState( itemsAvailable = itemsAvailable, ) - TrackScrollJank(scrollableState = state, stateName = "forYou:feed") +// TrackScrollJank(scrollableState = state, stateName = "forYou:feed") Box( modifier = modifier @@ -216,7 +207,7 @@ internal fun ForYouScreen( targetOffsetY = { fullHeight -> -fullHeight }, ) + fadeOut(), ) { - val loadingContentDescription = stringResource(id = R.string.feature_foryou_loading) + val loadingContentDescription = stringResource(Res.string.feature_foryou_loading) Box( modifier = Modifier .fillMaxWidth() @@ -243,7 +234,7 @@ internal fun ForYouScreen( ) } TrackScreenViewEvent(screenName = "ForYou") - NotificationPermissionEffect() +// NotificationPermissionEffect() DeepLinkEffect( deepLinkedUserNewsResource, onDeepLinkOpened, @@ -271,7 +262,7 @@ private fun LazyStaggeredGridScope.onboarding( item(span = StaggeredGridItemSpan.FullLine, contentType = "onboarding") { Column(modifier = interestsItemModifier) { Text( - text = stringResource(R.string.feature_foryou_onboarding_guidance_title), + text = stringResource(Res.string.feature_foryou_onboarding_guidance_title), textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() @@ -279,7 +270,7 @@ private fun LazyStaggeredGridScope.onboarding( style = MaterialTheme.typography.titleMedium, ) Text( - text = stringResource(R.string.feature_foryou_onboarding_guidance_subtitle), + text = stringResource(Res.string.feature_foryou_onboarding_guidance_subtitle), modifier = Modifier .fillMaxWidth() .padding(top = 8.dp, start = 24.dp, end = 24.dp), @@ -305,7 +296,7 @@ private fun LazyStaggeredGridScope.onboarding( .fillMaxWidth(), ) { Text( - text = stringResource(R.string.feature_foryou_done), + text = stringResource(Res.string.feature_foryou_done), ) } } @@ -320,11 +311,11 @@ private fun TopicSelection( onboardingUiState: OnboardingUiState.Shown, onTopicCheckedChanged: (String, Boolean) -> Unit, modifier: Modifier = Modifier, -) = trace("TopicSelection") { +) { val lazyGridState = rememberLazyGridState() val topicSelectionTestTag = "forYou:topicSelection" - TrackScrollJank(scrollableState = lazyGridState, stateName = topicSelectionTestTag) +// TrackScrollJank(scrollableState = lazyGridState, stateName = topicSelectionTestTag) Box( modifier = modifier @@ -381,7 +372,7 @@ private fun SingleTopicButton( imageUrl: String, isSelected: Boolean, onClick: (String, Boolean) -> Unit, -) = trace("SingleTopicButton") { +) { Surface( modifier = Modifier .width(312.dp) @@ -434,52 +425,53 @@ fun TopicIcon( modifier: Modifier = Modifier, ) { DynamicAsyncImage( - placeholder = painterResource(R.drawable.feature_foryou_ic_icon_placeholder), +// placeholder = painterResource(R.drawable.feature_foryou_ic_icon_placeholder), imageUrl = imageUrl, // decorative contentDescription = null, modifier = modifier .padding(10.dp) .size(32.dp), + imageLoader = ImageLoader(LocalPlatformContext.current), ) } -@Composable -@OptIn(ExperimentalPermissionsApi::class) -private fun NotificationPermissionEffect() { - // Permission requests should only be made from an Activity Context, which is not present - // in previews - if (LocalInspectionMode.current) return - if (VERSION.SDK_INT < VERSION_CODES.TIRAMISU) return - val notificationsPermissionState = rememberPermissionState( - android.Manifest.permission.POST_NOTIFICATIONS, - ) - LaunchedEffect(notificationsPermissionState) { - val status = notificationsPermissionState.status - if (status is Denied && !status.shouldShowRationale) { - notificationsPermissionState.launchPermissionRequest() - } - } -} +// @Composable +// @OptIn(ExperimentalPermissionsApi::class) +// private fun NotificationPermissionEffect() { +// // Permission requests should only be made from an Activity Context, which is not present +// // in previews +// if (LocalInspectionMode.current) return +// if (VERSION.SDK_INT < VERSION_CODES.TIRAMISU) return +// val notificationsPermissionState = rememberPermissionState( +// android.Manifest.permission.POST_NOTIFICATIONS, +// ) +// LaunchedEffect(notificationsPermissionState) { +// val status = notificationsPermissionState.status +// if (status is Denied && !status.shouldShowRationale) { +// notificationsPermissionState.launchPermissionRequest() +// } +// } +// } @Composable private fun DeepLinkEffect( userNewsResource: UserNewsResource?, onDeepLinkOpened: (String) -> Unit, ) { - val context = LocalContext.current - val backgroundColor = MaterialTheme.colorScheme.background.toArgb() - - LaunchedEffect(userNewsResource) { - if (userNewsResource == null) return@LaunchedEffect - if (!userNewsResource.hasBeenViewed) onDeepLinkOpened(userNewsResource.id) - - launchCustomChromeTab( - context = context, - uri = Uri.parse(userNewsResource.url), - toolbarColor = backgroundColor, - ) - } +// val context = LocalContext.current +// val backgroundColor = MaterialTheme.colorScheme.background.toArgb() +// +// LaunchedEffect(userNewsResource) { +// if (userNewsResource == null) return@LaunchedEffect +// if (!userNewsResource.hasBeenViewed) onDeepLinkOpened(userNewsResource.id) +// +// launchCustomChromeTab( +// context = context, +// uri = Uri.parse(userNewsResource.url), +// toolbarColor = backgroundColor, +// ) +// } } private fun feedItemsSize( diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt b/feature/foryou/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt similarity index 97% rename from feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt rename to feature/foryou/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt index 85035a77a..d668b2509 100644 --- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt +++ b/feature/foryou/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt @@ -29,7 +29,7 @@ import com.google.samples.apps.nowinandroid.core.data.util.SyncManager import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.feature.foryou.navigation.LINKED_NEWS_RESOURCE_ID -import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -39,9 +39,8 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import javax.inject.Inject +import me.tatarka.inject.annotations.Inject -@HiltViewModel class ForYouViewModel @Inject constructor( private val savedStateHandle: SavedStateHandle, syncManager: SyncManager, @@ -54,6 +53,7 @@ class ForYouViewModel @Inject constructor( private val shouldShowOnboarding: Flow = userDataRepository.userData.map { !it.shouldHideOnboarding } + @OptIn(ExperimentalCoroutinesApi::class) val deepLinkedNewsResource = savedStateHandle.getStateFlow( key = LINKED_NEWS_RESOURCE_ID, null, diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/OnboardingUiState.kt b/feature/foryou/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/OnboardingUiState.kt similarity index 100% rename from feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/OnboardingUiState.kt rename to feature/foryou/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/OnboardingUiState.kt diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt b/feature/foryou/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt similarity index 88% rename from feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt rename to feature/foryou/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt index 8e94a491a..5f77351f8 100644 --- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt +++ b/feature/foryou/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt @@ -22,8 +22,6 @@ import androidx.navigation.NavOptions import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.navArgument -import androidx.navigation.navDeepLink -import com.google.samples.apps.nowinandroid.feature.foryou.ForYouRoute const val LINKED_NEWS_RESOURCE_ID = "linkedNewsResourceId" const val FOR_YOU_ROUTE = "for_you_route/{$LINKED_NEWS_RESOURCE_ID}" @@ -36,12 +34,12 @@ fun NavGraphBuilder.forYouScreen(onTopicClick: (String) -> Unit) { composable( route = FOR_YOU_ROUTE, deepLinks = listOf( - navDeepLink { uriPattern = DEEP_LINK_URI_PATTERN }, +// navDeepLink { uriPattern = DEEP_LINK_URI_PATTERN }, ), arguments = listOf( navArgument(LINKED_NEWS_RESOURCE_ID) { type = NavType.StringType }, ), ) { - ForYouRoute(onTopicClick) +// ForYouRoute(onTopicClick) } } diff --git a/feature/foryou/src/main/AndroidManifest.xml b/feature/foryou/src/main/AndroidManifest.xml deleted file mode 100644 index 51d0cfc2e..000000000 --- a/feature/foryou/src/main/AndroidManifest.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - \ No newline at end of file