Merge remote-tracking branch 'github/main'

Change-Id: Ib23a555860e276e62a369b90c087d434d38a44cb
pull/127/head
Jose Alcérreca 3 years ago
commit 9dcc43c12c

@ -68,7 +68,7 @@ jobs:
androidTest:
needs: build
runs-on: macOS-latest # enables hardware acceleration in the virtual machine
timeout-minutes: 30
timeout-minutes: 45
strategy:
matrix:
api-level: [23, 26, 30]

@ -0,0 +1,6 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="Copyright &amp;#36;today.year The Android Open Source Project&#10; &#10; Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10; you may not use this file except in compliance with the License.&#10; You may obtain a copy of the License at&#10; &#10; https://www.apache.org/licenses/LICENSE-2.0&#10; &#10; Unless required by applicable law or agreed to in writing, software&#10; distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10; See the License for the specific language governing permissions and&#10; limitations under the License." />
<option name="myName" value="The Android Open Source Project" />
</copyright>
</component>

@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="The Android Open Source Project" />
</component>

@ -29,12 +29,12 @@ setup.
Current list of convention plugins:
- [`nowinandroid.spotless`](convention/src/main/kotlin/nowinandroid.spotless.gradle.kts):
- [`nowinandroid.spotless`](convention/src/main/kotlin/SpotlessConventionPlugin.kt):
Configures spotless.
- [`nowinandroid.android.application`](convention/src/main/kotlin/nowinandroid.android.application.gradle.kts),
[`nowinandroid.android.library`](convention/src/main/kotlin/nowinandroid.android.library.gradle.kts),
[`nowinandroid.android.test`](convention/src/main/kotlin/nowinandroid.android.test.gradle.kts):
- [`nowinandroid.android.application`](convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt),
[`nowinandroid.android.library`](convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt),
[`nowinandroid.android.test`](convention/src/main/kotlin/AndroidTestConventionPlugin.kt):
Configures common Android and Kotlin options.
- [`nowinandroid.android.application.compose`](convention/src/main/kotlin/nowinandroid.android.application.compose.gradle.kts),
[`nowinandroid.android.library.compose`](convention/src/main/kotlin/nowinandroid.android.library.gradle.kts):
- [`nowinandroid.android.application.compose`](convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt),
[`nowinandroid.android.library.compose`](convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt):
Configures Jetpack Compose options

@ -30,3 +30,44 @@ dependencies {
implementation(libs.kotlin.gradlePlugin)
implementation(libs.spotless.gradlePlugin)
}
gradlePlugin {
plugins {
register("androidApplicationCompose") {
id = "nowinandroid.android.application.compose"
implementationClass = "AndroidApplicationComposeConventionPlugin"
}
register("androidApplication") {
id = "nowinandroid.android.application"
implementationClass = "AndroidApplicationConventionPlugin"
}
register("androidApplicationJacoco") {
id = "nowinandroid.android.application.jacoco"
implementationClass = "AndroidApplicationJacocoConventionPlugin"
}
register("androidLibraryCompose") {
id = "nowinandroid.android.library.compose"
implementationClass = "AndroidLibraryComposeConventionPlugin"
}
register("androidLibrary") {
id = "nowinandroid.android.library"
implementationClass = "AndroidLibraryConventionPlugin"
}
register("androidFeature") {
id = "nowinandroid.android.feature"
implementationClass = "AndroidFeatureConventionPlugin"
}
register("androidLibraryJacoco") {
id = "nowinandroid.android.library.jacoco"
implementationClass = "AndroidLibraryJacocoConventionPlugin"
}
register("androidTest") {
id = "nowinandroid.android.test"
implementationClass = "AndroidTestConventionPlugin"
}
register("spotless") {
id = "nowinandroid.spotless"
implementationClass = "SpotlessConventionPlugin"
}
}
}

@ -0,0 +1,32 @@
/*
* Copyright 2022 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.
*/
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import com.google.samples.apps.nowinandroid.configureAndroidCompose
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByType
class AndroidApplicationComposeConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.android.application")
val extension = extensions.getByType<BaseAppModuleExtension>()
configureAndroidCompose(extension)
}
}
}

