Modularise the app

Change-Id: I63791eaf27d08a837598d774df53c96bb01b5864
pull/2/head
Simona Stojanovic 3 years ago
parent 1b06dc97ea
commit 1411b1576b

@ -17,11 +17,8 @@ plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'kotlinx-serialization'
id 'jacoco'
id 'dagger.hilt.android.plugin'
alias(libs.plugins.protobuf)
alias(libs.plugins.ksp)
}
def jacocoTestReport = tasks.create("jacocoTestReport")
@ -37,7 +34,7 @@ android {
versionName "0.0.1" // X.Y.Z; X = Major, Y = minor, Z = Patch level
// Custom test runner to set up Hilt dependency graph
testInstrumentationRunner "com.google.samples.apps.nowinandroid.NiaTestRunner"
testInstrumentationRunner "com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner"
vectorDrawables {
useSupportLibrary true
}
@ -112,81 +109,34 @@ android {
}
}
// Setup protobuf configuration, generating lite Java and Kotlin classes
protobuf {
protoc {
artifact = libs.protobuf.protoc.get()
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option "lite"
}
kotlin {
option "lite"
}
}
}
}
}
dependencies {
coreLibraryDesugaring libs.android.desugarJdkLibs
implementation project(':feature-following')
implementation project(':feature-foryou')
implementation project(':core-ui')
implementation libs.kotlinx.coroutines.android
implementation libs.kotlinx.datetime
implementation libs.kotlinx.serialization.json
androidTestImplementation project(':core-testing')
implementation project(':core-datastore-test')
coreLibraryDesugaring libs.android.desugarJdkLibs
implementation libs.androidx.activity.compose
implementation libs.androidx.core.ktx
implementation libs.androidx.dataStore
implementation libs.androidx.appcompat
implementation libs.androidx.hilt.navigation.compose
implementation libs.androidx.lifecycle.viewModelCompose
implementation libs.androidx.navigation.compose
implementation libs.material3
implementation libs.accompanist.flowlayout
implementation libs.androidx.compose.foundation.layout
// TODO (M3): Remove this dependency when all components are available
implementation libs.androidx.compose.material
implementation libs.androidx.compose.material.iconsExtended
implementation libs.androidx.compose.material3
implementation libs.androidx.compose.ui.tooling
implementation libs.androidx.compose.ui.util
implementation libs.androidx.compose.runtime
implementation libs.androidx.compose.runtime.livedata
implementation libs.hilt.android
kapt libs.hilt.compiler
implementation libs.room.runtime
implementation libs.room.ktx
ksp libs.room.compiler
implementation libs.protobuf.kotlin.lite
debugImplementation libs.androidx.compose.ui.testManifest
testImplementation libs.junit4
testImplementation libs.mockk
testImplementation libs.androidx.test.core
testImplementation libs.kotlinx.coroutines.test
testImplementation libs.turbine
androidTestImplementation libs.androidx.test.espresso.core
androidTestImplementation libs.androidx.test.runner
androidTestImplementation libs.androidx.test.rules
androidTestImplementation libs.androidx.compose.ui.test
androidTestImplementation libs.hilt.android.testing
kaptAndroidTest libs.hilt.compiler
// androidx.test is forcing JUnit, 4.12. This forces it to use 4.13
configurations.configureEach {
resolutionStrategy {
force libs.junit4
// Temporary workaround for https://issuetracker.google.com/174733673
force 'org.objenesis:objenesis:2.6'
}
}
}

@ -1,65 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
static <1>$Companion Companion;
}
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
static **$* *;
}
-keepclassmembers class <1>$<3> {
kotlinx.serialization.KSerializer serializer(...);
}
# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
public static ** INSTANCE;
}
-keepclassmembers class <1> {
public static <1> INSTANCE;
kotlinx.serialization.KSerializer serializer(...);
}
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
# If you have any, uncomment and replace classes with those containing named companion objects.
#-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
#-if @kotlinx.serialization.Serializable class
#com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
#com.example.myapplication.HasNamedCompanion2
#{
# static **$* *;
#}
#-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
# static <1>$$serializer INSTANCE;
#}
# Enable protobuf-related optimizations.
-shrinkunusedprotofields

@ -1,81 +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.
*/
package com.google.samples.apps.nowinandroid.di
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import com.google.samples.apps.nowinandroid.data.UserPreferences
import com.google.samples.apps.nowinandroid.data.UserPreferencesSerializer
import com.google.samples.apps.nowinandroid.data.fake.FakeNewsRepository
import com.google.samples.apps.nowinandroid.data.fake.FakeTopicsRepository
import com.google.samples.apps.nowinandroid.data.repository.NewsRepository
import com.google.samples.apps.nowinandroid.data.repository.TopicsRepository
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.components.SingletonComponent
import dagger.hilt.testing.TestInstallIn
import javax.inject.Singleton
import kotlinx.serialization.json.Json
import org.junit.rules.TemporaryFolder
/**
* The [TestAppModule] replaces [AppModule] during instrumentation tests. It creates test doubles
* where necessary. It also includes logic to prevent multiple data stores with the same file name
* from being created during one test execution context.
*/
@Module
@TestInstallIn(
components = [SingletonComponent::class],
replaces = [AppModule::class]
)
interface TestAppModule {
// Use a fake repository as a test double, so we don't have a network dependency.
@Binds
fun bindsTopicRepository(fakeTopicsRepository: FakeTopicsRepository): TopicsRepository
// Use a fake repository as a test double, so we don't have a network dependency.
@Binds
fun bindsNewsResourceRepository(fakeNewsRepository: FakeNewsRepository): NewsRepository
// Use the default dispatchers. For the high-level UI tests, we don't want to override these.
@Binds
fun bindsNiaDispatchers(defaultNiaDispatchers: DefaultNiaDispatchers): NiaDispatchers
companion object {
@Provides
@Singleton
fun providesUserPreferencesDataStore(
userPreferencesSerializer: UserPreferencesSerializer,
tmpFolder: TemporaryFolder
): DataStore<UserPreferences> {
return DataStoreFactory.create(
serializer = userPreferencesSerializer,
) {
tmpFolder.newFile("user_preferences_test.pb")
}
}
@Provides
@Singleton
fun providesNetworkJson(): Json = Json {
ignoreUnknownKeys = true
}
}
}

