diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ed5fa237d2..28ec373b73 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,10 +1,19 @@ -Thanks for submitting a pull request. Please include the following information. +_Thanks for submitting a pull request. Please include the following information._ **What I have done and why** -Include a summary of what your pull request contains, and why you have made these changes. + +_Include a summary of what your pull request contains, and why you have made these changes._ Fixes # +**How I'm testing it** + +_Choose at least one:_ +- Unit tests +- UI tests +- Screenshot tests +- N/A _(provide justification)_ + **Do tests pass?** - [ ] Run local tests on `DemoDebug` variant: `./gradlew testDemoDebug` - [ ] Check formatting: `./gradlew --init-script gradle/init.gradle.kts spotlessApply` diff --git a/app-nia-catalog/build.gradle.kts b/app-nia-catalog/build.gradle.kts index 0991ea0c58..94d55b81cf 100644 --- a/app-nia-catalog/build.gradle.kts +++ b/app-nia-catalog/build.gradle.kts @@ -59,7 +59,7 @@ android { // To publish on the Play store a private signing key is required, but to allow anyone // who clones the code to sign and run the release variant, use the debug signing key. // TODO: Abstract the signing configuration to a separate file to avoid hardcoding this. - signingConfig = signingConfigs.getByName("debug") + signingConfig = signingConfigs.named("debug").get() } } } diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6908104a76..d3a1098dea 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -52,7 +52,7 @@ android { // To publish on the Play store a private signing key is required, but to allow anyone // who clones the code to sign and run the release variant, use the debug signing key. // TODO: Abstract the signing configuration to a separate file to avoid hardcoding this. - signingConfig = signingConfigs.getByName("debug") + signingConfig = signingConfigs.named("debug").get() // Ensure Baseline Profile is fresh for release builds. baselineProfile.automaticGenerationDuringBuild = true } diff --git a/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt index 29d31f9e60..dbca79a5ef 100644 --- a/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt @@ -15,6 +15,7 @@ */ import androidx.room.gradle.RoomExtension +import com.google.devtools.ksp.gradle.KspExtension import com.google.samples.apps.nowinandroid.libs import org.gradle.api.Plugin import org.gradle.api.Project @@ -28,6 +29,10 @@ class AndroidRoomConventionPlugin : Plugin { pluginManager.apply("androidx.room") pluginManager.apply("com.google.devtools.ksp") + extensions.configure { + arg("room.generateKotlin", "true") + } + extensions.configure { // The schemas directory contains a schema file for each version of the Room database. // This is required to enable Room auto migrations. diff --git a/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt b/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DriverModule.kt similarity index 83% rename from core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt rename to core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DriverModule.kt index eb2c9ec5a9..b763979198 100644 --- a/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt +++ b/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DriverModule.kt @@ -14,15 +14,16 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.database +package com.google.samples.apps.nowinandroid.core.database.di import android.content.Context import androidx.sqlite.db.SupportSQLiteDatabase import app.cash.sqldelight.async.coroutines.synchronous -import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.QueryResult.AsyncValue import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.android.AndroidSqliteDriver +import app.cash.sqldelight.driver.android.AndroidSqliteDriver.Callback import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides @@ -31,18 +32,18 @@ internal actual abstract class DriverModule(private val context: Context) { @Provides actual suspend fun provideDbDriver( - schema: SqlSchema>, + schema: SqlSchema>, ): SqlDriver { val synchronousSchema = schema.synchronous() return AndroidSqliteDriver( schema = synchronousSchema, context = context, name = "nia-database.db", - callback = object : AndroidSqliteDriver.Callback(synchronousSchema) { + callback = object : Callback(synchronousSchema) { override fun onOpen(db: SupportSQLiteDatabase) { db.setForeignKeyConstraintsEnabled(true) } }, ) } -} +} \ No newline at end of file diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DatabaseModule.kt similarity index 92% rename from core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DatabaseModule.kt index ba69cfc038..10845d248d 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DatabaseModule.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * 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. @@ -14,9 +14,10 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.database +package com.google.samples.apps.nowinandroid.core.database.di import app.cash.sqldelight.db.SqlDriver +import com.google.samples.apps.nowinandroid.core.database.NiaDatabase import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceFtsDao import com.google.samples.apps.nowinandroid.core.database.dao.RecentSearchQueryDao diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DriverModule.kt similarity index 84% rename from core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DriverModule.kt index 4d79ddaf15..c52af38629 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DriverModule.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.database +package com.google.samples.apps.nowinandroid.core.database.di -import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.QueryResult.AsyncValue import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import me.tatarka.inject.annotations.Provides @@ -24,6 +24,6 @@ import me.tatarka.inject.annotations.Provides internal expect abstract class DriverModule { @Provides suspend fun provideDbDriver( - schema: SqlSchema>, + schema: SqlSchema>, ): SqlDriver -} +} \ No newline at end of file diff --git a/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt b/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DriverModule.kt similarity index 84% rename from core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt rename to core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DriverModule.kt index 220e575d56..b09da154d3 100644 --- a/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt +++ b/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DriverModule.kt @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.database +package com.google.samples.apps.nowinandroid.core.database.di -import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.QueryResult.AsyncValue import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver +import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver.Companion import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides import java.util.Properties @@ -28,7 +29,7 @@ import java.util.Properties internal actual abstract class DriverModule { @Provides actual suspend fun provideDbDriver( - schema: SqlSchema>, + schema: SqlSchema>, ): SqlDriver { return JdbcSqliteDriver( url = JdbcSqliteDriver.IN_MEMORY, @@ -36,4 +37,4 @@ internal actual abstract class DriverModule { ) .also { schema.create(it).await() } } -} +} \ No newline at end of file diff --git a/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt b/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DriverModule.kt similarity index 82% rename from core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt rename to core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DriverModule.kt index 3c4fce8bac..5986a5a6df 100644 --- a/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt +++ b/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DriverModule.kt @@ -14,14 +14,15 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.database +package com.google.samples.apps.nowinandroid.core.database.di import app.cash.sqldelight.async.coroutines.synchronous -import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.QueryResult.AsyncValue import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.native.NativeSqliteDriver import co.touchlab.sqliter.DatabaseConfiguration +import co.touchlab.sqliter.DatabaseConfiguration.Extended import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides @@ -30,7 +31,7 @@ internal actual abstract class DriverModule { @Provides actual suspend fun provideDbDriver( - schema: SqlSchema>, + schema: SqlSchema>, ): SqlDriver { val synchronousSchema = schema.synchronous() return NativeSqliteDriver( @@ -38,9 +39,9 @@ internal actual abstract class DriverModule { name = "nia-database.db", onConfiguration = { config: DatabaseConfiguration -> config.copy( - extendedConfig = DatabaseConfiguration.Extended(foreignKeyConstraints = true), + extendedConfig = Extended(foreignKeyConstraints = true), ) }, ) } -} +} \ No newline at end of file diff --git a/core/domain/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetSearchContentsCountUseCase.kt b/core/domain/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetSearchContentsCountUseCase.kt deleted file mode 100644 index b8c2fbf3d3..0000000000 --- a/core/domain/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetSearchContentsCountUseCase.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2023 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.domain - -import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsRepository -import kotlinx.coroutines.flow.Flow -import me.tatarka.inject.annotations.Inject - -/** - * A use case which returns total count of *Fts tables - */ -@Inject -class GetSearchContentsCountUseCase( - private val searchContentsRepository: SearchContentsRepository, -) { - operator fun invoke(): Flow = - searchContentsRepository.getSearchContentsCount() -} diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt index ad6ca61123..6c2af240cb 100644 --- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt +++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt @@ -23,9 +23,9 @@ import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent.Param import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository +import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.GetRecentSearchQueriesUseCase -import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsCountUseCase 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 @@ -42,8 +42,8 @@ import javax.inject.Inject @HiltViewModel class SearchViewModel @Inject constructor( getSearchContentsUseCase: GetSearchContentsUseCase, - getSearchContentsCountUseCase: GetSearchContentsCountUseCase, recentSearchQueriesUseCase: GetRecentSearchQueriesUseCase, + private val searchContentsRepository: SearchContentsRepository, private val recentSearchRepository: RecentSearchRepository, private val userDataRepository: UserDataRepository, private val savedStateHandle: SavedStateHandle, @@ -53,7 +53,7 @@ class SearchViewModel @Inject constructor( val searchQuery = savedStateHandle.getStateFlow(key = SEARCH_QUERY, initialValue = "") val searchResultUiState: StateFlow = - getSearchContentsCountUseCase() + searchContentsRepository.getSearchContentsCount() .flatMapLatest { totalCount -> if (totalCount < SEARCH_MIN_FTS_ENTITY_COUNT) { flowOf(SearchResultUiState.SearchNotReady) diff --git a/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt b/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt index 662afca7d6..c832401deb 100644 --- a/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt +++ b/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt @@ -19,7 +19,6 @@ package com.google.samples.apps.nowinandroid.feature.search import androidx.lifecycle.SavedStateHandle import com.google.samples.apps.nowinandroid.core.analytics.NoOpAnalyticsHelper import com.google.samples.apps.nowinandroid.core.domain.GetRecentSearchQueriesUseCase -import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsCountUseCase import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase import com.google.samples.apps.nowinandroid.core.testing.data.newsResourcesTestData import com.google.samples.apps.nowinandroid.core.testing.data.topicsTestData @@ -60,7 +59,6 @@ class SearchViewModelTest { ) private val recentSearchRepository = TestRecentSearchRepository() private val getRecentQueryUseCase = GetRecentSearchQueriesUseCase(recentSearchRepository) - private val getSearchContentsCountUseCase = GetSearchContentsCountUseCase(searchContentsRepository) private lateinit var viewModel: SearchViewModel @@ -68,8 +66,8 @@ class SearchViewModelTest { fun setup() { viewModel = SearchViewModel( getSearchContentsUseCase = getSearchContentsUseCase, - getSearchContentsCountUseCase = getSearchContentsCountUseCase, recentSearchQueriesUseCase = getRecentQueryUseCase, + searchContentsRepository = searchContentsRepository, savedStateHandle = SavedStateHandle(), recentSearchRepository = recentSearchRepository, userDataRepository = userDataRepository, @@ -87,7 +85,8 @@ class SearchViewModelTest { fun stateIsEmptyQuery_withEmptySearchQuery() = runTest { searchContentsRepository.addNewsResources(newsResourcesTestData) searchContentsRepository.addTopics(topicsTestData) - val collectJob = launch(UnconfinedTestDispatcher()) { viewModel.searchResultUiState.collect() } + val collectJob = + launch(UnconfinedTestDispatcher()) { viewModel.searchResultUiState.collect() } viewModel.onSearchQueryChanged("") @@ -98,7 +97,8 @@ class SearchViewModelTest { @Test fun emptyResultIsReturned_withNotMatchingQuery() = runTest { - val collectJob = launch(UnconfinedTestDispatcher()) { viewModel.searchResultUiState.collect() } + val collectJob = + launch(UnconfinedTestDispatcher()) { viewModel.searchResultUiState.collect() } viewModel.onSearchQueryChanged("XXX") searchContentsRepository.addNewsResources(newsResourcesTestData) @@ -112,7 +112,8 @@ class SearchViewModelTest { @Test fun recentSearches_verifyUiStateIsSuccess() = runTest { - val collectJob = launch(UnconfinedTestDispatcher()) { viewModel.recentSearchQueriesUiState.collect() } + val collectJob = + launch(UnconfinedTestDispatcher()) { viewModel.recentSearchQueriesUiState.collect() } viewModel.onSearchTriggered("kotlin") val result = viewModel.recentSearchQueriesUiState.value @@ -123,7 +124,8 @@ class SearchViewModelTest { @Test fun searchNotReady_withNoFtsTableEntity() = runTest { - val collectJob = launch(UnconfinedTestDispatcher()) { viewModel.searchResultUiState.collect() } + val collectJob = + launch(UnconfinedTestDispatcher()) { viewModel.searchResultUiState.collect() } viewModel.onSearchQueryChanged("")