@ -0,0 +1,38 @@
/*
* Copyright 2022 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.
*/
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import com.google.samples.apps.nowinandroid.configureKotlinAndroid
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
class AndroidApplicationConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.application")
apply("org.jetbrains.kotlin.android")
}
extensions.configure<BaseAppModuleExtension> {
configureKotlinAndroid(this)
defaultConfig.targetSdk = 32
}
}
}
}

@ -0,0 +1,35 @@
/*
* Copyright 2022 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.
*/
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.google.samples.apps.nowinandroid.configureJacoco
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByType
class AndroidApplicationJacocoConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("org.gradle.jacoco")
apply("com.android.application")
}
val extension = extensions.getByType<ApplicationAndroidComponentsExtension>()
configureJacoco(extension)
}
}
}

@ -0,0 +1,72 @@
/*
* Copyright 2022 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.
*/
import com.android.build.gradle.LibraryExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getByType
class AndroidFeatureConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply {
apply("com.android.library")
apply("org.jetbrains.kotlin.android")
apply("org.jetbrains.kotlin.kapt")
}
extensions.configure<LibraryExtension> {
defaultConfig {
testInstrumentationRunner =
"com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner"
}
}
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
dependencies {
add("implementation", project(":core-model"))
add("implementation", project(":core-ui"))
add("implementation", project(":core-data"))
add("implementation", project(":core-common"))
add("implementation", project(":core-navigation"))
add("testImplementation", project(":core-testing"))
add("androidTestImplementation", project(":core-testing"))
add("implementation", libs.findLibrary("coil.kt").get())
add("implementation", libs.findLibrary("coil.kt.compose").get())
add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get())
add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get())
add("implementation", libs.findLibrary("kotlinx.coroutines.android").get())
add("implementation", libs.findLibrary("hilt.android").get())
add("kapt", libs.findLibrary("hilt.compiler").get())
// TODO : Remove this dependency once we upgrade to Android Studio Dolphin b/228889042
// These dependencies are currently necessary to render Compose previews
add(
"debugImplementation",
libs.findLibrary("androidx.customview.poolingcontainer").get()
)
}
}
}
}

@ -0,0 +1,32 @@
/*
* Copyright 2022 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.
*/
import com.android.build.gradle.LibraryExtension
import com.google.samples.apps.nowinandroid.configureAndroidCompose
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByType
class AndroidLibraryComposeConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.android.library")
val extension = extensions.getByType<LibraryExtension>()
configureAndroidCompose(extension)
}
}
}

@ -0,0 +1,54 @@
/*
* Copyright 2022 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.
*/
import com.android.build.gradle.LibraryExtension
import com.google.samples.apps.nowinandroid.configureFlavors
import com.google.samples.apps.nowinandroid.configureKotlinAndroid
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getByType
class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.library")
apply("org.jetbrains.kotlin.android")
}
extensions.configure<LibraryExtension> {
configureKotlinAndroid(this)
defaultConfig.targetSdk = 32
configureFlavors(this)
}
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
dependencies {
configurations.configureEach {
resolutionStrategy {
force(libs.findLibrary("junit4").get())
// Temporary workaround for https://issuetracker.google.com/174733673
force("org.objenesis:objenesis:2.6")
}
}
}
}
}
}

@ -0,0 +1,35 @@
/*
* Copyright 2022 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.
*/
import com.android.build.api.variant.LibraryAndroidComponentsExtension
import com.google.samples.apps.nowinandroid.configureJacoco
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByType
class AndroidLibraryJacocoConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("org.gradle.jacoco")
apply("com.android.library")
}
val extension = extensions.getByType<LibraryAndroidComponentsExtension>()
configureJacoco(extension)
}
}
}

@ -0,0 +1,38 @@
/*
* Copyright 2022 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.
*/
import com.android.build.gradle.TestExtension
import com.google.samples.apps.nowinandroid.configureKotlinAndroid
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
class AndroidTestConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.test")
apply("org.jetbrains.kotlin.android")
}
extensions.configure<TestExtension> {
configureKotlinAndroid(this)
defaultConfig.targetSdk = 31
}
}
}
}