@ -1,81 +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.
*/
package com.google.samples.apps.nowinandroid.di
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.dataStoreFile
import com.google.samples.apps.nowinandroid.data.UserPreferences
import com.google.samples.apps.nowinandroid.data.UserPreferencesSerializer
import com.google.samples.apps.nowinandroid.data.fake.FakeNewsRepository
import com.google.samples.apps.nowinandroid.data.fake.FakeNiANetwork
import com.google.samples.apps.nowinandroid.data.fake.FakeTopicsRepository
import com.google.samples.apps.nowinandroid.data.network.NiANetwork
import com.google.samples.apps.nowinandroid.data.repository.NewsRepository
import com.google.samples.apps.nowinandroid.data.repository.TopicsRepository
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
import kotlinx.serialization.json.Json
@Module
@InstallIn(SingletonComponent::class)
interface AppModule {
@Binds
fun bindsNiANetwork(
fakeNiANetwork: FakeNiANetwork
): NiANetwork
@Binds
fun bindsTopicRepository(
fakeTopicsRepository: FakeTopicsRepository
): TopicsRepository
@Binds
fun bindsNewsResourceRepository(
fakeNewsRepository: FakeNewsRepository
): NewsRepository
@Binds
fun bindsNiaDispatchers(defaultNiaDispatchers: DefaultNiaDispatchers): NiaDispatchers
companion object {
@Provides
@Singleton
fun providesUserPreferencesDataStore(
@ApplicationContext context: Context,
userPreferencesSerializer: UserPreferencesSerializer
): DataStore<UserPreferences> =
DataStoreFactory.create(
serializer = userPreferencesSerializer
) {
context.dataStoreFile("user_preferences.pb")
}
@Provides
@Singleton
fun providesNetworkJson(): Json = Json {
ignoreUnknownKeys = true
}
}
}

@ -51,7 +51,8 @@ import androidx.compose.ui.unit.dp
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.google.samples.apps.nowinandroid.R
import com.google.samples.apps.nowinandroid.ui.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.ui.ClearRippleTheme
import com.google.samples.apps.nowinandroid.core.ui.theme.NiaTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable

@ -23,8 +23,8 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.google.samples.apps.nowinandroid.ui.following.FollowingRoute
import com.google.samples.apps.nowinandroid.ui.foryou.ForYouRoute
import com.google.samples.apps.nowinandroid.feature.following.FollowingRoute
import com.google.samples.apps.nowinandroid.feature.foryou.ForYouRoute
/**
* Top-level navigation graph. Navigation is organized as explained at

@ -15,22 +15,4 @@
-->
<resources>
<string name="app_name">Now in Android</string>
<string name="for_you">For you</string>
<string name="episodes">Episodes</string>
<string name="saved">Saved</string>
<string name="done">Done</string>
<string name="for_you_loading">Loading for you…</string>
<string name="navigate_up">Navigate up</string>
<!-- NewsResource Card -->
<string name="bookmark">Bookmark</string>
<string name="unbookmark">Unbookmark</string>
<!-- Following-->
<string name="following">Following</string>
<string name="following_loading">Loading topics</string>
<string name="following_error_header">"Error loading topics"</string>
<string name="following_topic_card_icon_content_desc">Topic icon</string>
<string name="following_topic_card_follow_button_content_desc">Follow Topic button</string>
<string name="following_topic_card_unfollow_button_content_desc">Unfollow Topic button</string>
</resources>

@ -49,7 +49,7 @@ subprojects {
target '**/*.kt'
targetExclude("$buildDir/**/*.kt")
targetExclude('bin/**/*.kt')
targetExclude("$rootDir/app/src/main/java/com/google/samples/apps/nowinandroid/data/fake/FakeData.kt")
targetExclude("$rootDir/core-network/src/main/java/com/google/samples/apps/nowinandroid/core/network/fake/FakeData.kt")
ktlint(libs.versions.ktlint.get()).userData([android: "true"])
licenseHeaderFile rootProject.file('spotless/copyright.kt')
}

@ -0,0 +1 @@
/build

