Skip to content

Commit 4c4caac

Browse files
committed
Reactive feature flags
1 parent ad08b9e commit 4c4caac

File tree

55 files changed

+631
-58
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+631
-58
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Clean Architecture
22

3-
This is a sample app to show best practices up to authors's knowledge by trying to
3+
This is a sample app to show best practices up to author's knowledge by trying to
44
stay close to standard technologies for a highly scalable, maintainable, and testable
55
Android app.
66

app/build.gradle.kts

+4-2
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,22 @@ android {
5353
}
5454

5555
packagingOptions {
56-
exclude("META-INF/public_release.kotlin_module")
57-
exclude("META-INF/impl_release.kotlin_module")
56+
exclude("META-INF/*.kotlin_module")
5857
}
5958
}
6059

6160
dependencies {
6261
implementation(project(Modules.corePublic))
6362
implementation(project(Modules.coreImpl))
63+
implementation(project(Modules.diPublic))
6464
implementation(project(Modules.singleActivityPublic))
6565
implementation(project(Modules.singleActivityImpl))
6666
implementation(project(Modules.guidomiaPublic))
6767
implementation(project(Modules.guidomiaImpl))
6868
implementation(project(Modules.databasePublic))
6969
implementation(project(Modules.databaseImpl))
70+
implementation(project(Modules.featureFlagsPublic))
71+
implementation(project(Modules.featureFlagsImpl))
7072

7173
kapt(Depends.daggerCompiler)
7274

app/src/main/java/com/hadilq/guidomia/CustomApplication.kt

+21-2
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
package com.hadilq.guidomia
1717

1818
import android.app.Application
19-
import com.hadilq.guidomia.core.api.di.AppScope
20-
import com.hadilq.guidomia.core.api.di.SingleIn
2119
import com.hadilq.guidomia.di.AppComponent
2220
import com.hadilq.guidomia.di.DaggerAppComponent
21+
import com.hadilq.guidomia.di.api.AppScope
22+
import com.hadilq.guidomia.di.api.SingleIn
23+
import com.hadilq.guidomia.featureflags.api.CommandHook
24+
import com.hadilq.guidomia.featureflags.api.CommandRegister
2325
import com.hadilq.guidomia.singleactivity.impl.di.SingleActivityComponent
2426
import com.hadilq.guidomia.singleactivity.impl.di.SingleActivityComponentProvider
27+
import javax.inject.Inject
2528

2629
@SingleIn(AppScope::class)
2730
class CustomApplication : Application(), SingleActivityComponentProvider {
@@ -34,4 +37,20 @@ class CustomApplication : Application(), SingleActivityComponentProvider {
3437

3538
override val singleActivityComponentProvider: SingleActivityComponent.Builder
3639
get() = component.singleActivityComponentBuilder()
40+
41+
@Inject
42+
protected lateinit var commandHookSet: Set<@JvmSuppressWildcards CommandHook>
43+
44+
@Inject
45+
protected lateinit var commandRegister: CommandRegister
46+
47+
override fun onCreate() {
48+
super.onCreate()
49+
component.inject(this)
50+
hookUpFeatureFlags()
51+
}
52+
53+
private fun hookUpFeatureFlags() {
54+
commandHookSet.forEach { it.hookUp(commandRegister) }
55+
}
3756
}

app/src/main/java/com/hadilq/guidomia/di/AppComponent.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
package com.hadilq.guidomia.di
1717

1818
import com.hadilq.guidomia.CustomApplication
19-
import com.hadilq.guidomia.core.api.di.AppScope
20-
import com.hadilq.guidomia.core.api.di.SingleIn
19+
import com.hadilq.guidomia.di.api.AppScope
20+
import com.hadilq.guidomia.di.api.SingleIn
2121
import com.hadilq.guidomia.singleactivity.impl.di.SingleActivityComponent
2222
import com.squareup.anvil.annotations.MergeComponent
2323
import dagger.BindsInstance
@@ -39,6 +39,8 @@ interface AppComponent {
3939
fun build(): AppComponent
4040
}
4141

42+
fun inject(app: CustomApplication)
43+
4244
fun singleActivityComponentBuilder(): SingleActivityComponent.Builder
4345
fun fragmentComponentBuilder(): FragmentComponent.Builder
4446
fun retainComponentBuilder(): RetainComponent.Builder

app/src/main/java/com/hadilq/guidomia/di/AppModule.kt

+5
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,13 @@ package com.hadilq.guidomia.di
1717

1818
import android.content.Context
1919
import com.hadilq.guidomia.CustomApplication
20+
import com.hadilq.guidomia.di.api.AppScope
21+
import com.hadilq.guidomia.featureflags.api.CommandHook
22+
import com.hadilq.guidomia.featureflags.api.CommandRegister
23+
import com.squareup.anvil.annotations.ContributesMultibinding
2024
import dagger.Binds
2125
import dagger.Module
26+
import javax.inject.Inject
2227

2328
@Module
2429
interface AppModule {

app/src/main/java/com/hadilq/guidomia/di/FragmentComponent.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
*/
1616
package com.hadilq.guidomia.di
1717

18-
import com.hadilq.guidomia.core.api.di.FragmentScope
19-
import com.hadilq.guidomia.core.api.di.SingleIn
18+
import com.hadilq.guidomia.di.api.FragmentScope
19+
import com.hadilq.guidomia.di.api.SingleIn
2020
import com.hadilq.guidomia.guidomia.impl.presentation.GuidomiaFragment
2121
import com.squareup.anvil.annotations.MergeSubcomponent
2222
import dagger.Subcomponent

app/src/main/java/com/hadilq/guidomia/di/RetainComponent.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
*/
1616
package com.hadilq.guidomia.di
1717

18-
import com.hadilq.guidomia.core.api.di.RetainScope
19-
import com.hadilq.guidomia.core.api.di.SingleIn
18+
import com.hadilq.guidomia.di.api.RetainScope
19+
import com.hadilq.guidomia.di.api.SingleIn
2020
import com.hadilq.guidomia.guidomia.impl.presentation.GuidomiaViewModel
2121
import com.squareup.anvil.annotations.MergeSubcomponent
2222
import dagger.Subcomponent

app/src/main/java/com/hadilq/guidomia/di/fragment/FragmentFactoryImpl.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package com.hadilq.guidomia.di.fragment
1818
import androidx.fragment.app.Fragment
1919
import com.hadilq.guidomia.core.api.FragmentFactory
2020
import com.hadilq.guidomia.core.api.SimpleFragmentFactory
21-
import com.hadilq.guidomia.core.api.di.AppScope
21+
import com.hadilq.guidomia.di.api.AppScope
2222
import com.squareup.anvil.annotations.ContributesBinding
2323
import javax.inject.Inject
2424
import kotlin.reflect.KClass

app/src/main/java/com/hadilq/guidomia/di/fragment/GuidomiaFragmentFactory.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package com.hadilq.guidomia.di.fragment
1818
import androidx.fragment.app.Fragment
1919
import com.hadilq.guidomia.core.api.FragmentKey
2020
import com.hadilq.guidomia.core.api.SimpleFragmentFactory
21-
import com.hadilq.guidomia.core.api.di.AppScope
21+
import com.hadilq.guidomia.di.api.AppScope
2222
import com.hadilq.guidomia.guidomia.impl.presentation.GuidomiaFragment
2323
import com.hadilq.guidomia.di.FragmentComponent
2424
import com.squareup.anvil.annotations.ContributesMultibinding

app/src/main/java/com/hadilq/guidomia/di/viewmodel/GuidomiaViewModelFactory.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package com.hadilq.guidomia.di.viewmodel
1818
import androidx.lifecycle.ViewModel
1919
import com.hadilq.guidomia.core.api.SimpleViewModelFactory
2020
import com.hadilq.guidomia.core.api.ViewModelKey
21-
import com.hadilq.guidomia.core.api.di.AppScope
21+
import com.hadilq.guidomia.di.api.AppScope
2222
import com.hadilq.guidomia.guidomia.impl.presentation.GuidomiaViewModel
2323
import com.hadilq.guidomia.di.RetainComponent
2424
import com.squareup.anvil.annotations.ContributesMultibinding

app/src/main/java/com/hadilq/guidomia/di/viewmodel/ViewModelFactoryImpl.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package com.hadilq.guidomia.di.viewmodel
1818
import androidx.lifecycle.ViewModel
1919
import com.hadilq.guidomia.core.api.SimpleViewModelFactory
2020
import com.hadilq.guidomia.core.api.ViewModelFactory
21-
import com.hadilq.guidomia.core.api.di.AppScope
21+
import com.hadilq.guidomia.di.api.AppScope
2222
import com.squareup.anvil.annotations.ContributesBinding
2323
import javax.inject.Inject
2424

buildSrc/src/main/kotlin/Depends.kt

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
object Depends {
1717
const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlin}"
18+
const val kotlinTest = "org.jetbrains.kotlin:kotlin-test:${Versions.kotlin}"
1819
const val coreKtx = "androidx.core:core-ktx:${Versions.coreKtx}"
1920
const val appCompat = "androidx.appcompat:appcompat:${Versions.appCompat}"
2021
const val material = "com.google.android.material:material:${Versions.material}"

buildSrc/src/main/kotlin/Modules.kt

+3
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@
1616
object Modules {
1717
const val corePublic = ":core:public"
1818
const val coreImpl = ":core:impl"
19+
const val diPublic = ":di:public"
1920
const val singleActivityPublic = ":single-activity:public"
2021
const val singleActivityImpl = ":single-activity:impl"
2122
const val guidomiaPublic = ":guidomia:public"
2223
const val guidomiaImpl = ":guidomia:impl"
2324
const val databaseImpl = ":database:impl"
2425
const val databasePublic = ":database:public"
26+
const val featureFlagsImpl = ":feature-flags:impl"
27+
const val featureFlagsPublic = ":feature-flags:public"
2528
}

buildSrc/src/main/kotlin/Versions.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ object Versions {
2828
const val recyclerView = "1.1.0"
2929
const val fragment = "1.3.2"
3030
const val dagger = "2.34.1"
31-
const val anvil = "2.2.1"
31+
const val anvil = "2.3.0"
3232
const val coroutines = "1.4.3"
3333
const val serialization = "1.4.30"
3434
const val jsonSerialization = "1.1.0"

core/impl/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ android {
3030

3131
dependencies {
3232
implementation(project(Modules.corePublic))
33+
implementation(project(Modules.diPublic))
3334
implementation(project(Modules.guidomiaPublic))
3435

3536
kapt(Depends.daggerCompiler)

core/impl/src/main/java/com/hadilq/guidomia/core/impl/DispatcherProviderImpl.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
package com.hadilq.guidomia.core.impl
1717

1818
import com.hadilq.guidomia.core.api.DispatcherProvider
19-
import com.hadilq.guidomia.core.api.di.AppScope
19+
import com.hadilq.guidomia.di.api.AppScope
2020
import com.squareup.anvil.annotations.ContributesBinding
2121
import kotlinx.coroutines.CoroutineDispatcher
2222
import kotlinx.coroutines.Dispatchers

database/impl/build.gradle.kts

+2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ android {
3838

3939
dependencies {
4040
implementation(project(Modules.corePublic))
41+
implementation(project(Modules.diPublic))
4142
implementation(project(Modules.databasePublic))
43+
implementation(project(Modules.featureFlagsPublic))
4244

4345
kapt(Depends.daggerCompiler)
4446
kapt(Depends.roomCompiler)

database/impl/src/main/java/com/hadilq/guidomia/database/impl/CarDataEntityCommandImpl.kt

+32-9
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,27 @@
1515
*/
1616
package com.hadilq.guidomia.database.impl
1717

18-
import com.hadilq.guidomia.core.api.di.AppScope
19-
import com.hadilq.guidomia.database.api.CarDataEntityCommand
20-
import com.hadilq.guidomia.database.api.CarDatabaseEntity
21-
import com.squareup.anvil.annotations.ContributesBinding
18+
import com.hadilq.guidomia.database.api.*
19+
import com.hadilq.guidomia.di.api.AppScope
20+
import com.hadilq.guidomia.featureflags.api.CommandCallbackImpl
21+
import com.hadilq.guidomia.featureflags.api.CommandHook
22+
import com.hadilq.guidomia.featureflags.api.CommandRegister
23+
import com.hadilq.guidomia.featureflags.api.CommandShooter
24+
import com.squareup.anvil.annotations.ContributesMultibinding
2225
import javax.inject.Inject
2326

24-
@ContributesBinding(AppScope::class)
27+
@ContributesMultibinding(AppScope::class)
2528
class CarDataEntityCommandImpl @Inject constructor(
2629
private val carDao: CarDao,
2730
private val carEntityMapper: CarEntityMapper,
28-
) : CarDataEntityCommand {
31+
private val commandShooter: CommandShooter,
32+
) : CommandHook {
2933

30-
override suspend fun getAll(): List<CarDatabaseEntity> =
34+
private suspend fun getAll(): List<CarDatabaseEntity> =
3135
carDao.getAll().map { carEntityMapper.map(it) }
3236

3337

34-
override suspend fun insertAll(cars: List<CarDatabaseEntity>) {
38+
private suspend fun insertAll(cars: List<CarDatabaseEntity>) {
3539
val carEntities = carDao.insertCarEntity(
3640
*cars.map { carEntityMapper.mapToCarEntity(it) }.toTypedArray()
3741
).mapIndexed { index, id ->
@@ -47,5 +51,24 @@ class CarDataEntityCommandImpl @Inject constructor(
4751
}.toTypedArray())
4852
}
4953

50-
override suspend fun isEmpty(): Boolean = carDao.getAll().isEmpty()
54+
private suspend fun isEmpty(): Boolean = carDao.getAll().isEmpty()
55+
56+
override fun hookUp(commandRegister: CommandRegister) {
57+
58+
commandRegister.register(CarDataEntityCommandGetAll::class,
59+
CommandCallbackImpl(commandShooter, CarDataEntityCommandGetAllResult::class) {
60+
CarDataEntityCommandGetAllResult(getAll())
61+
})
62+
63+
commandRegister.register(CarDataEntityCommandInsertAll::class,
64+
CommandCallbackImpl(commandShooter, CarDataEntityCommandInsertAllResult::class) {
65+
insertAll(it.cars)
66+
CarDataEntityCommandInsertAllResult()
67+
})
68+
69+
commandRegister.register(CarDataEntityCommandIsEmpty::class,
70+
CommandCallbackImpl(commandShooter, CarDataEntityCommandIsEmptyResult::class) {
71+
CarDataEntityCommandIsEmptyResult(isEmpty())
72+
})
73+
}
5174
}

database/impl/src/main/java/com/hadilq/guidomia/database/impl/di/DatabaseModule.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ package com.hadilq.guidomia.database.impl.di
1717

1818
import android.content.Context
1919
import androidx.room.Room
20-
import com.hadilq.guidomia.core.api.di.AppScope
21-
import com.hadilq.guidomia.core.api.di.SingleIn
20+
import com.hadilq.guidomia.di.api.AppScope
21+
import com.hadilq.guidomia.di.api.SingleIn
2222
import com.hadilq.guidomia.database.impl.AppDatabase
2323
import com.squareup.anvil.annotations.ContributesTo
2424
import dagger.Module

database/public/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ plugins {
1919

2020
dependencies {
2121
implementation(Depends.kotlinStdLib)
22+
implementation(project(Modules.featureFlagsPublic))
2223
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.hadilq.guidomia.database.api
2+
3+
import com.hadilq.guidomia.featureflags.api.Command
4+
5+
class CarDataEntityCommandGetAll : Command
6+
7+
class CarDataEntityCommandGetAllResult(
8+
val result: List<CarDatabaseEntity>
9+
) : Command
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.hadilq.guidomia.database.api
2+
3+
import com.hadilq.guidomia.featureflags.api.Command
4+
5+
class CarDataEntityCommandInsertAll(
6+
val cars: List<CarDatabaseEntity>
7+
) : Command
8+
9+
class CarDataEntityCommandInsertAllResult : Command
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.hadilq.guidomia.database.api
2+
3+
import com.hadilq.guidomia.featureflags.api.Command
4+
5+
class CarDataEntityCommandIsEmpty : Command
6+
7+
class CarDataEntityCommandIsEmptyResult(
8+
val result: Boolean
9+
) : Command

di/public/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.hadilq.guidomia.database.api
17-
18-
interface CarDataEntityCommand {
19-
20-
suspend fun getAll(): List<CarDatabaseEntity>
21-
22-
suspend fun insertAll(cars: List<CarDatabaseEntity>)
16+
plugins {
17+
kotlin("jvm")
18+
}
2319

24-
suspend fun isEmpty(): Boolean
20+
dependencies {
21+
implementation(Depends.kotlinStdLib)
22+
implementation(Depends.dagger)
2523
}

core/public/src/main/java/com/hadilq/guidomia/core/api/di/AppScope.kt di/public/src/main/java/com/hadilq/guidomia/di/api/AppScope.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.hadilq.guidomia.core.api.di
16+
package com.hadilq.guidomia.di.api
1717

1818
abstract class AppScope private constructor()

core/public/src/main/java/com/hadilq/guidomia/core/api/di/FragmentScope.kt di/public/src/main/java/com/hadilq/guidomia/di/api/FragmentScope.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.hadilq.guidomia.core.api.di
16+
package com.hadilq.guidomia.di.api
1717

1818
abstract class FragmentScope private constructor()

core/public/src/main/java/com/hadilq/guidomia/core/api/di/RetainScope.kt di/public/src/main/java/com/hadilq/guidomia/di/api/RetainScope.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.hadilq.guidomia.core.api.di
16+
package com.hadilq.guidomia.di.api
1717

1818
abstract class RetainScope private constructor()

core/public/src/main/java/com/hadilq/guidomia/core/api/di/SingleActivityScope.kt di/public/src/main/java/com/hadilq/guidomia/di/api/SingleActivityScope.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.hadilq.guidomia.core.api.di
16+
package com.hadilq.guidomia.di.api
1717

1818
abstract class SingleActivityScope private constructor()

core/public/src/main/java/com/hadilq/guidomia/core/api/di/SingleIn.kt di/public/src/main/java/com/hadilq/guidomia/di/api/SingleIn.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.hadilq.guidomia.core.api.di
16+
package com.hadilq.guidomia.di.api
1717

1818
import javax.inject.Scope
1919
import kotlin.reflect.KClass

feature-flags/impl/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

0 commit comments

Comments
 (0)