@ -0,0 +1,53 @@
/*
* Copyright 2022 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.
*/
import com.diffplug.gradle.spotless.SpotlessExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.getByType
class SpotlessConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.diffplug.spotless")
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
extensions.configure<SpotlessExtension> {
kotlin {
target("**/*.kt")
targetExclude("**/build/**/*.kt")
ktlint(libs.findVersion("ktlint").get().toString()).userData(mapOf("android" to "true"))
licenseHeaderFile(rootProject.file("spotless/copyright.kt"))
}
format("kts") {
target("**/*.kts")
targetExclude("**/build/**/*.kts")
// Look for the first line that doesn't have a block comment (assumed to be the license)
licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)")
}
format("xml") {
target("**/*.xml")
targetExclude("**/build/**/*.xml")
// Look for the first XML tag that isn't a comment (<!--) or the xml declaration (<?xml)
licenseHeaderFile(rootProject.file("spotless/copyright.xml"), "(<[^!?])")
}
}
}
}
}

@ -19,12 +19,13 @@ package com.google.samples.apps.nowinandroid
import com.android.build.api.dsl.CommonExtension
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getByType
/**
* Configure Compose-specific options
*/
fun Project.configureAndroidCompose(
internal fun Project.configureAndroidCompose(
commonExtension: CommonExtension<*, *, *, *>,
) {
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")

@ -36,7 +36,7 @@ private val coverageExclusions = listOf(
"**/Manifest*.*"
)
fun Project.configureJacoco(
internal fun Project.configureJacoco(
androidComponentsExtension: AndroidComponentsExtension<*, *, *>,
) {
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")

@ -28,7 +28,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
/**
* Configure base Kotlin with Android options
*/
fun Project.configureKotlinAndroid(
internal fun Project.configureKotlinAndroid(
commonExtension: CommonExtension<*, *, *, *>,
) {
commonExtension.apply {

@ -1,25 +0,0 @@
/*
* Copyright 2022 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.
*/
import com.google.samples.apps.nowinandroid.configureAndroidCompose
plugins {
id("com.android.application")
}
android {
configureAndroidCompose(this)
}

@ -1,30 +0,0 @@
/*
* Copyright 2022 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.
*/
import com.google.samples.apps.nowinandroid.configureKotlinAndroid
plugins {
id("com.android.application")
kotlin("android")
}
android {
configureKotlinAndroid(this)
defaultConfig {
targetSdk = 32
}
}

@ -1,28 +0,0 @@
/*
* Copyright 2022 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.
*/
import com.google.samples.apps.nowinandroid.configureJacoco
plugins {
id("com.android.application")
jacoco
}
android {
androidComponents {
configureJacoco(this)
}
}

@ -1,59 +0,0 @@
/*
* Copyright 2022 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.
*/
import com.google.samples.apps.nowinandroid.configureKotlinAndroid
import org.gradle.kotlin.dsl.support.delegates.ProjectDelegate
plugins {
id("com.android.library")
kotlin("android")
kotlin("kapt")
}
android {
defaultConfig {
testInstrumentationRunner =
"com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner"
}
}
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
dependencies {
implementation(project(":core-model"))
implementation(project(":core-ui"))
implementation(project(":core-data"))
implementation(project(":core-common"))
implementation(project(":core-navigation"))
testImplementation(project(":core-testing"))
androidTestImplementation(project(":core-testing"))
add("implementation", libs.findLibrary("coil.kt").get())
add("implementation", libs.findLibrary("coil.kt.compose").get())
add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get())
add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get())
add("implementation", libs.findLibrary("kotlinx.coroutines.android").get())
add("implementation", libs.findLibrary("hilt.android").get())
add("kapt", libs.findLibrary("hilt.compiler").get())
// TODO : Remove this dependency once we upgrade to Android Studio Dolphin b/228889042
// These dependencies are currently necessary to render Compose previews
add("debugImplementation", libs.findLibrary("androidx.customview.poolingcontainer").get())
}

@ -1,25 +0,0 @@
/*
* Copyright 2022 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.
*/
import com.google.samples.apps.nowinandroid.configureAndroidCompose
plugins {
id("com.android.library")
}
android {
configureAndroidCompose(this)
}

@ -1,46 +0,0 @@
/*
* Copyright 2022 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.
*/
import com.google.samples.apps.nowinandroid.configureFlavors
import com.google.samples.apps.nowinandroid.configureKotlinAndroid
plugins {
id("com.android.library")
kotlin("android")
}
android {
configureKotlinAndroid(this)
defaultConfig {
targetSdk = 32
}
configureFlavors(this)
}
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
dependencies {
// androidx.test is forcing JUnit, 4.12. This forces it to use 4.13
configurations.configureEach {
resolutionStrategy {
force(libs.findLibrary("junit4").get())
// Temporary workaround for https://issuetracker.google.com/174733673
force("org.objenesis:objenesis:2.6")
}
}
}

@ -1,28 +0,0 @@
/*
* Copyright 2022 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.
*/
import com.google.samples.apps.nowinandroid.configureJacoco
plugins {
id("com.android.library")
jacoco
}
android {
androidComponents {
configureJacoco(this)
}
}

@ -1,30 +0,0 @@
/*
* Copyright 2022 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.
*/
import com.google.samples.apps.nowinandroid.configureKotlinAndroid
plugins {
id("com.android.test")
kotlin("android")
}
android {
configureKotlinAndroid(this)
defaultConfig {
targetSdk = 31
}
}

@ -1,42 +0,0 @@
/*
* Copyright 2022 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.
*/
plugins {
id("com.diffplug.spotless")
}
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
spotless {
kotlin {
target("**/*.kt")
targetExclude("**/build/**/*.kt")
ktlint(libs.findVersion("ktlint").get().toString()).userData(mapOf("android" to "true"))
licenseHeaderFile(rootProject.file("spotless/copyright.kt"))
}
format("kts") {
target("**/*.kts")
targetExclude("**/build/**/*.kts")
// Look for the first line that doesn't have a block comment (assumed to be the license)
licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)")
}
format("xml") {
target("**/*.xml")
targetExclude("**/build/**/*.xml")
// Look for the first XML tag that isn't a comment (<!--) or the xml declaration (<?xml)
licenseHeaderFile(rootProject.file("spotless/copyright.xml"), "(<[^!?])")
}
}

@ -23,9 +23,9 @@ import com.google.samples.apps.nowinandroid.core.database.dao.AuthorDao
import com.google.samples.apps.nowinandroid.core.database.model.AuthorEntity
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.model.data.Author
import com.google.samples.apps.nowinandroid.core.network.NiaNetwork
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkAuthor
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@ -37,8 +37,8 @@ import kotlinx.coroutines.flow.map
*/
class OfflineFirstAuthorsRepository @Inject constructor(
private val authorDao: AuthorDao,
private val network: NiaNetwork,
private val niaPreferences: NiaPreferences,
private val network: NiaNetworkDataSource,
private val niaPreferences: NiaPreferencesDataSource,
) : AuthorsRepository {
override fun getAuthorStream(id: String): Flow<Author> =

@ -35,7 +35,7 @@ import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.network.NiaNetwork
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@ -50,7 +50,7 @@ class OfflineFirstNewsRepository @Inject constructor(
private val episodeDao: EpisodeDao,
private val authorDao: AuthorDao,
private val topicDao: TopicDao,
private val network: NiaNetwork,
private val network: NiaNetworkDataSource,
) : NewsRepository {
override fun getNewsResourcesStream(): Flow<List<NewsResource>> =

@ -23,9 +23,9 @@ import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.network.NiaNetwork
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@ -37,8 +37,8 @@ import kotlinx.coroutines.flow.map
*/
class OfflineFirstTopicsRepository @Inject constructor(
private val topicDao: TopicDao,
private val network: NiaNetwork,
private val niaPreferences: NiaPreferences,
private val network: NiaNetworkDataSource,
private val niaPreferences: NiaPreferencesDataSource,
) : TopicsRepository {
override fun getTopicsStream(): Flow<List<Topic>> =

@ -18,7 +18,7 @@ package com.google.samples.apps.nowinandroid.core.data.repository.fake
import com.google.samples.apps.nowinandroid.core.data.Synchronizer
import com.google.samples.apps.nowinandroid.core.data.repository.AuthorsRepository
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.model.data.Author
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
@ -41,7 +41,7 @@ import kotlinx.serialization.json.Json
*/
class FakeAuthorsRepository @Inject constructor(
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
private val niaPreferences: NiaPreferences,
private val niaPreferences: NiaPreferencesDataSource,
private val networkJson: Json,
) : AuthorsRepository {

@ -18,7 +18,7 @@ package com.google.samples.apps.nowinandroid.core.data.repository.fake
import com.google.samples.apps.nowinandroid.core.data.Synchronizer
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
@ -43,7 +43,7 @@ import kotlinx.serialization.json.Json
class FakeTopicsRepository @Inject constructor(
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
private val networkJson: Json,
private val niaPreferences: NiaPreferences
private val niaPreferences: NiaPreferencesDataSource
) : TopicsRepository {
override fun getTopicsStream(): Flow<List<Topic>> = flow<List<Topic>> {
emit(

@ -20,11 +20,11 @@ import com.google.samples.apps.nowinandroid.core.data.Synchronizer
import com.google.samples.apps.nowinandroid.core.data.model.asEntity
import com.google.samples.apps.nowinandroid.core.data.testdoubles.CollectionType
import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestAuthorDao
import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestNiaNetwork
import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestNiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.database.dao.AuthorDao
import com.google.samples.apps.nowinandroid.core.database.model.AuthorEntity
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
import com.google.samples.apps.nowinandroid.core.model.data.Author
import com.google.samples.apps.nowinandroid.core.network.model.NetworkAuthor
@ -43,7 +43,7 @@ class OfflineFirstAuthorsRepositoryTest {
private lateinit var authorDao: AuthorDao
private lateinit var network: TestNiaNetwork
private lateinit var network: TestNiaNetworkDataSource
private lateinit var synchronizer: Synchronizer
@ -53,16 +53,16 @@ class OfflineFirstAuthorsRepositoryTest {
@Before
fun setup() {
authorDao = TestAuthorDao()
network = TestNiaNetwork()
val niaPreferences = NiaPreferences(
network = TestNiaNetworkDataSource()
val niaPreferencesDataSource = NiaPreferencesDataSource(
tmpFolder.testUserPreferencesDataStore()
)
synchronizer = TestSynchronizer(niaPreferences)
synchronizer = TestSynchronizer(niaPreferencesDataSource)
subject = OfflineFirstAuthorsRepository(
authorDao = authorDao,
network = network,
niaPreferences = niaPreferences,
niaPreferences = niaPreferencesDataSource,
)
}

@ -27,7 +27,7 @@ import com.google.samples.apps.nowinandroid.core.data.testdoubles.CollectionType
import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestAuthorDao
import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestEpisodeDao
import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestNewsResourceDao
import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestNiaNetwork
import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestNiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestTopicDao
import com.google.samples.apps.nowinandroid.core.data.testdoubles.filteredInterestsIds
import com.google.samples.apps.nowinandroid.core.data.testdoubles.nonPresentInterestsIds
@ -38,7 +38,7 @@ import com.google.samples.apps.nowinandroid.core.database.model.PopulatedEpisode
import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
@ -63,7 +63,7 @@ class OfflineFirstNewsRepositoryTest {
private lateinit var topicDao: TestTopicDao
private lateinit var network: TestNiaNetwork
private lateinit var network: TestNiaNetworkDataSource
private lateinit var synchronizer: Synchronizer
@ -76,9 +76,9 @@ class OfflineFirstNewsRepositoryTest {
episodeDao = TestEpisodeDao()
authorDao = TestAuthorDao()
topicDao = TestTopicDao()
network = TestNiaNetwork()
network = TestNiaNetworkDataSource()
synchronizer = TestSynchronizer(
NiaPreferences(
NiaPreferencesDataSource(
tmpFolder.testUserPreferencesDataStore()
)
)

@ -19,12 +19,12 @@ package com.google.samples.apps.nowinandroid.core.data.repository
import com.google.samples.apps.nowinandroid.core.data.Synchronizer
import com.google.samples.apps.nowinandroid.core.data.model.asEntity
import com.google.samples.apps.nowinandroid.core.data.testdoubles.CollectionType
import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestNiaNetwork
import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestNiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestTopicDao
import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
@ -42,9 +42,9 @@ class OfflineFirstTopicsRepositoryTest {
private lateinit var topicDao: TopicDao
private lateinit var network: TestNiaNetwork
private lateinit var network: TestNiaNetworkDataSource
private lateinit var niaPreferences: NiaPreferences
private lateinit var niaPreferences: NiaPreferencesDataSource
private lateinit var synchronizer: Synchronizer
@ -54,8 +54,8 @@ class OfflineFirstTopicsRepositoryTest {
@Before
fun setup() {
topicDao = TestTopicDao()
network = TestNiaNetwork()
niaPreferences = NiaPreferences(
network = TestNiaNetworkDataSource()
niaPreferences = NiaPreferencesDataSource(
tmpFolder.testUserPreferencesDataStore()
)
synchronizer = TestSynchronizer(niaPreferences)

@ -18,13 +18,13 @@ package com.google.samples.apps.nowinandroid.core.data.repository
import com.google.samples.apps.nowinandroid.core.data.Synchronizer
import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
/**
* Test synchronizer that delegates to [NiaPreferences]
* Test synchronizer that delegates to [NiaPreferencesDataSource]
*/
class TestSynchronizer(
private val niaPreferences: NiaPreferences
private val niaPreferences: NiaPreferencesDataSource
) : Synchronizer {
override suspend fun getChangeListVersions(): ChangeListVersions =
niaPreferences.getChangeListVersions()

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.data.testdoubles
import com.google.samples.apps.nowinandroid.core.network.NiaNetwork
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.fake.FakeDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkAuthor
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
@ -33,9 +33,9 @@ enum class CollectionType {
}
/**
* Test double for [NiaNetwork]
* Test double for [NiaNetworkDataSource]
*/
class TestNiaNetwork : NiaNetwork {
class TestNiaNetworkDataSource : NiaNetworkDataSource {
private val networkJson = Json

@ -25,7 +25,7 @@ import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.retry
class NiaPreferences @Inject constructor(
class NiaPreferencesDataSource @Inject constructor(
private val userPreferences: DataStore<UserPreferences>
) {
suspend fun setFollowedTopicIds(followedTopicIds: Set<String>) {

@ -16,8 +16,8 @@
package com.google.samples.apps.nowinandroid.core.network.di
import com.google.samples.apps.nowinandroid.core.network.NiaNetwork
import com.google.samples.apps.nowinandroid.core.network.fake.FakeNiaNetwork
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.fake.FakeNiaNetworkDataSource
import dagger.Binds
import dagger.Module
import dagger.Provides
@ -32,8 +32,8 @@ interface NetworkModule {
@Binds
fun bindsNiaNetwork(
niANetwork: FakeNiaNetwork
): NiaNetwork
niANetwork: FakeNiaNetworkDataSource
): NiaNetworkDataSource
companion object {
@Provides

@ -24,7 +24,7 @@ import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
/**
* Interface representing network calls to the NIA backend
*/
interface NiaNetwork {
interface NiaNetworkDataSource {
suspend fun getTopics(ids: List<String>? = null): List<NetworkTopic>
suspend fun getAuthors(ids: List<String>? = null): List<NetworkAuthor>

@ -18,7 +18,7 @@ package com.google.samples.apps.nowinandroid.core.network.fake
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.NiaNetwork
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkAuthor
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
@ -30,12 +30,12 @@ import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
/**
* [NiaNetwork] implementation that provides static news resources to aid development
* [NiaNetworkDataSource] implementation that provides static news resources to aid development
*/
class FakeNiaNetwork @Inject constructor(
class FakeNiaNetworkDataSource @Inject constructor(
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
private val networkJson: Json
) : NiaNetwork {
) : NiaNetworkDataSource {
override suspend fun getTopics(ids: List<String>?): List<NetworkTopic> =
withContext(ioDispatcher) {
networkJson.decodeFromString(FakeDataSource.topicsData)

@ -17,7 +17,7 @@
package com.google.samples.apps.nowinandroid.core.network.retrofit
import com.google.samples.apps.nowinandroid.core.network.BuildConfig
import com.google.samples.apps.nowinandroid.core.network.NiaNetwork
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkAuthor
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
@ -80,12 +80,12 @@ private data class NetworkResponse<T>(
)
/**
* [Retrofit] backed [NiaNetwork]
* [Retrofit] backed [NiaNetworkDataSource]
*/
@Singleton
class RetrofitNiaNetwork @Inject constructor(
networkJson: Json
) : NiaNetwork {
) : NiaNetworkDataSource {
private val networkApi = Retrofit.Builder()
.baseUrl(NiaBaseUrl)

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.network.di
import com.google.samples.apps.nowinandroid.core.network.NiaNetwork
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.retrofit.RetrofitNiaNetwork
import dagger.Binds
import dagger.Module
@ -33,7 +33,7 @@ interface NetworkModule {
@Binds
fun bindsNiaNetwork(
niANetwork: RetrofitNiaNetwork
): NiaNetwork
): NiaNetworkDataSource
companion object {
@Provides

@ -23,15 +23,15 @@ import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
class FakeNiaNetworkTest {
class FakeNiaNetworkDataSourceTest {
private lateinit var subject: FakeNiaNetwork
private lateinit var subject: FakeNiaNetworkDataSource
private val testDispatcher = StandardTestDispatcher()
@Before
fun setUp() {
subject = FakeNiaNetwork(
subject = FakeNiaNetworkDataSource(
ioDispatcher = testDispatcher,
networkJson = Json { ignoreUnknownKeys = true }
)

@ -204,7 +204,7 @@ A repository may depend on one or more data sources. For example, the `OfflineFi
</td>
</tr>
<tr>
<td>NiaPreferences
<td>NiaPreferencesDataSource
</td>
<td><a href="https://developer.android.com/topic/libraries/architecture/datastore">Proto DataStore</a>
</td>
@ -212,7 +212,7 @@ A repository may depend on one or more data sources. For example, the `OfflineFi
</td>
</tr>
<tr>
<td>NiaNetwork
<td>NiaNetworkDataSource
</td>
<td>Remote API accessed using Retrofit
</td>

@ -4,16 +4,16 @@ androidDesugarJdkLibs = "1.1.5"
androidGradlePlugin = "7.2.1"
androidxActivity = "1.4.0"
androidxAppCompat = "1.3.0"
androidxCompose = "1.2.0-beta01"
androidxComposeMaterial3 = "1.0.0-alpha11"
androidxCompose = "1.2.0-beta02"
androidxComposeMaterial3 = "1.0.0-alpha12"
androidxCore = "1.7.0"
androidxCustomView = "1.0.0-alpha01"
androidxCustomView = "1.0.0-beta02"
androidxDataStore = "1.0.0"
androidxEspresso = "3.3.0"
androidxHiltNavigationCompose = "1.0.0"
androidxLifecycle = "2.5.0-beta01"
androidxMacroBenchmark = "1.1.0-rc02"
androidxNavigation = "2.4.0-rc01"
androidxNavigation = "2.4.2"
androidxProfileinstaller = "1.2.0-beta01"
androidxSavedState = "1.1.0"
androidxStartup = "1.1.1"
@ -26,7 +26,7 @@ coil = "2.0.0-rc01"
hilt = "2.41"
hiltExt = "1.0.0"
jacoco = "0.8.7"
junit4 = "4.13"
junit4 = "4.13.2"
kotlin = "1.6.21"
kotlinxCoroutines = "1.6.0"
kotlinxCoroutinesTest = "1.6.0"

@ -28,7 +28,7 @@ import com.google.samples.apps.nowinandroid.core.data.repository.AuthorsReposito
import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.sync.initializers.SyncConstraints
@ -48,7 +48,7 @@ import kotlinx.coroutines.withContext
class SyncWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted workerParams: WorkerParameters,
private val niaPreferences: NiaPreferences,
private val niaPreferences: NiaPreferencesDataSource,
private val topicRepository: TopicsRepository,
private val newsRepository: NewsRepository,
private val authorsRepository: AuthorsRepository,

Loading…
Cancel
Save