@ -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.
*/
plugins {
id 'com.android.library'
id 'kotlin-android'
}
android {
compileSdk buildConfig.compileSdk
defaultConfig {
minSdk buildConfig.minSdk
targetSdk buildConfig.targetSdk
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.nowinandroid.core.common">
</manifest>

@ -0,0 +1 @@
/build

@ -0,0 +1,52 @@
/*
* 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.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
alias(libs.plugins.ksp)
}
android {
compileSdk buildConfig.compileSdk
defaultConfig {
minSdk buildConfig.minSdk
targetSdk buildConfig.targetSdk
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation project(':core-model')
implementation libs.room.runtime
implementation libs.room.ktx
ksp libs.room.compiler
implementation libs.kotlinx.coroutines.android
implementation libs.kotlinx.datetime
implementation libs.hilt.android
kapt libs.hilt.compiler
}

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.nowinandroid.core.database">
</manifest>

@ -14,13 +14,12 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.di
package com.google.samples.apps.nowinandroid.core.database
import com.google.samples.apps.nowinandroid.data.local.NiADatabase
import com.google.samples.apps.nowinandroid.data.local.dao.AuthorDao
import com.google.samples.apps.nowinandroid.data.local.dao.EpisodeDao
import com.google.samples.apps.nowinandroid.data.local.dao.NewsResourceDao
import com.google.samples.apps.nowinandroid.data.local.dao.TopicDao
import com.google.samples.apps.nowinandroid.core.database.dao.AuthorDao
import com.google.samples.apps.nowinandroid.core.database.dao.EpisodeDao
import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao
import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn

@ -14,11 +14,10 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.di
package com.google.samples.apps.nowinandroid.core.database
import android.content.Context
import androidx.room.Room
import com.google.samples.apps.nowinandroid.data.local.NiADatabase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn

@ -14,24 +14,24 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local
package com.google.samples.apps.nowinandroid.core.database
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.google.samples.apps.nowinandroid.data.local.dao.AuthorDao
import com.google.samples.apps.nowinandroid.data.local.dao.EpisodeDao
import com.google.samples.apps.nowinandroid.data.local.dao.NewsResourceDao
import com.google.samples.apps.nowinandroid.data.local.dao.TopicDao
import com.google.samples.apps.nowinandroid.data.local.entities.AuthorEntity
import com.google.samples.apps.nowinandroid.data.local.entities.EpisodeAuthorCrossRef
import com.google.samples.apps.nowinandroid.data.local.entities.EpisodeEntity
import com.google.samples.apps.nowinandroid.data.local.entities.NewsResourceAuthorCrossRef
import com.google.samples.apps.nowinandroid.data.local.entities.NewsResourceEntity
import com.google.samples.apps.nowinandroid.data.local.entities.NewsResourceTopicCrossRef
import com.google.samples.apps.nowinandroid.data.local.entities.TopicEntity
import com.google.samples.apps.nowinandroid.data.local.utilities.InstantConverter
import com.google.samples.apps.nowinandroid.data.local.utilities.NewsResourceTypeConverter
import com.google.samples.apps.nowinandroid.core.database.dao.AuthorDao
import com.google.samples.apps.nowinandroid.core.database.dao.EpisodeDao
import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao
import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao
import com.google.samples.apps.nowinandroid.core.database.util.InstantConverter
import com.google.samples.apps.nowinandroid.core.database.util.NewsResourceTypeConverter
import com.google.samples.apps.nowinandroid.core.model.entities.AuthorEntity
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeAuthorCrossRef
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeEntity
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceAuthorCrossRef
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceTopicCrossRef
import com.google.samples.apps.nowinandroid.core.model.entities.TopicEntity
@Database(
entities = [

@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local.dao
package com.google.samples.apps.nowinandroid.core.database.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.google.samples.apps.nowinandroid.data.local.entities.AuthorEntity
import com.google.samples.apps.nowinandroid.core.model.entities.AuthorEntity
import kotlinx.coroutines.flow.Flow
/**

@ -14,13 +14,13 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local.dao
package com.google.samples.apps.nowinandroid.core.database.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.google.samples.apps.nowinandroid.data.local.entities.EpisodeEntity
import com.google.samples.apps.nowinandroid.data.model.Episode
import com.google.samples.apps.nowinandroid.core.model.data.Episode
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeEntity
import kotlinx.coroutines.flow.Flow
/**

@ -14,13 +14,13 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local.dao
package com.google.samples.apps.nowinandroid.core.database.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.google.samples.apps.nowinandroid.data.local.entities.NewsResourceEntity
import com.google.samples.apps.nowinandroid.data.model.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceEntity
import kotlinx.coroutines.flow.Flow
/**

@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local.dao
package com.google.samples.apps.nowinandroid.core.database.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.google.samples.apps.nowinandroid.data.local.entities.TopicEntity
import com.google.samples.apps.nowinandroid.core.model.entities.TopicEntity
import kotlinx.coroutines.flow.Flow
/**

@ -14,10 +14,10 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local.utilities
package com.google.samples.apps.nowinandroid.core.database.util
import androidx.room.TypeConverter
import com.google.samples.apps.nowinandroid.data.model.NewsResourceType
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType
import kotlinx.datetime.Instant
class InstantConverter {

@ -0,0 +1,55 @@
/*
* 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.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
compileSdk buildConfig.compileSdk
defaultConfig {
minSdk buildConfig.minSdk
targetSdk buildConfig.targetSdk
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation project(':core-datastore')
implementation project(':core-testing')
implementation libs.androidx.dataStore
implementation libs.hilt.android
kapt libs.hilt.compiler
kaptAndroidTest libs.hilt.compiler
configurations.configureEach {
resolutionStrategy {
// Temporary workaround for https://issuetracker.google.com/174733673
force 'org.objenesis:objenesis:2.6'
}
}
}

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.nowinandroid.core.datastore.test">
</manifest>

@ -0,0 +1,50 @@
/*
* 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.
*/
package com.google.samples.apps.nowinandroid.core.datastore.test
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferences
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferencesSerializer
import com.google.samples.apps.nowinandroid.core.datastore.di.DataStoreModule
import dagger.Module
import dagger.Provides
import dagger.hilt.components.SingletonComponent
import dagger.hilt.testing.TestInstallIn
import javax.inject.Singleton
import org.junit.rules.TemporaryFolder
@Module
@TestInstallIn(
components = [SingletonComponent::class],
replaces = [DataStoreModule::class]
)
object TestDataStoreModule {
@Provides
@Singleton
fun providesUserPreferencesDataStore(
userPreferencesSerializer: UserPreferencesSerializer,
tmpFolder: TemporaryFolder
): DataStore<UserPreferences> {
return DataStoreFactory.create(
serializer = userPreferencesSerializer,
) {
tmpFolder.newFile("user_preferences_test.pb")
}
}
}

@ -0,0 +1 @@
/build

@ -0,0 +1,70 @@
/*
* 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.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
alias(libs.plugins.protobuf)
}
android {
compileSdk buildConfig.compileSdk
defaultConfig {
minSdk buildConfig.minSdk
targetSdk buildConfig.targetSdk
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
// Setup protobuf configuration, generating lite Java and Kotlin classes
protobuf {
protoc {
artifact = libs.protobuf.protoc.get()
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option "lite"
}
kotlin {
option "lite"
}
}
}
}
}
dependencies {
testImplementation project(':core-testing')
implementation libs.kotlinx.coroutines.android
implementation libs.androidx.dataStore
implementation libs.protobuf.kotlin.lite
implementation libs.hilt.android
kapt libs.hilt.compiler
kaptAndroidTest libs.hilt.compiler
}

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.nowinandroid.core.datastore">
</manifest>

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data
package com.google.samples.apps.nowinandroid.core.datastore
import android.util.Log
import androidx.datastore.core.DataStore

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data
package com.google.samples.apps.nowinandroid.core.datastore
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer

@ -0,0 +1,47 @@
/*
* 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.
*/
package com.google.samples.apps.nowinandroid.core.datastore.di
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.dataStoreFile
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferences
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferencesSerializer
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object DataStoreModule {
@Provides
@Singleton
fun providesUserPreferencesDataStore(
@ApplicationContext context: Context,
userPreferencesSerializer: UserPreferencesSerializer
): DataStore<UserPreferences> =
DataStoreFactory.create(
serializer = userPreferencesSerializer
) {
context.dataStoreFile("user_preferences.pb")
}
}

@ -16,7 +16,7 @@
syntax = "proto3";
option java_package = "com.google.samples.apps.nowinandroid.data";
option java_package = "com.google.samples.apps.nowinandroid.core.datastore";
option java_multiple_files = true;
message UserPreferences {

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data
package com.google.samples.apps.nowinandroid.core.datastore
import androidx.datastore.core.CorruptionException
import java.io.ByteArrayInputStream

@ -0,0 +1 @@
/build

@ -0,0 +1,52 @@
/*
* 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.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
id 'kotlinx-serialization'
id 'dagger.hilt.android.plugin'
}
android {
compileSdk buildConfig.compileSdk
defaultConfig {
minSdk buildConfig.minSdk
targetSdk buildConfig.targetSdk
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation project(':core-model')
implementation project(':core-datastore')
implementation project(':core-network')
testImplementation project(':core-testing')
implementation libs.kotlinx.coroutines.android
implementation libs.kotlinx.serialization.json
implementation libs.hilt.android
kapt libs.hilt.compiler
}

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.nowinandroid.core.domain">
</manifest>

@ -0,0 +1,41 @@
/*
* 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.
*/
package com.google.samples.apps.nowinandroid.core.domain.di
import com.google.samples.apps.nowinandroid.core.domain.repository.FakeNewsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.FakeTopicsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
interface DomainModule {
@Binds
fun bindsTopicRepository(
fakeTopicsRepository: FakeTopicsRepository
): TopicsRepository
@Binds
fun bindsNewsResourceRepository(
fakeNewsRepository: FakeNewsRepository
): NewsRepository
}

@ -14,11 +14,10 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.fake
package com.google.samples.apps.nowinandroid.core.domain.repository
import com.google.samples.apps.nowinandroid.data.model.NewsResource
import com.google.samples.apps.nowinandroid.data.repository.NewsRepository
import com.google.samples.apps.nowinandroid.di.NiaDispatchers
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf

@ -14,13 +14,13 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.fake
package com.google.samples.apps.nowinandroid.core.domain.repository
import com.google.samples.apps.nowinandroid.data.NiaPreferences
import com.google.samples.apps.nowinandroid.data.model.Topic
import com.google.samples.apps.nowinandroid.data.network.NetworkTopic
import com.google.samples.apps.nowinandroid.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.di.NiaDispatchers
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.model.network.NetworkTopic
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers
import com.google.samples.apps.nowinandroid.core.network.fake.FakeDataSource
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

@ -14,9 +14,9 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.repository
package com.google.samples.apps.nowinandroid.core.domain.repository
import com.google.samples.apps.nowinandroid.data.model.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import kotlinx.coroutines.flow.Flow
/**

@ -14,9 +14,9 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.repository
package com.google.samples.apps.nowinandroid.core.domain.repository
import com.google.samples.apps.nowinandroid.data.model.Topic
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import kotlinx.coroutines.flow.Flow
interface TopicsRepository {

@ -14,9 +14,9 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.fake
package com.google.samples.apps.nowinandroid.core.domain.repository
import com.google.samples.apps.nowinandroid.di.DefaultNiaDispatchers
import com.google.samples.apps.nowinandroid.core.network.DefaultNiaDispatchers
import kotlinx.serialization.json.Json
import org.junit.Before

@ -0,0 +1 @@
/build

@ -0,0 +1,49 @@
/*
* 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.android.library'
id 'kotlin-android'
id 'kotlinx-serialization'
alias(libs.plugins.ksp)
}
android {
compileSdk buildConfig.compileSdk
defaultConfig {
minSdk buildConfig.minSdk
targetSdk buildConfig.targetSdk
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
testImplementation project(':core-testing')
implementation libs.room.runtime
implementation libs.room.ktx
ksp libs.room.compiler
implementation libs.kotlinx.coroutines.android
implementation libs.kotlinx.datetime
implementation libs.kotlinx.serialization.json
}

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.nowinandroid.core.model">
</manifest>

@ -14,15 +14,15 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.model
package com.google.samples.apps.nowinandroid.core.model.data
import androidx.room.Embedded
import androidx.room.Junction
import androidx.room.Relation
import com.google.samples.apps.nowinandroid.data.local.entities.AuthorEntity
import com.google.samples.apps.nowinandroid.data.local.entities.EpisodeAuthorCrossRef
import com.google.samples.apps.nowinandroid.data.local.entities.EpisodeEntity
import com.google.samples.apps.nowinandroid.data.local.entities.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.model.entities.AuthorEntity
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeAuthorCrossRef
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeEntity
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceEntity
/**
* External data layer representation of an NiA episode

@ -14,17 +14,17 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.model
package com.google.samples.apps.nowinandroid.core.model.data
import androidx.room.Embedded
import androidx.room.Junction
import androidx.room.Relation
import com.google.samples.apps.nowinandroid.data.local.entities.AuthorEntity
import com.google.samples.apps.nowinandroid.data.local.entities.EpisodeEntity
import com.google.samples.apps.nowinandroid.data.local.entities.NewsResourceAuthorCrossRef
import com.google.samples.apps.nowinandroid.data.local.entities.NewsResourceEntity
import com.google.samples.apps.nowinandroid.data.local.entities.NewsResourceTopicCrossRef
import com.google.samples.apps.nowinandroid.data.local.entities.TopicEntity
import com.google.samples.apps.nowinandroid.core.model.entities.AuthorEntity
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeEntity
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceAuthorCrossRef
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceTopicCrossRef
import com.google.samples.apps.nowinandroid.core.model.entities.TopicEntity
/**
* External data layer representation of a fully populated NiA news resource

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.model
package com.google.samples.apps.nowinandroid.core.model.data
/**
* Type for [NewsResource]

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.model
package com.google.samples.apps.nowinandroid.core.model.data
/**
* External data layer representation of a NiA Topic

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local.entities
package com.google.samples.apps.nowinandroid.core.model.entities
import androidx.room.ColumnInfo
import androidx.room.Entity

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local.entities
package com.google.samples.apps.nowinandroid.core.model.entities
import androidx.room.ColumnInfo
import androidx.room.Entity

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local.entities
package com.google.samples.apps.nowinandroid.core.model.entities
import androidx.room.ColumnInfo
import androidx.room.Entity

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local.entities
package com.google.samples.apps.nowinandroid.core.model.entities
import androidx.room.ColumnInfo
import androidx.room.Entity

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local.entities
package com.google.samples.apps.nowinandroid.core.model.entities
import androidx.room.ColumnInfo
import androidx.room.Entity

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local.entities
package com.google.samples.apps.nowinandroid.core.model.entities
import androidx.room.ColumnInfo
import androidx.room.Entity

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.local.entities
package com.google.samples.apps.nowinandroid.core.model.entities
import androidx.room.Entity
import androidx.room.Index

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.network.utilities
package com.google.samples.apps.nowinandroid.core.model.network
import kotlinx.datetime.Instant
import kotlinx.datetime.toInstant

@ -14,9 +14,9 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.network
package com.google.samples.apps.nowinandroid.core.model.network
import com.google.samples.apps.nowinandroid.data.local.entities.AuthorEntity
import com.google.samples.apps.nowinandroid.core.model.entities.AuthorEntity
import kotlinx.serialization.Serializable
/**

@ -14,11 +14,10 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.network
package com.google.samples.apps.nowinandroid.core.model.network
import androidx.room.PrimaryKey
import com.google.samples.apps.nowinandroid.data.local.entities.EpisodeEntity
import com.google.samples.apps.nowinandroid.data.network.utilities.InstantSerializer
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeEntity
import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable

@ -14,10 +14,9 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.network
package com.google.samples.apps.nowinandroid.core.model.network
import com.google.samples.apps.nowinandroid.data.local.entities.NewsResourceEntity
import com.google.samples.apps.nowinandroid.data.network.utilities.InstantSerializer
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceEntity
import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable

@ -14,9 +14,9 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.network
package com.google.samples.apps.nowinandroid.core.model.network
import com.google.samples.apps.nowinandroid.data.local.entities.TopicEntity
import com.google.samples.apps.nowinandroid.core.model.entities.TopicEntity
import kotlinx.serialization.Serializable
/**

@ -14,9 +14,16 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.network
import com.google.samples.apps.nowinandroid.data.model.NewsResourceType
package com.google.samples.apps.nowinandroid.core.model
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType
import com.google.samples.apps.nowinandroid.core.model.network.NetworkAuthor
import com.google.samples.apps.nowinandroid.core.model.network.NetworkEpisode
import com.google.samples.apps.nowinandroid.core.model.network.NetworkEpisodeExpanded
import com.google.samples.apps.nowinandroid.core.model.network.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.model.network.NetworkNewsResourceExpanded
import com.google.samples.apps.nowinandroid.core.model.network.NetworkTopic
import com.google.samples.apps.nowinandroid.core.model.network.asEntity
import kotlinx.datetime.Instant
import org.junit.Assert.assertEquals
import org.junit.Test

@ -0,0 +1 @@
/build

@ -0,0 +1,51 @@
/*
* 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.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
id 'kotlinx-serialization'
id 'dagger.hilt.android.plugin'
}
android {
compileSdk buildConfig.compileSdk
defaultConfig {
minSdk buildConfig.minSdk
targetSdk buildConfig.targetSdk
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation project(':core-model')
testImplementation project(':core-testing')
implementation libs.kotlinx.coroutines.android
implementation libs.kotlinx.serialization.json
implementation libs.kotlinx.datetime
implementation libs.hilt.android
kapt libs.hilt.compiler
}

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.nowinandroid.core.network">
</manifest>

@ -14,7 +14,10 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.network
package com.google.samples.apps.nowinandroid.core.network
import com.google.samples.apps.nowinandroid.core.model.network.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.model.network.NetworkTopic
/**
* Interface representing network calls to the NIA backend

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.di
package com.google.samples.apps.nowinandroid.core.network
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher

@ -0,0 +1,50 @@
/*
* 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.
*/
package com.google.samples.apps.nowinandroid.core.network.di
import com.google.samples.apps.nowinandroid.core.network.DefaultNiaDispatchers
import com.google.samples.apps.nowinandroid.core.network.NiANetwork
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers
import com.google.samples.apps.nowinandroid.core.network.fake.FakeNiANetwork
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
import kotlinx.serialization.json.Json
@Module
@InstallIn(SingletonComponent::class)
interface NetworkModule {
@Binds
fun bindsNiANetwork(
fakeNiANetwork: FakeNiANetwork
): NiANetwork
@Binds
fun bindsNiaDispatchers(defaultNiaDispatchers: DefaultNiaDispatchers): NiaDispatchers
companion object {
@Provides
@Singleton
fun providesNetworkJson(): Json = Json {
ignoreUnknownKeys = true
}
}
}

@ -14,10 +14,10 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.fake
package com.google.samples.apps.nowinandroid.core.network.fake
import com.google.samples.apps.nowinandroid.data.network.NetworkNewsResource
import com.google.samples.apps.nowinandroid.data.network.NetworkTopic
import com.google.samples.apps.nowinandroid.core.model.network.NetworkTopic
import com.google.samples.apps.nowinandroid.core.model.network.NetworkNewsResource
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant

@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.fake
package com.google.samples.apps.nowinandroid.core.network.fake
import com.google.samples.apps.nowinandroid.data.network.NetworkNewsResource
import com.google.samples.apps.nowinandroid.data.network.NetworkTopic
import com.google.samples.apps.nowinandroid.data.network.NiANetwork
import com.google.samples.apps.nowinandroid.di.NiaDispatchers
import com.google.samples.apps.nowinandroid.core.model.network.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.model.network.NetworkTopic
import com.google.samples.apps.nowinandroid.core.network.NiANetwork
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers
import javax.inject.Inject
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable

@ -14,9 +14,9 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.data.fake
package com.google.samples.apps.nowinandroid.core.network.fake
import com.google.samples.apps.nowinandroid.di.DefaultNiaDispatchers
import com.google.samples.apps.nowinandroid.core.network.DefaultNiaDispatchers
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import org.junit.Assert.assertEquals
@ -29,7 +29,7 @@ class FakeNiANetworkTest {
@Before
fun setUp() {
subject = FakeNiANetwork(
subject = com.google.samples.apps.nowinandroid.core.network.fake.FakeNiANetwork(
// TODO: Create test-specific NiaDispatchers
dispatchers = DefaultNiaDispatchers(),
networkJson = Json { ignoreUnknownKeys = true }

@ -0,0 +1 @@
/build

@ -0,0 +1,61 @@
/*
* 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.android.library'
id 'kotlin-android'
}
android {
compileSdk buildConfig.compileSdk
defaultConfig {
minSdk buildConfig.minSdk
targetSdk buildConfig.targetSdk
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation project(':core-domain')
implementation project(':core-model')
api libs.junit4
api libs.mockk
api libs.androidx.test.core
api libs.kotlinx.coroutines.test
api libs.turbine
api libs.androidx.test.espresso.core
api libs.androidx.test.runner
api libs.androidx.test.rules
api libs.androidx.compose.ui.test
api libs.hilt.android.testing
debugApi libs.androidx.compose.ui.testManifest
configurations.configureEach {
resolutionStrategy {
// Temporary workaround for https://issuetracker.google.com/174733673
force 'org.objenesis:objenesis:2.6'
}
}
}

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.nowinandroid.core.testing">
</manifest>

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid
package com.google.samples.apps.nowinandroid.core.testing
import android.app.Application
import android.content.Context

@ -14,10 +14,10 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.testutil
package com.google.samples.apps.nowinandroid.core.testing.repository
import com.google.samples.apps.nowinandroid.data.model.NewsResource
import com.google.samples.apps.nowinandroid.data.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow

@ -14,10 +14,10 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.testutil
package com.google.samples.apps.nowinandroid.core.testing.repository
import com.google.samples.apps.nowinandroid.data.model.Topic
import com.google.samples.apps.nowinandroid.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.testutil
package com.google.samples.apps.nowinandroid.core.testing.util
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers

@ -0,0 +1 @@
/build

@ -0,0 +1,59 @@
/*
* 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.android.library'
id 'kotlin-android'
}
android {
compileSdk buildConfig.compileSdk
defaultConfig {
minSdk buildConfig.minSdk
targetSdk buildConfig.targetSdk
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion libs.versions.androidxCompose.get()
}
}
dependencies {
implementation project(':core-model')
implementation libs.androidx.core.ktx
implementation libs.kotlinx.datetime
api libs.androidx.compose.foundation.layout
// TODO (M3): Remove this dependency when all components are available
api libs.androidx.compose.material
api libs.androidx.compose.material.iconsExtended
api libs.androidx.compose.material3
debugApi libs.androidx.compose.ui.tooling
api libs.androidx.compose.ui.tooling.preview
api libs.androidx.compose.ui.util
api libs.androidx.compose.runtime
api libs.androidx.compose.runtime.livedata
}

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.nowinandroid.core.ui">
</manifest>

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui
package com.google.samples.apps.nowinandroid.core.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -36,17 +36,14 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.R
import com.google.samples.apps.nowinandroid.data.fake.FakeDataSource
import com.google.samples.apps.nowinandroid.data.model.NewsResource
import com.google.samples.apps.nowinandroid.data.network.NetworkAuthor
import com.google.samples.apps.nowinandroid.data.network.NetworkEpisode
import com.google.samples.apps.nowinandroid.data.network.NetworkTopic
import com.google.samples.apps.nowinandroid.data.network.asEntity
import com.google.samples.apps.nowinandroid.ui.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.entities.AuthorEntity
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeEntity
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.model.entities.TopicEntity
import com.google.samples.apps.nowinandroid.core.ui.theme.NiaTheme
import kotlinx.datetime.Instant
/**
* [NewsResource] card used on the following screens: For You, Episodes, Saved
@ -166,10 +163,9 @@ fun BookmarkButtonBookmarkedPreview() {
}
}
@Preview("NewsResourceCardExpanded")
@Composable
fun ExpandedNewsResourcePreview(
@PreviewParameter(NewsResourcePreviewParameterProvider::class) newsResource: NewsResource
) {
fun ExpandedNewsResourcePreview() {
NiaTheme {
Surface {
NewsResourceCardExpanded(newsResource, true, {})
@ -177,32 +173,36 @@ fun ExpandedNewsResourcePreview(
}
}
class NewsResourcePreviewParameterProvider : PreviewParameterProvider<NewsResource> {
override val values = sequenceOf(
NewsResource(
FakeDataSource.sampleResource.asEntity(),
NetworkEpisode(
private val newsResource = NewsResource(
NewsResourceEntity(
id = 1,
episodeId = 1,
title = "Title",
content = "Content",
url = "url",
publishDate = Instant.DISTANT_FUTURE,
type = "type",
),
EpisodeEntity(
id = 1,
name = "Now in Android 40",
alternateVideo = null,
alternateAudio = null,
publishDate = FakeDataSource.sampleResource.publishDate
).asEntity(),
name = "Title",
publishDate = Instant.DISTANT_FUTURE,
alternateVideo = "alternateVideo",
alternateAudio = "alternateAudio",
),
listOf(
NetworkAuthor(
AuthorEntity(
id = 1,
name = "Android",
imageUrl = ""
name = "Name",
imageUrl = "imageUrl"
)
.asEntity()
),
listOf(
NetworkTopic(
id = 3,
name = "Performance",
description = ""
).asEntity()
TopicEntity(
id = 1,
name = "Name",
description = "Description",
followed = false
)
)
)
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui
package com.google.samples.apps.nowinandroid.core.ui
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui.theme
package com.google.samples.apps.nowinandroid.core.ui.theme
import androidx.compose.ui.graphics.Color

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui.theme
package com.google.samples.apps.nowinandroid.core.ui.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui.theme
package com.google.samples.apps.nowinandroid.core.ui.theme
import androidx.compose.material3.Typography

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2021 Google LLC
~
~ 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.
-->
<resources>
<string name="bookmark">Bookmark</string>
<string name="unbookmark">Unbookmark</string>
</resources>

@ -0,0 +1 @@
/build

@ -0,0 +1,71 @@
/*
* 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.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
compileSdk buildConfig.compileSdk
defaultConfig {
minSdk buildConfig.minSdk
targetSdk buildConfig.targetSdk
testInstrumentationRunner "com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion libs.versions.androidxCompose.get()
}
}
dependencies {
implementation project(':core-model')
implementation project(':core-ui')
implementation project(':core-domain')
testImplementation project(':core-testing')
androidTestImplementation project(':core-testing')
implementation libs.kotlinx.coroutines.android
implementation libs.androidx.hilt.navigation.compose
implementation libs.androidx.lifecycle.viewModelCompose
implementation libs.hilt.android
kapt libs.hilt.compiler
// androidx.test is forcing JUnit, 4.12. This forces it to use 4.13
configurations.configureEach {
resolutionStrategy {
force libs.junit4
// Temporary workaround for https://issuetracker.google.com/174733673
force 'org.objenesis:objenesis:2.6'
}
}
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui.following
package com.google.samples.apps.nowinandroid.following
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.assertCountEquals
@ -24,8 +24,10 @@ import androidx.compose.ui.test.onAllNodesWithContentDescription
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import com.google.samples.apps.nowinandroid.R
import com.google.samples.apps.nowinandroid.data.model.Topic
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.feature.following.FollowingScreen
import com.google.samples.apps.nowinandroid.feature.following.FollowingUiState
import com.google.samples.apps.nowinandroid.feature.following.R
import org.junit.Before
import org.junit.Rule
import org.junit.Test

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.nowinandroid.feature.following">
</manifest>

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui.following
package com.google.samples.apps.nowinandroid.feature.following
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@ -45,11 +45,10 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.google.samples.apps.nowinandroid.R
import com.google.samples.apps.nowinandroid.data.model.Topic
import com.google.samples.apps.nowinandroid.ui.NiaLoadingIndicator
import com.google.samples.apps.nowinandroid.ui.NiaToolbar
import com.google.samples.apps.nowinandroid.ui.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.ui.NiaLoadingIndicator
import com.google.samples.apps.nowinandroid.core.ui.NiaToolbar
import com.google.samples.apps.nowinandroid.core.ui.theme.NiaTheme
@Composable
fun FollowingRoute(

@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui.following
package com.google.samples.apps.nowinandroid.feature.following
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.data.model.Topic
import com.google.samples.apps.nowinandroid.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2022 Google LLC
~
~ 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.
-->
<resources>
<string name="following">Following</string>
<string name="following_loading">Loading topics</string>
<string name="following_error_header">"Error loading topics"</string>
<string name="following_topic_card_icon_content_desc">Topic icon</string>
<string name="following_topic_card_follow_button_content_desc">Follow Topic button</string>
<string name="following_topic_card_unfollow_button_content_desc">Unfollow Topic button</string>
</resources>

@ -14,12 +14,14 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui.following
package com.google.samples.apps.nowinandroid.following
import app.cash.turbine.test
import com.google.samples.apps.nowinandroid.data.model.Topic
import com.google.samples.apps.nowinandroid.testutil.TestDispatcherRule
import com.google.samples.apps.nowinandroid.testutil.TestTopicsRepository
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository
import com.google.samples.apps.nowinandroid.core.testing.util.TestDispatcherRule
import com.google.samples.apps.nowinandroid.feature.following.FollowingUiState
import com.google.samples.apps.nowinandroid.feature.following.FollowingViewModel
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before

@ -0,0 +1 @@
/build

@ -0,0 +1,73 @@
/*
* 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.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
compileSdk buildConfig.compileSdk
defaultConfig {
minSdk buildConfig.minSdk
targetSdk buildConfig.targetSdk
testInstrumentationRunner "com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion libs.versions.androidxCompose.get()
}
}
dependencies {
implementation project(':core-model')
implementation project(':core-ui')
implementation project(':core-domain')
testImplementation project(':core-testing')
androidTestImplementation project(':core-testing')
implementation libs.kotlinx.coroutines.android
implementation libs.androidx.hilt.navigation.compose
implementation libs.androidx.lifecycle.viewModelCompose
implementation libs.accompanist.flowlayout
implementation libs.hilt.android
kapt libs.hilt.compiler
// androidx.test is forcing JUnit, 4.12. This forces it to use 4.13
configurations.configureEach {
resolutionStrategy {
force libs.junit4
// Temporary workaround for https://issuetracker.google.com/174733673
force 'org.objenesis:objenesis:2.6'
}
}
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui.foryou
package com.google.samples.apps.nowinandroid.feature.foryou
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.assertHasClickAction
@ -26,8 +26,7 @@ import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import com.google.samples.apps.nowinandroid.R
import com.google.samples.apps.nowinandroid.data.model.Topic
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import org.junit.Rule
import org.junit.Test

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.nowinandroid.feature.foryou">
</manifest>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save