├── .gitignore
├── .idea
├── .gitignore
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
└── compiler.xml
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── hadilq
│ │ └── guidomia
│ │ ├── CustomApplication.kt
│ │ └── di
│ │ ├── AppComponent.kt
│ │ ├── AppModule.kt
│ │ ├── fragment
│ │ └── FragmentFactoryImpl.kt
│ │ └── viewmodel
│ │ └── ViewModelFactoryImpl.kt
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
├── build.gradle.kts
├── buildSrc
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── kotlin
│ ├── ConfigAndroidLibrary.kt
│ ├── Depends.kt
│ ├── Modules.kt
│ ├── Versions.kt
│ └── featureflags
│ ├── FeatureFlags.kt
│ └── Scenario.kt
├── core
├── impl
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── hadilq
│ │ │ └── guidomia
│ │ │ └── core
│ │ │ └── impl
│ │ │ └── DispatcherProviderImpl.kt
│ │ └── res
│ │ └── values
│ │ └── strings.xml
└── public
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── hadilq
│ │ └── guidomia
│ │ └── core
│ │ └── api
│ │ ├── DispatcherProvider.kt
│ │ ├── FragmentFactory.kt
│ │ ├── FragmentKey.kt
│ │ ├── SimpleFragmentFactory.kt
│ │ ├── SimpleViewModelFactory.kt
│ │ ├── ViewBinding.kt
│ │ ├── ViewModelFactory.kt
│ │ └── ViewModelKey.kt
│ └── res
│ ├── values-night
│ └── themes.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── themes.xml
├── database
├── impl
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── hadilq
│ │ └── guidomia
│ │ └── database
│ │ └── impl
│ │ ├── AppDatabase.kt
│ │ ├── CarDao.kt
│ │ ├── CarDataEntityCommandHook.kt
│ │ ├── CarDataEntityCommandImpl.kt
│ │ ├── CarEntityMapper.kt
│ │ ├── di
│ │ └── DatabaseModule.kt
│ │ └── entiry
│ │ ├── CarEntity.kt
│ │ ├── CarWithProsCons.kt
│ │ └── ProConEntity.kt
└── public
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── java
│ └── com
│ └── hadilq
│ └── guidomia
│ └── database
│ └── api
│ ├── CarDataEntity.kt
│ ├── CarDataEntityCommand.kt
│ └── GetCarEntityCommand.kt
├── di
└── public
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── java
│ └── com
│ └── hadilq
│ └── guidomia
│ └── di
│ └── api
│ ├── AppScope.kt
│ ├── FragmentScope.kt
│ ├── RetainScope.kt
│ ├── SingleActivityScope.kt
│ └── SingleIn.kt
├── doc-image
└── dip-module-dependencies-switch.png
├── feature-flags
├── impl
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── hadilq
│ │ └── guidomia
│ │ └── featureflags
│ │ └── impl
│ │ └── CommandKuModule.kt
└── public
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── java
│ └── com
│ └── hadilq
│ └── guidomia
│ └── featureflags
│ └── api
│ └── CommandExecutor.kt
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── guidomia
├── impl
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── assets
│ │ │ └── car_list.json
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── hadilq
│ │ │ │ └── guidomia
│ │ │ │ └── guidomia
│ │ │ │ └── impl
│ │ │ │ ├── GuidomiaCommandHook.kt
│ │ │ │ ├── data
│ │ │ │ ├── datasource
│ │ │ │ │ ├── CarDatabaseDataSource.kt
│ │ │ │ │ ├── CarsCacheDataSource.kt
│ │ │ │ │ └── CarsDataSource.kt
│ │ │ │ ├── entity
│ │ │ │ │ └── CarDataEntity.kt
│ │ │ │ ├── mapper
│ │ │ │ │ ├── CarDataMapper.kt
│ │ │ │ │ └── CarDatabaseMapper.kt
│ │ │ │ └── repository
│ │ │ │ │ └── CarsRepository.kt
│ │ │ │ ├── domain
│ │ │ │ ├── entity
│ │ │ │ │ ├── CarEntity.kt
│ │ │ │ │ ├── FilterEntity.kt
│ │ │ │ │ ├── ImageEntity.kt
│ │ │ │ │ ├── MakeEntity.kt
│ │ │ │ │ ├── ModelEntity.kt
│ │ │ │ │ ├── PriceEntity.kt
│ │ │ │ │ └── RateEntity.kt
│ │ │ │ └── usecase
│ │ │ │ │ ├── GetCars.kt
│ │ │ │ │ └── GetFilteredCars.kt
│ │ │ │ └── presentation
│ │ │ │ ├── CarItemFilter.kt
│ │ │ │ ├── CarItemOnClick.kt
│ │ │ │ ├── CarModel.kt
│ │ │ │ ├── CarModelMapper.kt
│ │ │ │ ├── GuidomiaFragment.kt
│ │ │ │ ├── GuidomiaNavigatorImpl.kt
│ │ │ │ ├── GuidomiaRecyclerAdapter.kt
│ │ │ │ ├── GuidomiaViewModel.kt
│ │ │ │ └── di
│ │ │ │ ├── GuidomiaFragmentComponent.kt
│ │ │ │ └── GuidomiaModule.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ ├── alpine_roadster.jpg
│ │ │ ├── bmw_330i.jpg
│ │ │ ├── filter_background.xml
│ │ │ ├── filter_edittext_background.xml
│ │ │ ├── ic_baseline_dehaze_24.xml
│ │ │ ├── logo.png
│ │ │ ├── mercedez_benz_glc.jpg
│ │ │ ├── pros_cons_dot.xml
│ │ │ ├── range_rover.jpg
│ │ │ ├── star.png
│ │ │ └── tacoma.jpg
│ │ │ ├── layout
│ │ │ ├── car_item.xml
│ │ │ ├── filter_item.xml
│ │ │ ├── fragment_guidomia.xml
│ │ │ ├── line_item.xml
│ │ │ └── pros_cons_item.xml
│ │ │ └── values
│ │ │ ├── dimens.xml
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── hadilq
│ │ └── guidomia
│ │ └── guidomia
│ │ └── impl
│ │ ├── data
│ │ └── repository
│ │ │ └── CarRepositoryTest.kt
│ │ └── domain
│ │ ├── entity
│ │ ├── CarEntityProvider.kt
│ │ ├── MakeEntityProvider.kt
│ │ └── ModelEntityProvider.kt
│ │ └── usecase
│ │ └── GetFilteredCarsTest.kt
└── public
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── hadilq
│ └── guidomia
│ └── guidomia
│ └── api
│ ├── GetGuidomiaNavigatorFactoryCommand.kt
│ ├── GuidomiaNavigator.kt
│ └── GuidomiaNavigatorFactory.kt
├── settings.gradle.kts
└── single-activity
├── impl
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── hadilq
│ │ └── guidomia
│ │ └── singleactivity
│ │ └── impl
│ │ ├── NavigatorImpl.kt
│ │ ├── SingleActivity.kt
│ │ └── di
│ │ ├── SingleActivityComponent.kt
│ │ └── SingleActivityComponentProvider.kt
│ └── res
│ └── layout
│ └── activity_main.xml
└── public
├── .gitignore
├── build.gradle.kts
└── src
└── main
├── AndroidManifest.xml
└── java
└── com
└── hadilq
└── guidomia
└── singleactivity
└── api
├── Navigator.kt
└── NavigatorFactory.kt
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | .DS_Store
5 | /build
6 | /captures
7 | .externalNativeBuild
8 | .cxx
9 | local.properties
10 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | caches
5 | libraries
6 | modules.xml
7 | workspace.xml
8 | navEditor.xml
9 | assetWizardSettings.xml
10 | jarRepositories.xml
11 | gradle.xml
12 | misc.xml
13 | runConfigurations.xml
14 | vcs.xml
15 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | xmlns:android
20 |
21 | ^$
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | xmlns:.*
31 |
32 | ^$
33 |
34 |
35 | BY_NAME
36 |
37 |
38 |
39 |
40 |
41 |
42 | .*:id
43 |
44 | http://schemas.android.com/apk/res/android
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | .*:name
54 |
55 | http://schemas.android.com/apk/res/android
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | name
65 |
66 | ^$
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | style
76 |
77 | ^$
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | .*
87 |
88 | ^$
89 |
90 |
91 | BY_NAME
92 |
93 |
94 |
95 |
96 |
97 |
98 | .*
99 |
100 | http://schemas.android.com/apk/res/android
101 |
102 |
103 | ANDROID_ATTRIBUTE_ORDER
104 |
105 |
106 |
107 |
108 |
109 |
110 | .*
111 |
112 | .*
113 |
114 |
115 | BY_NAME
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Clean Architecture
2 |
3 | This is a sample app to show best practices up to author's knowledge by trying to
4 | stay close to standard technologies for a highly scalable, maintainable, and testable
5 | Android app.
6 |
7 | ## Technologies
8 |
9 | - Clean Architecture
10 | - Public/Impl modules to handle Dependency Inversion Principle on module level
11 | - Anvil to handle Inversion of Control
12 | - Compile time feature flags
13 | - Dagger to handle Dependency Injection
14 | - Single Activity for faster initialization of pages
15 | - AndroidX
16 | - MVVM
17 | - Kotlin Coroutines
18 | - KotlinX Serialization
19 | - Room database
20 | - JUnit 5
21 | - Mockk
22 |
23 | ### Dependency Inversion Principle
24 |
25 | All modules are depending on each other only by `:public` interfaces, except the `:app`
26 | where is the only user of `:impl` modules. Also by keeping the `:app` as light as possible,
27 | rebuilding time of any `:impl` module will be short.
28 |
29 | ### Dagger
30 |
31 | Defined scopes in Dagger
32 | - AppScope
33 | - SingleActivityScope
34 | - FragmentScope
35 | - RetainScope
36 |
37 | ### Modules
38 | Here we have one root module, which is the `:app`, and one feature module `:guidomia` that
39 | is following clean architecture practices.
40 | Other modules considered as library modules.
41 |
42 | - core
43 | - database
44 | - single-activity
45 | - di
46 | - feature-flags
47 |
48 | For your convenient you may want to put them in separated directories, such as `features`, `libraries`, etc.
49 |
50 | ### Feature Flags
51 | The idea is to switch on/off modules in compile time. We call this concept compile time feature
52 | flags, because they use the same logic of runtime feature flags, the concept that we had before.
53 | Here we have switches for `:database` and `:guidomia` modules, one library module and one feature
54 | module. In this project, where you can find part of its dependency tree here
55 |
56 | 
57 |
58 | the feature flag logic to handle switch off of `:database:impl` is in the `:guidomia:impl`, and
59 | feature flag logic to handle switch off of `:guimomia:impl` is in `:single-activity:impl`.
60 | To switch them on and off, we developed this sample to use "scenario" concept. You can put
61 | `scenario` variable like
62 |
63 | ```properties
64 | scenario=>guidomia>database
65 | ```
66 |
67 | in `local.properties` file. It will enable `guidomia` and `database` modules, and it's the default
68 | `scenario`, when the developer didn't provide any `scenario` variable in the `local.properties`.
69 | For this project there are two different `scenario`s that developer can use to decrease the
70 | overall build time of the project. They are as following.
71 |
72 | ```properties
73 | scenario=>guidomia
74 | ```
75 |
76 | or
77 |
78 | ```properties
79 | scenario=>
80 | ```
81 |
82 | The last one is the root scenario that switch off all plugable modules in this project. Plugable
83 | modules are the one that has a switch.
84 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import featureflags.installScenario
17 | import featureflags.loadScenarioFromLocal
18 | import featureflags.scenarios
19 |
20 |
21 | plugins {
22 | id("com.android.application")
23 | kotlin("android")
24 | kotlin("kapt")
25 | id("com.squareup.anvil") version Versions.anvil
26 | }
27 |
28 | android {
29 | compileSdkVersion(Versions.compileSdk)
30 | defaultConfig {
31 | applicationId = "com.hadilq.guidomia"
32 | minSdkVersion(Versions.minSdk)
33 | targetSdkVersion(Versions.targetSdk)
34 | versionCode = 1
35 | versionName = "1.0"
36 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
37 | }
38 |
39 | buildTypes {
40 | getByName("debug") {
41 | matchingFallbacks.add("release")
42 | }
43 | getByName("release") {
44 | isMinifyEnabled = false
45 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
46 | }
47 | }
48 |
49 | compileOptions {
50 | sourceCompatibility(JavaVersion.VERSION_1_8)
51 | targetCompatibility(JavaVersion.VERSION_1_8)
52 | }
53 |
54 | kotlinOptions {
55 | jvmTarget = JavaVersion.VERSION_1_8.toString()
56 | freeCompilerArgs = listOf("-Xinline-classes")
57 | }
58 |
59 | packagingOptions {
60 | exclude("META-INF/*.kotlin_module")
61 | }
62 | }
63 |
64 | dependencies {
65 | installScenario(scenarios, project.loadScenarioFromLocal())
66 |
67 | implementation(project(Modules.corePublic))
68 | implementation(project(Modules.coreImpl))
69 | implementation(project(Modules.diPublic))
70 | implementation(project(Modules.singleActivityPublic))
71 | implementation(project(Modules.singleActivityImpl))
72 | implementation(project(Modules.guidomiaPublic))
73 | implementation(project(Modules.databasePublic))
74 | implementation(project(Modules.featureFlagsPublic))
75 | implementation(project(Modules.featureFlagsImpl))
76 |
77 | kapt(Depends.daggerCompiler)
78 |
79 | implementation(Depends.kotlinStdLib)
80 | implementation(Depends.appCompat)
81 | implementation(Depends.dagger)
82 | implementation(Depends.recyclerView)
83 | }
84 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hadilq/guidomia/CustomApplication.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia
17 |
18 | import android.app.Application
19 | import com.github.hadilq.commandku.api.CommandHook
20 | import com.github.hadilq.commandku.api.CommandRegister
21 | import com.hadilq.guidomia.di.AppComponent
22 | import com.hadilq.guidomia.di.DaggerAppComponent
23 | import com.hadilq.guidomia.di.api.AppScope
24 | import com.hadilq.guidomia.di.api.SingleIn
25 | import com.hadilq.guidomia.singleactivity.impl.di.SingleActivityComponent
26 | import com.hadilq.guidomia.singleactivity.impl.di.SingleActivityComponentProvider
27 | import javax.inject.Inject
28 |
29 | @SingleIn(AppScope::class)
30 | class CustomApplication : Application(), SingleActivityComponentProvider {
31 |
32 | private val component: AppComponent by lazy {
33 | DaggerAppComponent.builder()
34 | .application(this)
35 | .build()
36 | }
37 |
38 | override val singleActivityComponentProvider: SingleActivityComponent.Builder
39 | 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 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/hadilq/guidomia/di/AppComponent.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.di
17 |
18 | import com.hadilq.guidomia.CustomApplication
19 | import com.hadilq.guidomia.di.api.AppScope
20 | import com.hadilq.guidomia.di.api.SingleIn
21 | import com.hadilq.guidomia.singleactivity.impl.di.SingleActivityComponent
22 | import com.squareup.anvil.annotations.MergeComponent
23 | import dagger.BindsInstance
24 | import dagger.Component
25 |
26 | @SingleIn(AppScope::class)
27 | @MergeComponent(
28 | scope = AppScope::class,
29 | modules = [
30 | AppModule::class
31 | ]
32 | )
33 | interface AppComponent {
34 |
35 | @Component.Builder
36 | interface Builder {
37 | @BindsInstance
38 | fun application(customApplication: CustomApplication): Builder
39 | fun build(): AppComponent
40 | }
41 |
42 | fun inject(app: CustomApplication)
43 |
44 | fun singleActivityComponentBuilder(): SingleActivityComponent.Builder
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hadilq/guidomia/di/AppModule.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.di
17 |
18 | import android.content.Context
19 | import com.github.hadilq.commandku.api.CommandHook
20 | import com.github.hadilq.commandku.api.CommandRegister
21 | import com.hadilq.guidomia.CustomApplication
22 | import com.hadilq.guidomia.di.api.AppScope
23 | import com.squareup.anvil.annotations.ContributesMultibinding
24 | import dagger.Binds
25 | import dagger.Module
26 | import javax.inject.Inject
27 |
28 | @Module
29 | interface AppModule {
30 |
31 | @Binds
32 | fun provideContext(app: CustomApplication): Context
33 | }
34 |
35 | @ContributesMultibinding(AppScope::class)
36 | class EmptyHook @Inject constructor() : CommandHook {
37 | override fun hookUp(commandRegister: CommandRegister) {
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hadilq/guidomia/di/fragment/FragmentFactoryImpl.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.di.fragment
17 |
18 | import androidx.fragment.app.Fragment
19 | import com.hadilq.guidomia.core.api.FragmentFactory
20 | import com.hadilq.guidomia.core.api.SimpleFragmentFactory
21 | import com.hadilq.guidomia.di.api.AppScope
22 | import com.squareup.anvil.annotations.ContributesBinding
23 | import javax.inject.Inject
24 | import kotlin.reflect.KClass
25 |
26 | @ContributesBinding(AppScope::class)
27 | class FragmentFactoryImpl @Inject constructor(
28 | private val creators: Map, @JvmSuppressWildcards SimpleFragmentFactory>
29 | ) : FragmentFactory {
30 |
31 | override fun instantiate(clazz: KClass): Fragment =
32 | creators[clazz.java]!!.instantiate()
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hadilq/guidomia/di/viewmodel/ViewModelFactoryImpl.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.di.viewmodel
17 |
18 | import androidx.lifecycle.ViewModel
19 | import com.hadilq.guidomia.core.api.SimpleViewModelFactory
20 | import com.hadilq.guidomia.core.api.ViewModelFactory
21 | import com.hadilq.guidomia.di.api.AppScope
22 | import com.squareup.anvil.annotations.ContributesBinding
23 | import javax.inject.Inject
24 |
25 | @ContributesBinding(AppScope::class)
26 | class ViewModelFactoryImpl @Inject constructor(
27 | private val creators: Map, @JvmSuppressWildcards SimpleViewModelFactory>
28 | ) : ViewModelFactory {
29 |
30 | @Suppress("UNCHECKED_CAST")
31 | override fun create(modelClass: Class): T =
32 | creators[modelClass]!!.create() as T
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | buildscript {
17 | repositories {
18 | google()
19 | mavenCentral()
20 | }
21 | dependencies {
22 | classpath("com.android.tools.build:gradle:4.1.3") // Change the version in buildSrc dependencies too.
23 | classpath(kotlin("gradle-plugin", version = Versions.kotlin))
24 | classpath("de.mannodermaus.gradle.plugins:android-junit5:1.7.1.1")
25 | }
26 | }
27 |
28 | allprojects {
29 | repositories {
30 | google()
31 | mavenCentral()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/buildSrc/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | plugins {
17 | `kotlin-dsl`
18 | }
19 |
20 | repositories {
21 | google()
22 | mavenCentral()
23 | }
24 |
25 | dependencies {
26 | implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.32")
27 | implementation("com.android.tools.build:gradle:4.1.3")
28 | }
29 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/ConfigAndroidLibrary.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import com.android.build.gradle.LibraryExtension
17 | import com.android.builder.core.BuilderConstants
18 | import org.gradle.api.Action
19 | import org.gradle.api.JavaVersion
20 | import org.gradle.api.Project
21 | import org.gradle.api.plugins.ExtensionAware
22 | import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
23 |
24 | fun Project.configureAndroidLibrary() {
25 | androidLibrary {
26 | compileSdkVersion(Versions.compileSdk)
27 |
28 | defaultConfig {
29 | minSdkVersion(Versions.minSdk)
30 | targetSdkVersion(Versions.targetSdk)
31 | }
32 |
33 | buildTypes {
34 | getByName("release") {
35 | isMinifyEnabled = false
36 | }
37 | }
38 |
39 | compileOptions {
40 | sourceCompatibility(JavaVersion.VERSION_1_8)
41 | targetCompatibility(JavaVersion.VERSION_1_8)
42 | }
43 |
44 | kotlinOptions {
45 | jvmTarget = JavaVersion.VERSION_1_8.toString()
46 | freeCompilerArgs = listOf("-Xinline-classes")
47 | }
48 |
49 | variantFilter {
50 | if (buildType.name == BuilderConstants.DEBUG) {
51 | ignore = true
52 | }
53 | }
54 |
55 | buildFeatures {
56 | aidl = false
57 | dataBinding = false
58 | viewBinding = false
59 | shaders = false
60 | buildConfig = false
61 | renderScript = false
62 | }
63 | }
64 | }
65 |
66 | /**
67 | * Configures the [android][LibraryExtension] extension.
68 | */
69 | fun Project.androidLibrary(configure: Action): Unit =
70 | (this as ExtensionAware).extensions.configure("android", configure)
71 |
72 | /**
73 | * Configures the [kotlinOptions][KotlinJvmOptions] extension.
74 | */
75 | fun LibraryExtension.kotlinOptions(configure: Action): Unit =
76 | (this as ExtensionAware).extensions.configure("kotlinOptions", configure)
77 |
78 |
79 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/Depends.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | object Depends {
17 | const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlin}"
18 | const val kotlinTest = "org.jetbrains.kotlin:kotlin-test:${Versions.kotlin}"
19 | const val coreKtx = "androidx.core:core-ktx:${Versions.coreKtx}"
20 | const val appCompat = "androidx.appcompat:appcompat:${Versions.appCompat}"
21 | const val material = "com.google.android.material:material:${Versions.material}"
22 | const val constraintLayout =
23 | "androidx.constraintlayout:constraintlayout:${Versions.constraintLayout}"
24 | const val lifecycleCompiler = "androidx.lifecycle:lifecycle-compiler:${Versions.lifecycle}"
25 | const val fragment = "androidx.fragment:fragment-ktx:${Versions.fragment}"
26 | const val viewModel = "androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.lifecycle}"
27 | const val recyclerView = "androidx.recyclerview:recyclerview:${Versions.recyclerView}"
28 | const val daggerCompiler = "com.google.dagger:dagger-compiler:${Versions.dagger}"
29 | const val dagger = "com.google.dagger:dagger:${Versions.dagger}"
30 | const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.coroutines}"
31 | const val jsonSerialization =
32 | "org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.jsonSerialization}"
33 | const val roomRuntime = "androidx.room:room-runtime:${Versions.room}"
34 | const val roomCompiler = "androidx.room:room-compiler:${Versions.room}"
35 | const val roomKtx = "androidx.room:room-ktx:${Versions.room}"
36 | const val commandKuApi = "com.github.hadilq:command-ku-api:${Versions.commandKu}"
37 | const val commandKuImpl = "com.github.hadilq:command-ku-impl:${Versions.commandKu}"
38 |
39 | const val junit = "junit:junit:${Versions.junit}"
40 | const val junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${Versions.junitJupiter}"
41 | const val junitJupiterEngine = "org.junit.jupiter:junit-jupiter-engine:${Versions.junitJupiter}"
42 | const val mockk = "io.mockk:mockk:${Versions.mockk}"
43 | const val testExtJunit = "androidx.test.ext:junit:${Versions.testExtJunit}"
44 | const val espressoCore = "androidx.test.espresso:espresso-core:${Versions.espressoCore}"
45 | }
46 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/Modules.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | object Modules {
17 | const val corePublic = ":core:public"
18 | const val coreImpl = ":core:impl"
19 | const val diPublic = ":di:public"
20 | const val singleActivityPublic = ":single-activity:public"
21 | const val singleActivityImpl = ":single-activity:impl"
22 | const val guidomiaPublic = ":guidomia:public"
23 | const val guidomiaImpl = ":guidomia:impl"
24 | const val databaseImpl = ":database:impl"
25 | const val databasePublic = ":database:public"
26 | const val featureFlagsImpl = ":feature-flags:impl"
27 | const val featureFlagsPublic = ":feature-flags:public"
28 | }
29 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/Versions.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | object Versions {
17 | const val compileSdk = 30
18 | const val targetSdk = 30
19 | const val minSdk = 16
20 |
21 | const val kotlin = "1.4.32" // Change the version in buildSrc dependencies too.
22 |
23 | const val coreKtx = "1.3.2"
24 | const val appCompat = "1.2.0"
25 | const val material = "1.3.0"
26 | const val constraintLayout = "2.0.4"
27 | const val lifecycle = "2.3.1"
28 | const val recyclerView = "1.1.0"
29 | const val fragment = "1.3.2"
30 | const val dagger = "2.34.1"
31 | const val anvil = "2.2.1"
32 | const val coroutines = "1.4.3"
33 | const val serialization = "1.4.30"
34 | const val jsonSerialization = "1.1.0"
35 | const val room = "2.2.6"
36 | const val commandKu = "0.1.0"
37 |
38 | const val junit = "4.13.2"
39 | const val junitJupiter = "5.7.1"
40 | const val mockk = "1.11.0"
41 | const val testExtJunit = "1.1.2"
42 | const val espressoCore = "3.3.0"
43 | }
44 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/featureflags/FeatureFlags.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package featureflags
17 |
18 | import org.gradle.api.Project
19 | import org.gradle.api.artifacts.Dependency
20 | import org.gradle.api.artifacts.ProjectDependency
21 | import org.gradle.api.artifacts.dsl.DependencyHandler
22 | import org.gradle.kotlin.dsl.DependencyHandlerScope
23 | import org.gradle.kotlin.dsl.add
24 | import java.util.*
25 |
26 | fun DependencyHandlerScope.installScenario(
27 | scenarios: Map>,
28 | scenario: String
29 | ) {
30 | if (scenario.isNullOrBlank()) return
31 | println("Installing scenario: '$scenario'")
32 | val trimScenario = if (scenario.endsWith(SCENARIO_DELIMITER) && scenario != SCENARIO_DELIMITER) {
33 | scenario.substringBeforeLast(SCENARIO_DELIMITER)
34 | } else {
35 | scenario
36 | }
37 | installScenario(scenarios, trimScenario.substringBeforeLast(SCENARIO_DELIMITER))
38 |
39 | scenarios[scenario]?.forEach { dependency ->
40 | if (dependency.startsWith(SCENARIO_DELIMITER)) {
41 | installScenario(scenarios, dependency)
42 | } else {
43 | implementation(dependency)
44 | }
45 | } ?: error("Cannot find the feature flag: '$scenario'")
46 | }
47 |
48 | fun Project.loadScenarioFromLocal(): String {
49 | val props = Properties()
50 | val inputStream = file("${rootDir}/local.properties").inputStream()
51 |
52 | inputStream.use { props.load(it) }
53 | return props["scenario"] as? String ?: DEFAULT_SCENARIO
54 | }
55 |
56 | /**
57 | * Returns the runtime Java class of this object.
58 | */
59 | @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST")
60 | inline val T.javaClass: Class
61 | @Suppress("UsePropertyAccessSyntax")
62 | get() = (this as java.lang.Object).getClass() as Class
63 |
64 | /**
65 | * Adds a dependency to the 'implementation' configuration.
66 | *
67 | * @param dependencyNotation notation for the dependency to be added.
68 | * @return The dependency.
69 | *
70 | * @see [DependencyHandler.add]
71 | */
72 | private fun DependencyHandler.implementation(path: Any): Dependency? =
73 | add("implementation", project(mapOf("path" to path)) as ProjectDependency)
74 |
75 | private const val SCENARIO_DELIMITER = ">"
76 | const val DEFAULT_SCENARIO = "${SCENARIO_DELIMITER}default"
77 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/featureflags/Scenario.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package featureflags
17 |
18 | val scenarios = mapOf(
19 | Scenario.default to listOf(Scenario.guidomiaWithDatabase),
20 | Scenario.root to listOf(),
21 | Scenario.guidomia to listOf(Modules.guidomiaImpl),
22 | Scenario.guidomiaWithDatabase to listOf(Modules.databaseImpl)
23 | )
24 |
25 | private object Scenario {
26 | const val default = DEFAULT_SCENARIO
27 | const val root = ">"
28 | const val guidomia = ">guidomia"
29 | const val guidomiaWithDatabase = ">guidomia>database"
30 | }
31 |
--------------------------------------------------------------------------------
/core/impl/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/impl/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | plugins {
17 | id("com.android.library")
18 | kotlin("android")
19 | kotlin("kapt")
20 | id("com.squareup.anvil") version Versions.anvil
21 | }
22 |
23 | configureAndroidLibrary()
24 |
25 | android {
26 | buildFeatures {
27 | viewBinding = true
28 | }
29 | }
30 |
31 | anvil {
32 | generateDaggerFactories = true
33 | }
34 |
35 | dependencies {
36 | implementation(project(Modules.corePublic))
37 | implementation(project(Modules.diPublic))
38 | implementation(project(Modules.guidomiaPublic))
39 |
40 | implementation(Depends.kotlinStdLib)
41 | implementation(Depends.coreKtx)
42 | implementation(Depends.appCompat)
43 | implementation(Depends.material)
44 | implementation(Depends.constraintLayout)
45 | implementation(Depends.dagger)
46 | implementation(Depends.coroutines)
47 |
48 | testImplementation(Depends.junit)
49 | androidTestImplementation(Depends.testExtJunit)
50 | androidTestImplementation(Depends.espressoCore)
51 | }
52 |
--------------------------------------------------------------------------------
/core/impl/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/impl/src/main/java/com/hadilq/guidomia/core/impl/DispatcherProviderImpl.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.core.impl
17 |
18 | import com.hadilq.guidomia.core.api.DispatcherProvider
19 | import com.hadilq.guidomia.di.api.AppScope
20 | import com.squareup.anvil.annotations.ContributesBinding
21 | import kotlinx.coroutines.CoroutineDispatcher
22 | import kotlinx.coroutines.Dispatchers
23 | import javax.inject.Inject
24 |
25 | @ContributesBinding(AppScope::class)
26 | class DispatcherProviderImpl @Inject constructor() : DispatcherProvider {
27 |
28 | override val Main: CoroutineDispatcher
29 | get() = Dispatchers.Main
30 |
31 | override val Default: CoroutineDispatcher
32 | get() = Dispatchers.Default
33 |
34 | override val IO: CoroutineDispatcher
35 | get() = Dispatchers.IO
36 | }
--------------------------------------------------------------------------------
/core/impl/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/public/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/public/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | plugins {
17 | id("com.android.library")
18 | kotlin("android")
19 | }
20 |
21 | configureAndroidLibrary()
22 |
23 | android {
24 | buildFeatures {
25 | viewBinding = true
26 | }
27 | }
28 |
29 | dependencies {
30 | implementation(Depends.kotlinStdLib)
31 | implementation(Depends.material)
32 | implementation(Depends.dagger)
33 | implementation(Depends.viewModel)
34 | }
35 |
--------------------------------------------------------------------------------
/core/public/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/public/src/main/java/com/hadilq/guidomia/core/api/DispatcherProvider.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.core.api
17 |
18 | import kotlinx.coroutines.CoroutineDispatcher
19 |
20 | interface DispatcherProvider {
21 | val Main: CoroutineDispatcher
22 | val Default: CoroutineDispatcher
23 | val IO: CoroutineDispatcher
24 | }
--------------------------------------------------------------------------------
/core/public/src/main/java/com/hadilq/guidomia/core/api/FragmentFactory.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.core.api
17 |
18 | import androidx.fragment.app.Fragment
19 | import kotlin.reflect.KClass
20 |
21 | interface FragmentFactory {
22 |
23 | fun instantiate(clazz: KClass): Fragment
24 | }
25 |
--------------------------------------------------------------------------------
/core/public/src/main/java/com/hadilq/guidomia/core/api/FragmentKey.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.core.api
17 |
18 | import androidx.fragment.app.Fragment
19 | import dagger.MapKey
20 | import kotlin.reflect.KClass
21 |
22 | @MustBeDocumented
23 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
24 | @Retention(AnnotationRetention.RUNTIME)
25 | @MapKey
26 | annotation class FragmentKey(val value: KClass)
27 |
--------------------------------------------------------------------------------
/core/public/src/main/java/com/hadilq/guidomia/core/api/SimpleFragmentFactory.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.core.api
17 |
18 | import androidx.fragment.app.Fragment
19 |
20 | interface SimpleFragmentFactory {
21 |
22 | fun instantiate(): Fragment
23 | }
24 |
--------------------------------------------------------------------------------
/core/public/src/main/java/com/hadilq/guidomia/core/api/SimpleViewModelFactory.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.core.api
17 |
18 | import androidx.lifecycle.ViewModel
19 |
20 | interface SimpleViewModelFactory {
21 |
22 | fun create(): ViewModel
23 | }
24 |
--------------------------------------------------------------------------------
/core/public/src/main/java/com/hadilq/guidomia/core/api/ViewBinding.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.core.api
17 |
18 | import android.os.Looper
19 | import androidx.lifecycle.Lifecycle
20 | import androidx.lifecycle.LifecycleObserver
21 | import androidx.lifecycle.LifecycleOwner
22 | import androidx.lifecycle.OnLifecycleEvent
23 | import androidx.viewbinding.ViewBinding
24 | import kotlin.properties.ReadOnlyProperty
25 | import kotlin.reflect.KProperty
26 |
27 | fun LifecycleOwner.viewBinding(
28 | initializer: () -> T
29 | ): ReadOnlyProperty = ViewBindingPropertyDelegate(this, initializer)
30 |
31 | class ViewBindingPropertyDelegate(
32 | owner: LifecycleOwner,
33 | private val initializer: () -> T
34 | ) : ReadOnlyProperty, LifecycleObserver {
35 |
36 | private var _value: T? = null
37 |
38 | init {
39 | owner.lifecycle.addObserver(this)
40 | }
41 |
42 | @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
43 | fun onDestroy() {
44 | _value = null
45 | }
46 |
47 | override fun getValue(thisRef: LifecycleOwner, property: KProperty<*>): T {
48 | if (_value == null) {
49 |
50 | // This must be on the main thread only
51 | if (Looper.myLooper() != Looper.getMainLooper()) {
52 | throw IllegalThreadStateException(
53 | "This cannot be called from other threads. " +
54 | "It should be on the main thread only."
55 | )
56 | }
57 |
58 | _value = initializer()
59 | }
60 | return _value!!
61 | }
62 | }
--------------------------------------------------------------------------------
/core/public/src/main/java/com/hadilq/guidomia/core/api/ViewModelFactory.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.core.api
17 |
18 | import androidx.lifecycle.ViewModel
19 | import androidx.lifecycle.ViewModelProvider
20 |
21 | interface ViewModelFactory : ViewModelProvider.Factory {
22 |
23 | override fun create(modelClass: Class): T
24 | }
25 |
--------------------------------------------------------------------------------
/core/public/src/main/java/com/hadilq/guidomia/core/api/ViewModelKey.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.core.api
17 |
18 | import androidx.lifecycle.ViewModel
19 | import dagger.MapKey
20 | import kotlin.reflect.KClass
21 |
22 | @MustBeDocumented
23 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
24 | @Retention(AnnotationRetention.RUNTIME)
25 | @MapKey
26 | annotation class ViewModelKey(val value: KClass)
27 |
--------------------------------------------------------------------------------
/core/public/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/core/public/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FC6016
4 | #858585
5 | #D5D5D5
6 | #73000000
7 | #FF000000
8 | #FFFFFFFF
9 |
10 |
--------------------------------------------------------------------------------
/core/public/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2dp
4 |
5 |
--------------------------------------------------------------------------------
/core/public/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Guidomia
3 |
--------------------------------------------------------------------------------
/core/public/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/database/impl/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /schemas
3 |
--------------------------------------------------------------------------------
/database/impl/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | plugins {
17 | id("com.android.library")
18 | kotlin("android")
19 | kotlin("kapt")
20 | id("com.squareup.anvil") version Versions.anvil
21 | }
22 |
23 | configureAndroidLibrary()
24 |
25 | android {
26 | defaultConfig {
27 | javaCompileOptions {
28 | annotationProcessorOptions {
29 | arguments += mapOf(
30 | "room.schemaLocation" to "$projectDir/schemas",
31 | "room.incremental" to "true",
32 | "room.expandProjection" to "true"
33 | )
34 | }
35 | }
36 | }
37 | }
38 |
39 | anvil {
40 | generateDaggerFactories = true
41 | }
42 |
43 | dependencies {
44 | implementation(project(Modules.corePublic))
45 | implementation(project(Modules.diPublic))
46 | implementation(project(Modules.databasePublic))
47 | implementation(project(Modules.featureFlagsPublic))
48 |
49 | kapt(Depends.roomCompiler)
50 |
51 | implementation(Depends.kotlinStdLib)
52 | implementation(Depends.dagger)
53 | implementation(Depends.coroutines)
54 | implementation(Depends.roomRuntime)
55 | implementation(Depends.roomKtx)
56 |
57 | testImplementation(Depends.junit)
58 | androidTestImplementation(Depends.testExtJunit)
59 | androidTestImplementation(Depends.espressoCore)
60 | }
61 |
--------------------------------------------------------------------------------
/database/impl/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/database/impl/src/main/java/com/hadilq/guidomia/database/impl/AppDatabase.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.database.impl
17 |
18 | import androidx.room.Database
19 | import androidx.room.RoomDatabase
20 | import com.hadilq.guidomia.database.impl.entiry.CarEntity
21 | import com.hadilq.guidomia.database.impl.entiry.ProConEntity
22 |
23 | @Database(
24 | entities = [
25 | CarEntity::class,
26 | ProConEntity::class
27 | ],
28 | version = 1
29 | )
30 | abstract class AppDatabase : RoomDatabase() {
31 |
32 | abstract fun carDao(): CarDao
33 | }
34 |
--------------------------------------------------------------------------------
/database/impl/src/main/java/com/hadilq/guidomia/database/impl/CarDao.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.database.impl
17 |
18 | import androidx.room.*
19 | import com.hadilq.guidomia.database.impl.entiry.CarEntity
20 | import com.hadilq.guidomia.database.impl.entiry.CarWithProsCons
21 | import com.hadilq.guidomia.database.impl.entiry.ProConEntity
22 |
23 | @Dao
24 | interface CarDao {
25 |
26 | @Transaction
27 | @Query("SELECT * FROM `car-entity`")
28 | suspend fun getAll(): List
29 |
30 | @Insert(onConflict = OnConflictStrategy.REPLACE)
31 | suspend fun insertCarEntity(vararg car: CarEntity): Array
32 |
33 | @Insert(onConflict = OnConflictStrategy.REPLACE)
34 | suspend fun insertProConEntity(vararg proCon: ProConEntity): Array
35 |
36 | @Delete
37 | suspend fun delete(car: CarEntity)
38 | }
39 |
--------------------------------------------------------------------------------
/database/impl/src/main/java/com/hadilq/guidomia/database/impl/CarDataEntityCommandHook.kt:
--------------------------------------------------------------------------------
1 | package com.hadilq.guidomia.database.impl
2 |
3 | import com.github.hadilq.commandku.api.*
4 | import com.hadilq.guidomia.database.api.GetCarEntityCommand
5 | import com.hadilq.guidomia.database.api.GetCarEntityCommandResult
6 | import com.hadilq.guidomia.di.api.AppScope
7 | import com.squareup.anvil.annotations.ContributesMultibinding
8 | import javax.inject.Inject
9 |
10 | @ContributesMultibinding(AppScope::class)
11 | class CarDataEntityCommandHook @Inject constructor(
12 | private val carDataEntityCommand: CarDataEntityCommandImpl,
13 | private val commandShooter: CommandResultShooter,
14 | ) : CommandHook {
15 |
16 | override fun hookUp(commandRegister: CommandRegister) {
17 | commandRegister.register(GetCarEntityCommand::class,
18 | CommandCallbackImpl(commandShooter) {
19 | GetCarEntityCommandResult(carDataEntityCommand)
20 | })
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/database/impl/src/main/java/com/hadilq/guidomia/database/impl/CarDataEntityCommandImpl.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.database.impl
17 |
18 | import com.hadilq.guidomia.database.api.CarDataEntityCommand
19 | import com.hadilq.guidomia.database.api.CarDatabaseEntity
20 | import com.hadilq.guidomia.di.api.AppScope
21 | import com.hadilq.guidomia.di.api.SingleIn
22 | import javax.inject.Inject
23 |
24 | @SingleIn(AppScope::class)
25 | class CarDataEntityCommandImpl @Inject constructor(
26 | private val carDao: CarDao,
27 | private val carEntityMapper: CarEntityMapper,
28 | ) : CarDataEntityCommand {
29 |
30 | override suspend fun getAll(): List =
31 | carDao.getAll().map { carEntityMapper.map(it) }
32 |
33 |
34 | override suspend fun insertAll(cars: List) {
35 | val carEntities = carDao.insertCarEntity(
36 | *cars.map { carEntityMapper.mapToCarEntity(it) }.toTypedArray()
37 | ).mapIndexed { index, id ->
38 | id to cars[index]
39 | }.toMap()
40 |
41 | carDao.insertProConEntity(*carEntities.keys.flatMap { id ->
42 | carEntities[id]!!.prosList.map { pro ->
43 | carEntityMapper.mapToProConEntity(id, true, pro)
44 | } + carEntities[id]!!.consList.map { con ->
45 | carEntityMapper.mapToProConEntity(id, false, con)
46 | }
47 | }.toTypedArray())
48 | }
49 |
50 | override suspend fun isEmpty(): Boolean = carDao.getAll().isEmpty()
51 | }
52 |
--------------------------------------------------------------------------------
/database/impl/src/main/java/com/hadilq/guidomia/database/impl/CarEntityMapper.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.database.impl
17 |
18 | import com.hadilq.guidomia.database.api.CarDatabaseEntity
19 | import com.hadilq.guidomia.database.impl.entiry.CarEntity
20 | import com.hadilq.guidomia.database.impl.entiry.CarWithProsCons
21 | import com.hadilq.guidomia.database.impl.entiry.ProConEntity
22 | import javax.inject.Inject
23 |
24 | class CarEntityMapper @Inject constructor() {
25 |
26 | fun map(car: CarWithProsCons) = car.run {
27 | CarDatabaseEntity(
28 | make = this.car.make,
29 | model = this.car.model,
30 | image = this.car.image,
31 | price = this.car.price,
32 | rating = this.car.rating,
33 | prosList = proConList.filter { it.pro }.map { it.value },
34 | consList = proConList.filter { !it.pro }.map { it.value },
35 | )
36 | }
37 |
38 | fun mapToCarEntity(car: CarDatabaseEntity) = car.run {
39 | CarEntity(
40 | make = make,
41 | model = model,
42 | image = image,
43 | price = price,
44 | rating = rating,
45 | )
46 | }
47 |
48 | fun mapToProConEntity(
49 | carId: Long,
50 | pro: Boolean,
51 | value: String,
52 | ) = ProConEntity(
53 | carId = carId,
54 | pro = pro,
55 | value = value,
56 | )
57 | }
58 |
--------------------------------------------------------------------------------
/database/impl/src/main/java/com/hadilq/guidomia/database/impl/di/DatabaseModule.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.database.impl.di
17 |
18 | import android.content.Context
19 | import androidx.room.Room
20 | import com.hadilq.guidomia.database.impl.AppDatabase
21 | import com.hadilq.guidomia.database.impl.CarDao
22 | import com.hadilq.guidomia.di.api.AppScope
23 | import com.hadilq.guidomia.di.api.SingleIn
24 | import com.squareup.anvil.annotations.ContributesTo
25 | import dagger.Module
26 | import dagger.Provides
27 |
28 | @[Module ContributesTo(AppScope::class)]
29 | object DatabaseModule {
30 |
31 | @[Provides SingleIn(AppScope::class)]
32 | fun provideDatabase(context: Context): AppDatabase = Room.databaseBuilder(
33 | context.applicationContext,
34 | AppDatabase::class.java, "car-database"
35 | ).build()
36 |
37 | @[Provides SingleIn(AppScope::class)]
38 | fun provideCarDao(database: AppDatabase): CarDao = database.carDao()
39 | }
40 |
--------------------------------------------------------------------------------
/database/impl/src/main/java/com/hadilq/guidomia/database/impl/entiry/CarEntity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.database.impl.entiry
17 |
18 | import androidx.room.Entity
19 | import androidx.room.PrimaryKey
20 |
21 | @Entity(tableName = "car-entity")
22 | data class CarEntity(
23 | @PrimaryKey(autoGenerate = true) val id: Long = 0L,
24 | val make: String,
25 | val model: String,
26 | val image: Int,
27 | val price: Float,
28 | val rating: Int,
29 | )
30 |
--------------------------------------------------------------------------------
/database/impl/src/main/java/com/hadilq/guidomia/database/impl/entiry/CarWithProsCons.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.database.impl.entiry
17 |
18 | import androidx.room.Embedded
19 | import androidx.room.Relation
20 |
21 | data class CarWithProsCons(
22 | @Embedded
23 | val car: CarEntity,
24 |
25 | @Relation(
26 | parentColumn = "id",
27 | entityColumn = "carId"
28 | )
29 | val proConList: List
30 | )
31 |
--------------------------------------------------------------------------------
/database/impl/src/main/java/com/hadilq/guidomia/database/impl/entiry/ProConEntity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.database.impl.entiry
17 |
18 | import androidx.room.Entity
19 | import androidx.room.ForeignKey
20 | import androidx.room.ForeignKey.CASCADE
21 | import androidx.room.PrimaryKey
22 |
23 | @Entity(
24 | tableName = "pro-con-entity",
25 | foreignKeys = [ForeignKey(
26 | entity = CarEntity::class,
27 | parentColumns = arrayOf("id"),
28 | childColumns = arrayOf("carId"),
29 | onDelete = CASCADE,
30 | onUpdate = CASCADE,
31 | )]
32 | )
33 | data class ProConEntity(
34 | @PrimaryKey(autoGenerate = true) val id: Long = 0L,
35 | val carId: Long,
36 | val pro: Boolean,
37 | val value: String,
38 | )
39 |
--------------------------------------------------------------------------------
/database/public/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/database/public/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | plugins {
17 | kotlin("jvm")
18 | }
19 |
20 | dependencies {
21 | implementation(Depends.kotlinStdLib)
22 | implementation(project(Modules.featureFlagsPublic))
23 | }
24 |
--------------------------------------------------------------------------------
/database/public/src/main/java/com/hadilq/guidomia/database/api/CarDataEntity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.database.api
17 |
18 | data class CarDatabaseEntity(
19 | val make: String,
20 | val model: String,
21 | val image: Int,
22 | val price: Float,
23 | val rating: Int,
24 | val prosList: List,
25 | val consList: List,
26 | )
27 |
--------------------------------------------------------------------------------
/database/public/src/main/java/com/hadilq/guidomia/database/api/CarDataEntityCommand.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.database.api
17 |
18 | interface CarDataEntityCommand {
19 |
20 | suspend fun getAll(): List
21 |
22 | suspend fun insertAll(cars: List)
23 |
24 | suspend fun isEmpty(): Boolean
25 | }
26 |
--------------------------------------------------------------------------------
/database/public/src/main/java/com/hadilq/guidomia/database/api/GetCarEntityCommand.kt:
--------------------------------------------------------------------------------
1 | package com.hadilq.guidomia.database.api
2 |
3 | import com.github.hadilq.commandku.api.Command
4 |
5 | class GetCarEntityCommand : Command
6 |
7 | class GetCarEntityCommandResult(
8 | val result: CarDataEntityCommand
9 | ) : Command
10 |
--------------------------------------------------------------------------------
/di/public/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/di/public/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | plugins {
17 | kotlin("jvm")
18 | }
19 |
20 | dependencies {
21 | implementation(Depends.kotlinStdLib)
22 | implementation(Depends.dagger)
23 | }
24 |
--------------------------------------------------------------------------------
/di/public/src/main/java/com/hadilq/guidomia/di/api/AppScope.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.di.api
17 |
18 | abstract class AppScope private constructor()
19 |
--------------------------------------------------------------------------------
/di/public/src/main/java/com/hadilq/guidomia/di/api/FragmentScope.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.di.api
17 |
18 | abstract class FragmentScope private constructor()
19 |
--------------------------------------------------------------------------------
/di/public/src/main/java/com/hadilq/guidomia/di/api/RetainScope.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.di.api
17 |
18 | abstract class RetainScope private constructor()
19 |
--------------------------------------------------------------------------------
/di/public/src/main/java/com/hadilq/guidomia/di/api/SingleActivityScope.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.di.api
17 |
18 | abstract class SingleActivityScope private constructor()
19 |
--------------------------------------------------------------------------------
/di/public/src/main/java/com/hadilq/guidomia/di/api/SingleIn.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.di.api
17 |
18 | import javax.inject.Scope
19 | import kotlin.reflect.KClass
20 |
21 | @Scope
22 | @Retention(AnnotationRetention.RUNTIME)
23 | annotation class SingleIn(val clazz: KClass<*>)
24 |
--------------------------------------------------------------------------------
/doc-image/dip-module-dependencies-switch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/doc-image/dip-module-dependencies-switch.png
--------------------------------------------------------------------------------
/feature-flags/impl/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/feature-flags/impl/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | plugins {
17 | kotlin("jvm")
18 | kotlin("kapt")
19 | id("com.squareup.anvil") version Versions.anvil
20 | }
21 |
22 | anvil {
23 | generateDaggerFactories = true
24 | }
25 |
26 | dependencies {
27 | implementation(project(Modules.featureFlagsPublic))
28 | implementation(project(Modules.diPublic))
29 |
30 | api(Depends.commandKuImpl)
31 | implementation(Depends.dagger)
32 | implementation(Depends.kotlinStdLib)
33 | implementation(Depends.coroutines)
34 | }
35 |
--------------------------------------------------------------------------------
/feature-flags/impl/src/main/java/com/hadilq/guidomia/featureflags/impl/CommandKuModule.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.featureflags.impl
17 |
18 | import com.github.hadilq.commandku.api.*
19 | import com.github.hadilq.commandku.impl.CommandExecutorImpl
20 | import com.github.hadilq.commandku.impl.CommandKU
21 | import com.hadilq.guidomia.di.api.AppScope
22 | import com.hadilq.guidomia.di.api.SingleIn
23 | import com.squareup.anvil.annotations.ContributesTo
24 | import dagger.Binds
25 | import dagger.Module
26 | import dagger.Provides
27 |
28 |
29 | @ContributesTo(AppScope::class)
30 | @Module
31 | abstract class CommandKuModule {
32 |
33 | @Binds
34 | abstract fun commandRegister(commandKU: CommandKU): CommandRegister
35 |
36 | @Binds
37 | abstract fun commandResultRegister(commandKU: CommandKU): CommandResultRegister
38 |
39 | @Binds
40 | abstract fun commandShooter(commandKU: CommandKU): CommandShooter
41 |
42 | @Binds
43 | abstract fun commandResultShooter(commandKU: CommandKU): CommandResultShooter
44 |
45 | @Binds
46 | abstract fun commandExecutor(commandExecutorImpl: CommandExecutorImpl): CommandExecutor
47 |
48 | companion object {
49 | @Provides
50 | @SingleIn(AppScope::class)
51 | fun commandKU(): CommandKU = CommandKU()
52 |
53 | @Provides
54 | @SingleIn(AppScope::class)
55 | fun commandExecutorImpl(
56 | commandShooter: CommandShooter,
57 | commandResultRegister: CommandResultRegister,
58 | ): CommandExecutorImpl = CommandExecutorImpl(commandShooter, commandResultRegister)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/feature-flags/public/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/feature-flags/public/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | plugins {
17 | kotlin("jvm")
18 | }
19 |
20 | tasks.withType().configureEach {
21 | kotlinOptions {
22 | freeCompilerArgs = listOf("-Xinline-classes")
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation(Depends.kotlinStdLib)
28 | api(Depends.commandKuApi)
29 | }
30 |
--------------------------------------------------------------------------------
/feature-flags/public/src/main/java/com/hadilq/guidomia/featureflags/api/CommandExecutor.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.featureflags.api
17 |
18 | import com.github.hadilq.commandku.api.*
19 | import kotlin.reflect.KClass
20 |
21 | suspend inline
22 | fun CommandExecutor.featureFlag(
23 | input: IN,
24 | @Suppress("UNUSED_PARAMETER") output: KClass,
25 | ): FeatureFlag =
26 | when (val commandResult: CommandResponse = exe(input)) {
27 | is Available<*> -> FeatureFlag.On(commandResult.command as OUT)
28 | else -> FeatureFlag.Off()
29 | }
30 |
31 | sealed class FeatureFlag {
32 | abstract fun to(map: T.() -> O): FeatureFlag
33 |
34 | class On(val value: T) : FeatureFlag() {
35 | override fun to(map: T.() -> O): FeatureFlag = On(value.map())
36 | }
37 |
38 | class Off : FeatureFlag() {
39 | override fun to(map: T.() -> O): FeatureFlag = Off()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | org.gradle.parallel=true
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Apr 15 16:25:31 EDT 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/guidomia/impl/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/guidomia/impl/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | id("com.android.library")
19 | kotlin("android")
20 | kotlin("kapt")
21 | id("com.squareup.anvil") version Versions.anvil
22 | kotlin("plugin.serialization") version Versions.serialization
23 | id("de.mannodermaus.android-junit5")
24 | }
25 |
26 | configureAndroidLibrary()
27 |
28 | android {
29 | buildFeatures {
30 | viewBinding = true
31 | }
32 | }
33 |
34 | anvil {
35 | generateDaggerFactories = true
36 | }
37 |
38 | dependencies {
39 | implementation(project(Modules.corePublic))
40 | implementation(project(Modules.diPublic))
41 | implementation(project(Modules.singleActivityPublic))
42 | implementation(project(Modules.guidomiaPublic))
43 | implementation(project(Modules.databasePublic))
44 | implementation(project(Modules.featureFlagsPublic))
45 |
46 | implementation(Depends.kotlinStdLib)
47 | implementation(Depends.coreKtx)
48 | implementation(Depends.appCompat)
49 | implementation(Depends.material)
50 | implementation(Depends.constraintLayout)
51 | implementation(Depends.fragment)
52 | implementation(Depends.dagger)
53 | implementation(Depends.viewModel)
54 | implementation(Depends.recyclerView)
55 | implementation(Depends.coroutines)
56 | implementation(Depends.jsonSerialization)
57 |
58 | testImplementation(Depends.junitJupiterApi)
59 | testRuntimeOnly(Depends.junitJupiterEngine)
60 | testImplementation(Depends.mockk)
61 | androidTestImplementation(Depends.testExtJunit)
62 | androidTestImplementation(Depends.espressoCore)
63 | }
64 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/assets/car_list.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "consList": [
4 | "Bad direction"
5 | ],
6 | "customerPrice": 120000.0,
7 | "make": "Land Rover",
8 | "marketPrice": 125000.0,
9 | "model": "Range Rover",
10 | "prosList": [
11 | "You can go everywhere",
12 | "Good sound system"
13 | ],
14 | "rating": 3
15 | },
16 | {
17 | "consList": [
18 | "Sometime explode"
19 | ],
20 | "customerPrice": 220000.0,
21 | "make": "Alpine",
22 | "marketPrice": 225000.0,
23 | "model": "Roadster",
24 | "prosList": [
25 | "This car is so fast",
26 | "Jame Bond would love to steal that car",
27 | "",
28 | ""
29 | ],
30 | "rating": 4
31 | },
32 | {
33 | "consList": [
34 | "You can heard the engine over children cry at the back",
35 | "",
36 | "You may lose this one if you divorce"
37 | ],
38 | "customerPrice": 65000.0,
39 | "make": "BMW",
40 | "marketPrice": 55900.0,
41 | "model": "3300i",
42 | "prosList": [
43 | "Your average business man car",
44 | "Can bring the family home safely",
45 | "The city must have"
46 | ],
47 | "rating": 5
48 | },
49 | {
50 | "consList": [
51 | "You may lose a wheel",
52 | "Expensive maintenance"
53 | ],
54 | "customerPrice": 95000.0,
55 | "make": "Mercedes Benz",
56 | "marketPrice": 85900.0,
57 | "model": "GLE coupe",
58 | "prosList": [],
59 | "rating": 2
60 | }
61 | ]
62 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/GuidomiaCommandHook.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl
17 |
18 | import com.github.hadilq.commandku.api.CommandCallbackImpl
19 | import com.github.hadilq.commandku.api.CommandHook
20 | import com.github.hadilq.commandku.api.CommandRegister
21 | import com.github.hadilq.commandku.api.CommandResultShooter
22 | import com.hadilq.guidomia.di.api.AppScope
23 | import com.hadilq.guidomia.guidomia.api.GetGuidomiaNavigatorFactoryCommand
24 | import com.hadilq.guidomia.guidomia.api.GetGuidomiaNavigatorFactoryCommandResult
25 | import com.hadilq.guidomia.guidomia.api.GuidomiaNavigatorFactory
26 | import com.squareup.anvil.annotations.ContributesMultibinding
27 | import javax.inject.Inject
28 |
29 | @ContributesMultibinding(AppScope::class)
30 | class GuidomiaCommandHook @Inject constructor(
31 | private val factory: GuidomiaNavigatorFactory,
32 | private val commandShooter: CommandResultShooter,
33 | ) : CommandHook {
34 |
35 | override fun hookUp(commandRegister: CommandRegister) {
36 | commandRegister.register(GetGuidomiaNavigatorFactoryCommand::class,
37 | CommandCallbackImpl(commandShooter) {
38 | GetGuidomiaNavigatorFactoryCommandResult(factory)
39 | })
40 | }
41 | }
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/data/datasource/CarDatabaseDataSource.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.data.datasource
17 |
18 | import com.github.hadilq.commandku.api.CommandExecutor
19 | import com.hadilq.guidomia.database.api.CarDataEntityCommand
20 | import com.hadilq.guidomia.database.api.GetCarEntityCommand
21 | import com.hadilq.guidomia.database.api.GetCarEntityCommandResult
22 | import com.hadilq.guidomia.featureflags.api.FeatureFlag
23 | import com.hadilq.guidomia.featureflags.api.featureFlag
24 | import com.hadilq.guidomia.guidomia.impl.data.mapper.CarDatabaseMapper
25 | import com.hadilq.guidomia.guidomia.impl.domain.entity.CarEntity
26 | import javax.inject.Inject
27 |
28 | class CarDatabaseDataSource @Inject constructor(
29 | private val executor: CommandExecutor,
30 | private val mapper: CarDatabaseMapper,
31 | ) {
32 |
33 | private var command: FeatureFlag? = null
34 |
35 | suspend fun featureFlag(): Boolean = when (fetchFlag()) {
36 | is FeatureFlag.On -> true
37 | is FeatureFlag.Off -> false
38 | }
39 |
40 | suspend fun isEmpty(): Boolean = when (val flag = fetchFlag()) {
41 | is FeatureFlag.On -> flag.value.isEmpty()
42 | is FeatureFlag.Off -> true
43 | }
44 |
45 | suspend fun fetchCars(): List = when (val flag = fetchFlag()) {
46 | is FeatureFlag.On -> flag.value.getAll().map { mapper.map(it) }
47 | is FeatureFlag.Off -> emptyList()
48 | }
49 |
50 | suspend fun save(cars: List) {
51 | when (val flag = fetchFlag()) {
52 | is FeatureFlag.On -> flag.value.insertAll(cars.map { mapper.map(it) })
53 | }
54 | }
55 |
56 | private suspend fun fetchFlag(): FeatureFlag = command ?: run {
57 | executor.featureFlag(GetCarEntityCommand(), GetCarEntityCommandResult::class)
58 | .to { result }
59 | .also { command = it }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/data/datasource/CarsCacheDataSource.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.data.datasource
17 |
18 | import com.hadilq.guidomia.guidomia.impl.domain.entity.CarEntity
19 | import javax.inject.Inject
20 |
21 | class CarsCacheDataSource @Inject constructor() {
22 |
23 | var caching: List = listOf()
24 | }
25 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/data/datasource/CarsDataSource.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.data.datasource
17 |
18 | import android.content.Context
19 | import com.hadilq.guidomia.core.api.DispatcherProvider
20 | import com.hadilq.guidomia.guidomia.impl.data.entity.CarDataEntity
21 | import com.hadilq.guidomia.guidomia.impl.data.mapper.CarDataMapper
22 | import com.hadilq.guidomia.guidomia.impl.domain.entity.CarEntity
23 | import kotlinx.coroutines.flow.Flow
24 | import kotlinx.coroutines.flow.flow
25 | import kotlinx.coroutines.withContext
26 | import kotlinx.serialization.decodeFromString
27 | import kotlinx.serialization.json.Json
28 | import javax.inject.Inject
29 |
30 | class CarsDataSource @Inject constructor(
31 | private val context: Context,
32 | private val carDataMapper: CarDataMapper,
33 | private val dispatcherProvider: DispatcherProvider,
34 | ) {
35 |
36 | fun fetchCars(): Flow> = flow {
37 | @Suppress("BlockingMethodInNonBlockingContext")
38 | val json = withContext(dispatcherProvider.IO) {
39 | context.assets.open(JSON_FILE_NAME).bufferedReader().use { it.readText() }
40 | }
41 |
42 | emit(Json.decodeFromString>(json).map { carDataMapper.map(it) })
43 | }
44 | }
45 |
46 | private const val JSON_FILE_NAME = "car_list.json"
47 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/data/entity/CarDataEntity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.data.entity
17 |
18 | import kotlinx.serialization.Serializable
19 |
20 | @Serializable
21 | data class CarDataEntity(
22 | val make: String,
23 | val model: String,
24 | val marketPrice: Float,
25 | val customerPrice: Float,
26 | val rating: Int,
27 | val prosList: List,
28 | val consList: List,
29 | )
30 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/data/mapper/CarDataMapper.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.data.mapper
17 |
18 | import com.hadilq.guidomia.guidomia.impl.R
19 | import com.hadilq.guidomia.guidomia.impl.data.entity.CarDataEntity
20 | import com.hadilq.guidomia.guidomia.impl.domain.entity.*
21 | import javax.inject.Inject
22 |
23 | class CarDataMapper @Inject constructor() {
24 |
25 | fun map(data: CarDataEntity) = data.run {
26 | CarEntity(
27 | model = ModelEntity(model),
28 | make = MakeEntity(make),
29 | price = PriceEntity(marketPrice),
30 | rate = Rate.values()[rating - 1],
31 | pros = prosList.filter { it.isNotBlank() },
32 | cons = consList.filter { it.isNotBlank() },
33 | image = ImageEntity(
34 | when (model) {
35 | "Range Rover" -> R.drawable.range_rover
36 | "Roadster" -> R.drawable.alpine_roadster
37 | "3300i" -> R.drawable.bmw_330i
38 | "GLE coupe" -> R.drawable.mercedez_benz_glc
39 | else -> 0
40 | }
41 | )
42 | )
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/data/mapper/CarDatabaseMapper.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.data.mapper
17 |
18 | import com.hadilq.guidomia.database.api.CarDatabaseEntity
19 | import com.hadilq.guidomia.guidomia.impl.domain.entity.*
20 | import javax.inject.Inject
21 |
22 | class CarDatabaseMapper @Inject constructor() {
23 |
24 | fun map(car: CarDatabaseEntity) = with(car) {
25 | CarEntity(
26 | make = MakeEntity(make),
27 | model = ModelEntity(model),
28 | image = ImageEntity(image),
29 | price = PriceEntity(price),
30 | rate = Rate.values()[rating - 1],
31 | pros = prosList,
32 | cons = consList,
33 | )
34 | }
35 |
36 | fun map(car: CarEntity) = with(car) {
37 | CarDatabaseEntity(
38 | make = make.value,
39 | model = model.value,
40 | image = image.value,
41 | price = price.value,
42 | rating = rate.value,
43 | prosList = pros,
44 | consList = cons,
45 | )
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/data/repository/CarsRepository.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.data.repository
17 |
18 | import com.hadilq.guidomia.guidomia.impl.data.datasource.CarDatabaseDataSource
19 | import com.hadilq.guidomia.guidomia.impl.data.datasource.CarsCacheDataSource
20 | import com.hadilq.guidomia.guidomia.impl.data.datasource.CarsDataSource
21 | import com.hadilq.guidomia.guidomia.impl.domain.entity.CarEntity
22 | import kotlinx.coroutines.flow.*
23 | import javax.inject.Inject
24 |
25 | class CarsRepository @Inject constructor(
26 | private val carsDataSource: CarsDataSource,
27 | private val cacheDataSource: CarsCacheDataSource,
28 | private val carDatabaseDataSource: CarDatabaseDataSource
29 | ) {
30 |
31 | fun getCars(): Flow> =
32 | if (cacheDataSource.caching.isEmpty()) {
33 | flow {
34 | if (!carDatabaseDataSource.featureFlag() || carDatabaseDataSource.isEmpty()) {
35 | emitAll(carsDataSource.fetchCars()
36 | .onEach { carDatabaseDataSource.save(it) }
37 | .onEach { cacheDataSource.caching = it }
38 | )
39 | } else {
40 | emit(carDatabaseDataSource.fetchCars())
41 | }
42 | }
43 | } else {
44 | flowOf(cacheDataSource.caching)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/domain/entity/CarEntity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.entity
17 |
18 | data class CarEntity(
19 | val model: ModelEntity,
20 | val make: MakeEntity,
21 | val image: ImageEntity,
22 | val price: PriceEntity,
23 | val rate: Rate,
24 | val pros: List,
25 | val cons: List,
26 | )
27 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/domain/entity/FilterEntity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.entity
17 |
18 | data class FilterEntity(
19 | val make: MakeEntity,
20 | val model: ModelEntity,
21 | )
22 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/domain/entity/ImageEntity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.entity
17 |
18 | import androidx.annotation.DrawableRes
19 |
20 | inline class ImageEntity(@DrawableRes val value: Int)
21 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/domain/entity/MakeEntity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.entity
17 |
18 | inline class MakeEntity(val value: String)
19 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/domain/entity/ModelEntity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.entity
17 |
18 | inline class ModelEntity(val value: String)
19 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/domain/entity/PriceEntity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.entity
17 |
18 | inline class PriceEntity(val value: Float)
19 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/domain/entity/RateEntity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.entity
17 |
18 | enum class Rate(val value: Int) {
19 | ONE(1), TWO(2), THREE(3), FOUR(4), FIVE(5)
20 | }
21 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/domain/usecase/GetCars.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.usecase
17 |
18 | import com.hadilq.guidomia.guidomia.impl.data.repository.CarsRepository
19 | import com.hadilq.guidomia.guidomia.impl.domain.entity.CarEntity
20 | import kotlinx.coroutines.flow.Flow
21 | import javax.inject.Inject
22 |
23 | class GetCars @Inject constructor(
24 | private val carsRepository: CarsRepository,
25 | ) {
26 |
27 | operator fun invoke(): Flow> = carsRepository.getCars()
28 | }
29 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/domain/usecase/GetFilteredCars.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.usecase
17 |
18 | import com.hadilq.guidomia.guidomia.impl.data.repository.CarsRepository
19 | import com.hadilq.guidomia.guidomia.impl.domain.entity.CarEntity
20 | import com.hadilq.guidomia.guidomia.impl.domain.entity.FilterEntity
21 | import kotlinx.coroutines.flow.Flow
22 | import kotlinx.coroutines.flow.map
23 | import javax.inject.Inject
24 |
25 | class GetFilteredCars @Inject constructor(
26 | private val carsRepository: CarsRepository,
27 | ) {
28 |
29 | operator fun invoke(filterEntity: FilterEntity): Flow> =
30 | carsRepository.getCars().map { list ->
31 | val make = filterEntity.make.value.toLowerCase()
32 | val model = filterEntity.model.value.toLowerCase()
33 | list.filter { car ->
34 | car.make.value.toLowerCase().contains(make) &&
35 | car.model.value.toLowerCase().contains(model)
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/presentation/CarItemFilter.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.presentation
17 |
18 | interface CarItemFilter {
19 |
20 | fun onNewFilter(filterModel: FilterModel)
21 | }
22 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/presentation/CarItemOnClick.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.presentation
17 |
18 | interface CarItemOnClick {
19 |
20 | fun onClickCar(position: Int)
21 | }
22 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/presentation/CarModel.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.presentation
17 |
18 | import com.hadilq.guidomia.guidomia.impl.domain.entity.*
19 |
20 | enum class CarListType(val index: Int) {
21 | LINE(0), CAR(1), FILTER(2)
22 | }
23 |
24 | sealed class CarListModel(
25 | val type: CarListType
26 | )
27 |
28 | object LineModel : CarListModel(CarListType.LINE)
29 |
30 | data class CarModel(
31 | val model: ModelEntity,
32 | val make: MakeEntity,
33 | val image: ImageEntity,
34 | val price: PriceEntity,
35 | val rate: Rate,
36 | val pros: List,
37 | val cons: List,
38 | val collapsed: Boolean = true,
39 | ) : CarListModel(CarListType.CAR)
40 |
41 | data class FilterModel(
42 | val make: MakeEntity = MakeEntity(""),
43 | val model: ModelEntity = ModelEntity(""),
44 | val makeUpdated: Boolean = false,
45 | val modelUpdated: Boolean = false,
46 | val cursorPosition: Int = 0,
47 | ) : CarListModel(CarListType.FILTER)
48 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/presentation/CarModelMapper.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.presentation
17 |
18 | import com.hadilq.guidomia.guidomia.impl.domain.entity.CarEntity
19 | import com.hadilq.guidomia.guidomia.impl.domain.entity.FilterEntity
20 | import javax.inject.Inject
21 |
22 | class CarModelMapper @Inject constructor() {
23 |
24 | fun map(carEntity: CarEntity, collapsed: Boolean = true) = carEntity.run {
25 | CarModel(
26 | model = model,
27 | make = make,
28 | image = image,
29 | price = price,
30 | rate = rate,
31 | pros = pros,
32 | cons = cons,
33 | collapsed = collapsed
34 | )
35 | }
36 |
37 | fun map(filterModel: FilterModel) = filterModel.run {
38 | FilterEntity(
39 | make = make,
40 | model = model,
41 | )
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/presentation/GuidomiaFragment.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.presentation
17 |
18 | import android.os.Bundle
19 | import android.view.LayoutInflater
20 | import android.view.View
21 | import android.view.ViewGroup
22 | import androidx.appcompat.app.AppCompatActivity
23 | import androidx.core.content.ContextCompat
24 | import androidx.fragment.app.Fragment
25 | import androidx.lifecycle.ViewModelProvider
26 | import androidx.lifecycle.lifecycleScope
27 | import androidx.recyclerview.widget.LinearLayoutManager
28 | import com.hadilq.guidomia.core.api.ViewModelFactory
29 | import com.hadilq.guidomia.core.api.viewBinding
30 | import com.hadilq.guidomia.di.api.FragmentScope
31 | import com.hadilq.guidomia.di.api.SingleIn
32 | import com.hadilq.guidomia.guidomia.impl.R
33 | import com.hadilq.guidomia.guidomia.impl.databinding.FragmentGuidomiaBinding
34 | import kotlinx.coroutines.flow.collect
35 | import javax.inject.Inject
36 |
37 | @SingleIn(FragmentScope::class)
38 | class GuidomiaFragment @Inject constructor(
39 | private val viewModelFactory: ViewModelFactory,
40 | private val adapter: GuidomiaRecyclerAdapter,
41 | ) : Fragment() {
42 |
43 | private val binding by viewBinding { FragmentGuidomiaBinding.inflate(layoutInflater) }
44 |
45 | internal val viewModel: GuidomiaViewModel by lazy {
46 | ViewModelProvider(this, viewModelFactory)
47 | .get(GuidomiaViewModel::class.java)
48 | }
49 |
50 | override fun onCreate(savedInstanceState: Bundle?) {
51 | super.onCreate(savedInstanceState)
52 | lifecycleScope.launchWhenStarted {
53 | viewModel.uiState.collect { uiState ->
54 | when (uiState) {
55 | is CarListUiState.Success -> adapter.submitList(uiState.list)
56 | is CarListUiState.Error -> TODO()
57 | }
58 | }
59 | }
60 | }
61 |
62 | override fun onCreateView(
63 | inflater: LayoutInflater,
64 | container: ViewGroup?,
65 | savedInstanceState: Bundle?
66 | ): View {
67 | binding.rvGuidomia.adapter = adapter
68 | binding.rvGuidomia.layoutManager = LinearLayoutManager(context)
69 | (requireActivity() as? AppCompatActivity)?.apply {
70 | setSupportActionBar(binding.toolbar)
71 | supportActionBar?.setHomeAsUpIndicator(
72 | ContextCompat.getDrawable(this, R.drawable.ic_baseline_dehaze_24)
73 | )
74 | supportActionBar?.setDisplayHomeAsUpEnabled(true)
75 | supportActionBar?.setDisplayShowTitleEnabled(false)
76 | binding.toolbar.logo = ContextCompat.getDrawable(this, R.drawable.logo)
77 | }
78 |
79 |
80 | return binding.root
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/presentation/GuidomiaNavigatorImpl.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.presentation
17 |
18 | import androidx.appcompat.app.AppCompatActivity
19 | import com.hadilq.guidomia.core.api.FragmentFactory
20 | import com.hadilq.guidomia.di.api.AppScope
21 | import com.hadilq.guidomia.guidomia.api.GuidomiaNavigator
22 | import com.hadilq.guidomia.guidomia.api.GuidomiaNavigatorFactory
23 | import com.hadilq.guidomia.singleactivity.api.NavigatorFactory
24 | import com.squareup.anvil.annotations.ContributesBinding
25 | import dagger.assisted.Assisted
26 | import dagger.assisted.AssistedFactory
27 | import dagger.assisted.AssistedInject
28 |
29 | @ContributesBinding(AppScope::class)
30 | @AssistedFactory
31 | interface GuidomiaNavigatorFactoryImpl : GuidomiaNavigatorFactory {
32 |
33 | override fun create(activity: AppCompatActivity): GuidomiaNavigatorImpl
34 | }
35 |
36 | class GuidomiaNavigatorImpl @AssistedInject constructor(
37 | private val navigatorFactory: NavigatorFactory,
38 | private val fragmentFactory: FragmentFactory,
39 | @Assisted private val activity: AppCompatActivity
40 | ) : GuidomiaNavigator {
41 |
42 | override fun commit() {
43 | navigatorFactory.create(activity)
44 | .commit(fragmentFactory.instantiate(GuidomiaFragment::class))
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/presentation/GuidomiaRecyclerAdapter.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.presentation
17 |
18 | import android.text.Editable
19 | import android.text.TextWatcher
20 | import android.view.LayoutInflater
21 | import android.view.View
22 | import android.view.ViewGroup
23 | import androidx.core.content.ContextCompat
24 | import androidx.recyclerview.widget.AsyncListDiffer
25 | import androidx.recyclerview.widget.DiffUtil.ItemCallback
26 | import androidx.recyclerview.widget.RecyclerView
27 | import com.hadilq.guidomia.guidomia.impl.R
28 | import com.hadilq.guidomia.guidomia.impl.databinding.CarItemBinding
29 | import com.hadilq.guidomia.guidomia.impl.databinding.FilterItemBinding
30 | import com.hadilq.guidomia.guidomia.impl.databinding.LineItemBinding
31 | import com.hadilq.guidomia.guidomia.impl.databinding.ProsConsItemBinding
32 | import com.hadilq.guidomia.guidomia.impl.domain.entity.MakeEntity
33 | import com.hadilq.guidomia.guidomia.impl.domain.entity.ModelEntity
34 | import dagger.assisted.Assisted
35 | import dagger.assisted.AssistedFactory
36 | import dagger.assisted.AssistedInject
37 | import javax.inject.Inject
38 |
39 |
40 | class GuidomiaRecyclerAdapter @Inject constructor(
41 | private val carViewHolderFactory: CarViewHolderFactory,
42 | private val lineViewHolderFactory: LineViewHolderFactory,
43 | private val filterViewHolderFactory: FilterViewHolderFactory,
44 | ) : RecyclerView.Adapter() {
45 |
46 | private val diffCallback: ItemCallback = object : ItemCallback() {
47 |
48 | override fun areItemsTheSame(oldItem: CarListModel, newItem: CarListModel): Boolean = when {
49 | oldItem is CarModel && newItem is CarModel -> oldItem.model == newItem.model
50 | oldItem is LineModel && newItem is LineModel -> true
51 | oldItem is FilterModel && newItem is FilterModel -> true
52 | else -> false
53 | }
54 |
55 | override fun areContentsTheSame(oldItem: CarListModel, newItem: CarListModel): Boolean =
56 | oldItem == newItem
57 | }
58 |
59 | private val diff = AsyncListDiffer(this, diffCallback)
60 |
61 | fun submitList(list: List) {
62 | diff.submitList(list)
63 | }
64 |
65 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
66 | return when (CarListType.values()[viewType]) {
67 | CarListType.LINE -> lineViewHolderFactory.create(
68 | LineItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
69 | )
70 | CarListType.CAR -> carViewHolderFactory.create(
71 | CarItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
72 | )
73 | CarListType.FILTER -> filterViewHolderFactory.create(
74 | FilterItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
75 | )
76 | }
77 | }
78 |
79 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
80 | val item = diff.currentList[position]
81 | when (item.type) {
82 | CarListType.LINE -> Unit
83 | CarListType.CAR -> (holder as CarViewHolder).bind(item as CarModel)
84 | CarListType.FILTER -> (holder as FilterViewHolder).bind(item as FilterModel)
85 | }
86 | }
87 |
88 | override fun getItemCount(): Int = diff.currentList.size
89 |
90 | override fun getItemViewType(position: Int): Int {
91 | return diff.currentList[position].type.index
92 | }
93 | }
94 |
95 | @AssistedFactory
96 | interface LineViewHolderFactory {
97 |
98 | fun create(binding: LineItemBinding): LineViewHolder
99 | }
100 |
101 | class LineViewHolder @AssistedInject constructor(
102 | @Assisted private val binding: LineItemBinding,
103 | ) : RecyclerView.ViewHolder(binding.root)
104 |
105 | @AssistedFactory
106 | interface CarViewHolderFactory {
107 |
108 | fun create(binding: CarItemBinding): CarViewHolder
109 | }
110 |
111 | class CarViewHolder @AssistedInject constructor(
112 | @Assisted private val binding: CarItemBinding,
113 | private val carItemOnClick: CarItemOnClick,
114 | ) : RecyclerView.ViewHolder(binding.root) {
115 |
116 | private val proConsBindingPool = ProConsItemPool(binding.root)
117 |
118 | fun bind(car: CarModel) = binding.apply {
119 | ivCar.setImageDrawable(ContextCompat.getDrawable(root.context, car.image.value))
120 | tvTitle.text = root.context.getString(
121 | R.string.car_item_title, car.make.value, car.model.value
122 | )
123 | tvPrice.text =
124 | binding.root.context.getString(R.string.car_item_price, (car.price.value / 1000).toInt())
125 | ivStar1.visibility = if (car.rate.value >= 1) View.VISIBLE else View.GONE
126 | ivStar2.visibility = if (car.rate.value >= 2) View.VISIBLE else View.GONE
127 | ivStar3.visibility = if (car.rate.value >= 3) View.VISIBLE else View.GONE
128 | ivStar4.visibility = if (car.rate.value >= 4) View.VISIBLE else View.GONE
129 | ivStar5.visibility = if (car.rate.value >= 5) View.VISIBLE else View.GONE
130 |
131 | if (car.collapsed) {
132 | tvPros.visibility = View.GONE
133 | llPros.visibility = View.GONE
134 | tvCons.visibility = View.GONE
135 | llCons.visibility = View.GONE
136 | } else {
137 | llPros.removeAllViews()
138 | llCons.removeAllViews()
139 | proConsBindingPool.releaseAll()
140 | if (car.pros.isEmpty()) {
141 | tvPros.visibility = View.GONE
142 | llPros.visibility = View.GONE
143 | } else {
144 | tvPros.visibility = View.VISIBLE
145 | llPros.visibility = View.VISIBLE
146 | car.pros.forEach { pro ->
147 | val prosConsBinding = proConsBindingPool.newItem
148 | prosConsBinding.root.text = pro
149 | llPros.addView(
150 | prosConsBinding.root,
151 | ViewGroup.LayoutParams.MATCH_PARENT,
152 | ViewGroup.LayoutParams.WRAP_CONTENT
153 | )
154 | }
155 | }
156 |
157 | if (car.cons.isEmpty()) {
158 | tvCons.visibility = View.GONE
159 | llCons.visibility = View.GONE
160 | } else {
161 | tvCons.visibility = View.VISIBLE
162 | llCons.visibility = View.VISIBLE
163 | car.cons.forEach { con ->
164 | val prosConsBinding = proConsBindingPool.newItem
165 | prosConsBinding.root.text = con
166 | llCons.addView(
167 | prosConsBinding.root,
168 | ViewGroup.LayoutParams.MATCH_PARENT,
169 | ViewGroup.LayoutParams.WRAP_CONTENT
170 | )
171 | }
172 | }
173 | }
174 | root.setOnClickListener { carItemOnClick.onClickCar(adapterPosition) }
175 | }
176 |
177 | /**
178 | * To avoid inflation on each click.
179 | */
180 | private class ProConsItemPool(private val root: ViewGroup) {
181 |
182 | private val pool = ArrayList()
183 |
184 | val newItem: ProsConsItemBinding
185 | get() =
186 | pool.find { it.released }?.let {
187 | it.released = false
188 | it.prosConsItemBinding
189 | } ?: run {
190 | val prosConsBinding =
191 | ProsConsItemBinding.inflate(LayoutInflater.from(root.context), root, false)
192 | pool.add(ItemState(prosConsBinding, false))
193 | prosConsBinding
194 | }
195 |
196 | fun releaseAll() {
197 | pool.forEach { it.released = true }
198 | }
199 |
200 | private class ItemState(
201 | val prosConsItemBinding: ProsConsItemBinding,
202 | var released: Boolean = true
203 | )
204 | }
205 | }
206 |
207 | @AssistedFactory
208 | interface FilterViewHolderFactory {
209 |
210 | fun create(binding: FilterItemBinding): FilterViewHolder
211 | }
212 |
213 | class FilterViewHolder @AssistedInject constructor(
214 | @Assisted private val binding: FilterItemBinding,
215 | private val carItemFilter: CarItemFilter
216 | ) : RecyclerView.ViewHolder(binding.root) {
217 |
218 | private lateinit var currentFilter: FilterModel
219 |
220 | private val makeTextWatcher: TextWatcher = object : TextWatcher {
221 | override fun afterTextChanged(s: Editable) {
222 | }
223 |
224 | override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
225 | }
226 |
227 | override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
228 | carItemFilter.onNewFilter(
229 | currentFilter.copy(
230 | make = MakeEntity(s.toString()),
231 | cursorPosition = start + count,
232 | makeUpdated = true,
233 | modelUpdated = false,
234 | )
235 | )
236 | }
237 | }
238 |
239 | private val modelTextWatcher: TextWatcher = object : TextWatcher {
240 | override fun afterTextChanged(s: Editable) {
241 | }
242 |
243 | override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
244 | }
245 |
246 | override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
247 | carItemFilter.onNewFilter(
248 | currentFilter.copy(
249 | model = ModelEntity(s.toString()),
250 | cursorPosition = start + count,
251 | makeUpdated = false,
252 | modelUpdated = true,
253 | )
254 | )
255 | }
256 | }
257 |
258 | fun bind(car: FilterModel) = binding.apply {
259 | currentFilter = car
260 |
261 | filterMake.removeTextChangedListener(makeTextWatcher)
262 | filterMake.setText(car.make.value)
263 | if (car.makeUpdated) {
264 | filterMake.requestFocus()
265 | filterMake.setSelection(car.cursorPosition)
266 | }
267 | filterMake.addTextChangedListener(makeTextWatcher)
268 |
269 | filterModel.removeTextChangedListener(modelTextWatcher)
270 | filterModel.setText(car.model.value)
271 | if (car.modelUpdated) {
272 | filterModel.requestFocus()
273 | filterModel.setSelection(car.cursorPosition)
274 | }
275 | filterModel.addTextChangedListener(modelTextWatcher)
276 | }
277 | }
278 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/presentation/GuidomiaViewModel.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.presentation
17 |
18 | import androidx.lifecycle.ViewModel
19 | import androidx.lifecycle.viewModelScope
20 | import com.hadilq.guidomia.di.api.RetainScope
21 | import com.hadilq.guidomia.di.api.SingleIn
22 | import com.hadilq.guidomia.guidomia.impl.domain.entity.CarEntity
23 | import com.hadilq.guidomia.guidomia.impl.domain.usecase.GetCars
24 | import com.hadilq.guidomia.guidomia.impl.domain.usecase.GetFilteredCars
25 | import kotlinx.coroutines.flow.MutableStateFlow
26 | import kotlinx.coroutines.flow.StateFlow
27 | import kotlinx.coroutines.flow.collect
28 | import kotlinx.coroutines.launch
29 | import javax.inject.Inject
30 |
31 | @SingleIn(RetainScope::class)
32 | class GuidomiaViewModel @Inject constructor(
33 | private val getCars: GetCars,
34 | private val getFilteredCars: GetFilteredCars,
35 | private val carMapper: CarModelMapper,
36 | ) : ViewModel(), CarItemOnClick, CarItemFilter {
37 |
38 | private val _uiState = MutableStateFlow(CarListUiState.Success(emptyList()))
39 | val uiState: StateFlow = _uiState
40 |
41 | init {
42 | viewModelScope.launch {
43 | getCars().collect(::updateCars)
44 | }
45 | }
46 |
47 | private fun updateCars(cars: List, filter: FilterModel = FilterModel()) {
48 | val list = cars.flatMapIndexed { index, car ->
49 | val collapsed = index != 0
50 | listOf(carMapper.map(car, collapsed), LineModel)
51 | }.toMutableList()
52 | if (list.size > 0) {
53 | list.removeAt(list.size - 1)
54 | }
55 | list.add(0, filter)
56 | _uiState.value = CarListUiState.Success(list)
57 | }
58 |
59 | override fun onClickCar(position: Int) {
60 | viewModelScope.launch {
61 | _uiState.value = CarListUiState.Success(_uiState.value.list.mapIndexed { index, carList ->
62 | if (carList is CarModel) {
63 | if (index == position) {
64 | carList.copy(collapsed = false)
65 | } else {
66 | carList.copy(collapsed = true)
67 | }
68 | } else {
69 | carList
70 | }
71 | })
72 | }
73 | }
74 |
75 | override fun onNewFilter(filterModel: FilterModel) {
76 | viewModelScope.launch {
77 | getFilteredCars(carMapper.map(filterModel)).collect {
78 | updateCars(it, filterModel)
79 | }
80 | }
81 | }
82 | }
83 |
84 | sealed class CarListUiState {
85 | data class Success(val list: List) : CarListUiState()
86 | data class Error(val exception: Throwable) : CarListUiState()
87 | }
88 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/presentation/di/GuidomiaFragmentComponent.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.presentation.di
17 |
18 | import androidx.fragment.app.Fragment
19 | import androidx.lifecycle.ViewModel
20 | import com.hadilq.guidomia.core.api.FragmentKey
21 | import com.hadilq.guidomia.core.api.SimpleFragmentFactory
22 | import com.hadilq.guidomia.core.api.SimpleViewModelFactory
23 | import com.hadilq.guidomia.core.api.ViewModelKey
24 | import com.hadilq.guidomia.di.api.AppScope
25 | import com.hadilq.guidomia.di.api.FragmentScope
26 | import com.hadilq.guidomia.di.api.RetainScope
27 | import com.hadilq.guidomia.di.api.SingleIn
28 | import com.hadilq.guidomia.guidomia.impl.presentation.GuidomiaFragment
29 | import com.hadilq.guidomia.guidomia.impl.presentation.GuidomiaViewModel
30 | import com.squareup.anvil.annotations.ContributesMultibinding
31 | import com.squareup.anvil.annotations.ContributesTo
32 | import com.squareup.anvil.annotations.MergeSubcomponent
33 | import dagger.Subcomponent
34 | import javax.inject.Inject
35 |
36 | @[ContributesMultibinding(AppScope::class) FragmentKey(GuidomiaFragment::class)]
37 | class GuidomiaFragmentFactory @Inject constructor(
38 | private val componentBuilder: GuidomiaFragmentComponent.Builder
39 | ) : SimpleFragmentFactory {
40 |
41 | override fun instantiate(): Fragment = componentBuilder.build().guidomiaFragment()
42 | }
43 |
44 | @[ContributesMultibinding(AppScope::class) ViewModelKey(GuidomiaViewModel::class)]
45 | class GuidomiaViewModelFactory @Inject constructor(
46 | private val componentBuilder: GuidomiaRetainComponent.Builder
47 | ) : SimpleViewModelFactory {
48 |
49 | override fun create(): ViewModel = componentBuilder.build().guidomiaViewModel()
50 | }
51 |
52 | @[SingleIn(FragmentScope::class) MergeSubcomponent(FragmentScope::class)]
53 | interface GuidomiaFragmentComponent {
54 |
55 | @Subcomponent.Builder
56 | interface Builder {
57 | fun build(): GuidomiaFragmentComponent
58 | }
59 |
60 | fun guidomiaFragment(): GuidomiaFragment
61 | }
62 |
63 | @[SingleIn(RetainScope::class) MergeSubcomponent(RetainScope::class)]
64 | interface GuidomiaRetainComponent {
65 |
66 | @Subcomponent.Builder
67 | interface Builder {
68 | fun build(): GuidomiaRetainComponent
69 | }
70 |
71 | fun guidomiaViewModel(): GuidomiaViewModel
72 | }
73 |
74 | @[SingleIn(AppScope::class) ContributesTo(AppScope::class)]
75 | interface GuidomiaFragmentComponentProvider {
76 |
77 | fun guidomiaFragmentComponentBuilder(): GuidomiaFragmentComponent.Builder
78 | }
79 |
80 | @[SingleIn(AppScope::class) ContributesTo(AppScope::class)]
81 | interface GuidomiaRetainComponentProvider {
82 |
83 | fun guidomiaRetainComponentBuilder(): GuidomiaRetainComponent.Builder
84 | }
85 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/java/com/hadilq/guidomia/guidomia/impl/presentation/di/GuidomiaModule.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.presentation.di
17 |
18 | import com.hadilq.guidomia.di.api.FragmentScope
19 | import com.hadilq.guidomia.di.api.RetainScope
20 | import com.hadilq.guidomia.guidomia.impl.presentation.CarItemFilter
21 | import com.hadilq.guidomia.guidomia.impl.presentation.CarItemOnClick
22 | import com.hadilq.guidomia.guidomia.impl.presentation.GuidomiaFragment
23 | import com.squareup.anvil.annotations.ContributesTo
24 | import dagger.Module
25 | import dagger.Provides
26 |
27 | @[Module ContributesTo(scope = FragmentScope::class)]
28 | object GuidomiaModule {
29 |
30 | /**
31 | * The lifecycle of [RetainScope] is longer than the lifecycle of [FragmentScope],
32 | * so without any leak we can pass objects of [RetainScope] to [FragmentScope].
33 | */
34 | @Provides
35 | fun provideCarItemOnClick(fragment: GuidomiaFragment): CarItemOnClick =
36 | fragment.viewModel
37 |
38 | /**
39 | * The lifecycle of [RetainScope] is longer than the lifecycle of [FragmentScope],
40 | * so without any leak we can pass objects of [RetainScope] to [FragmentScope].
41 | */
42 | @Provides
43 | fun provideCarItemFilter(fragment: GuidomiaFragment): CarItemFilter =
44 | fragment.viewModel
45 | }
46 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/drawable/alpine_roadster.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/guidomia/impl/src/main/res/drawable/alpine_roadster.jpg
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/drawable/bmw_330i.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/guidomia/impl/src/main/res/drawable/bmw_330i.jpg
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/drawable/filter_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/drawable/filter_edittext_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/drawable/ic_baseline_dehaze_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/drawable/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/guidomia/impl/src/main/res/drawable/logo.png
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/drawable/mercedez_benz_glc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/guidomia/impl/src/main/res/drawable/mercedez_benz_glc.jpg
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/drawable/pros_cons_dot.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/drawable/range_rover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/guidomia/impl/src/main/res/drawable/range_rover.jpg
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/drawable/star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/guidomia/impl/src/main/res/drawable/star.png
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/drawable/tacoma.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hadilq/CleanArchitecture/f4c5b566d22547cc159e193568f87c8210310604/guidomia/impl/src/main/res/drawable/tacoma.jpg
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/layout/car_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
34 |
35 |
44 |
45 |
54 |
55 |
64 |
65 |
74 |
75 |
84 |
85 |
93 |
94 |
105 |
106 |
116 |
117 |
128 |
129 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/layout/filter_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
29 |
30 |
45 |
46 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/layout/fragment_guidomia.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
19 |
20 |
25 |
26 |
36 |
37 |
50 |
51 |
63 |
64 |
65 |
66 |
73 |
74 |
75 |
76 |
77 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/layout/line_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
15 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/layout/pros_cons_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 28dp
4 | 25dp
5 | 1dp
6 | 20dp
7 | 42sp
8 |
9 | 16dp
10 | 110dp
11 | 80dp
12 | 5dp
13 | 20dp
14 | 20dp
15 | 8dp
16 | 24dp
17 | 4dp
18 | 12dp
19 | 12dp
20 | 14dp
21 | 34dp
22 | 12dp
23 | 44dp
24 | 12dp
25 | 8dp
26 | 4dp
27 | 18dp
28 | 14dp
29 | 32dp
30 | 10dp
31 | 14dp
32 | 16dp
33 | 24dp
34 | 8dp
35 | 16dp
36 |
37 |
--------------------------------------------------------------------------------
/guidomia/impl/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Tacoma 2021
3 | Get your\'s now
4 |
5 | Price : %dk
6 | %s %s
7 | Pros :
8 | Cons :
9 | Filters
10 | Any make
11 | Any model
12 |
13 |
--------------------------------------------------------------------------------
/guidomia/impl/src/test/java/com/hadilq/guidomia/guidomia/impl/data/repository/CarRepositoryTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.data.repository
17 |
18 | import com.hadilq.guidomia.guidomia.impl.data.datasource.CarDatabaseDataSource
19 | import com.hadilq.guidomia.guidomia.impl.data.datasource.CarsCacheDataSource
20 | import com.hadilq.guidomia.guidomia.impl.data.datasource.CarsDataSource
21 | import com.hadilq.guidomia.guidomia.impl.domain.entity.CarEntityProvider
22 | import io.mockk.coEvery
23 | import io.mockk.coVerify
24 | import io.mockk.every
25 | import io.mockk.impl.annotations.MockK
26 | import io.mockk.junit5.MockKExtension
27 | import io.mockk.verify
28 | import kotlinx.coroutines.flow.collect
29 | import kotlinx.coroutines.flow.flowOf
30 | import kotlinx.coroutines.runBlocking
31 | import org.junit.jupiter.api.Test
32 | import org.junit.jupiter.api.extension.ExtendWith
33 |
34 | @ExtendWith(MockKExtension::class)
35 | internal class CarRepositoryTest {
36 |
37 | @MockK
38 | lateinit var carsDataSource: CarsDataSource
39 |
40 | @MockK
41 | lateinit var cacheDataSource: CarsCacheDataSource
42 |
43 | @MockK
44 | lateinit var carDatabaseDataSource: CarDatabaseDataSource
45 |
46 | @Test
47 | fun `given_empty_cache_and_empty_database then_fetch_cars`() = runBlocking {
48 | val repository = CarsRepository(carsDataSource, cacheDataSource, carDatabaseDataSource)
49 | every { cacheDataSource.caching } returns listOf()
50 | coEvery { carDatabaseDataSource.isEmpty() } returns true
51 | coEvery { carDatabaseDataSource.featureFlag() } returns true
52 | every { carsDataSource.fetchCars() } returns flowOf()
53 |
54 | repository.getCars().collect()
55 |
56 | verify(exactly = 1) { carsDataSource.fetchCars() }
57 | coVerify(exactly = 1) { cacheDataSource.caching }
58 | coVerify(exactly = 0) { carDatabaseDataSource.fetchCars() }
59 | }
60 |
61 | @Test
62 | fun `given_empty_cache_and_not_available_database then_fetch_cars`() = runBlocking {
63 | val repository = CarsRepository(carsDataSource, cacheDataSource, carDatabaseDataSource)
64 | every { cacheDataSource.caching } returns listOf()
65 | coEvery { carDatabaseDataSource.isEmpty() } returns true
66 | coEvery { carDatabaseDataSource.featureFlag() } returns false
67 | every { carsDataSource.fetchCars() } returns flowOf()
68 |
69 | repository.getCars().collect()
70 |
71 | verify(exactly = 1) { carsDataSource.fetchCars() }
72 | coVerify(exactly = 1) { cacheDataSource.caching }
73 | coVerify(exactly = 0) { carDatabaseDataSource.fetchCars() }
74 | }
75 |
76 | @Test
77 | fun `given_empty_cache_and_non_empty_database then_fetch_cars_from_database`() = runBlocking {
78 | val repository = CarsRepository(carsDataSource, cacheDataSource, carDatabaseDataSource)
79 | every { cacheDataSource.caching } returns listOf()
80 | coEvery { carDatabaseDataSource.isEmpty() } returns false
81 | coEvery { carDatabaseDataSource.featureFlag() } returns true
82 | coEvery { carDatabaseDataSource.fetchCars() } returns listOf()
83 |
84 | repository.getCars().collect()
85 |
86 | coVerify(exactly = 0) { carsDataSource.fetchCars() }
87 | coVerify(exactly = 1) { cacheDataSource.caching }
88 | coVerify(exactly = 1) { carDatabaseDataSource.fetchCars() }
89 | }
90 |
91 | @Test
92 | fun `given_non_empty_cache then_dont_fetch_cars`() = runBlocking {
93 | val repository = CarsRepository(carsDataSource, cacheDataSource, carDatabaseDataSource)
94 | every { cacheDataSource.caching } returns listOf(CarEntityProvider.provide())
95 | every { carsDataSource.fetchCars() } returns flowOf()
96 |
97 | repository.getCars().collect()
98 |
99 | verify(exactly = 0) { carsDataSource.fetchCars() }
100 | coVerify(exactly = 2) { cacheDataSource.caching }
101 | coVerify(exactly = 0) { carDatabaseDataSource.fetchCars() }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/guidomia/impl/src/test/java/com/hadilq/guidomia/guidomia/impl/domain/entity/CarEntityProvider.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.entity
17 |
18 | object CarEntityProvider {
19 |
20 | fun provide(
21 | make: MakeEntity = MakeEntity("make"),
22 | model: ModelEntity = ModelEntity("model")
23 | ) = CarEntity(
24 | make = make,
25 | model = model,
26 | image = ImageEntity(0),
27 | price = PriceEntity(1.0f),
28 | rate = Rate.FIVE,
29 | pros = listOf("pro"),
30 | cons = listOf("con"),
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/guidomia/impl/src/test/java/com/hadilq/guidomia/guidomia/impl/domain/entity/MakeEntityProvider.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.entity
17 |
18 | object MakeEntityProvider {
19 | fun provide(make: String = "make") = MakeEntity(make)
20 | }
21 |
--------------------------------------------------------------------------------
/guidomia/impl/src/test/java/com/hadilq/guidomia/guidomia/impl/domain/entity/ModelEntityProvider.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.entity
17 |
18 | object ModelEntityProvider {
19 | fun provide(model: String = "model") = ModelEntity(model)
20 | }
21 |
--------------------------------------------------------------------------------
/guidomia/impl/src/test/java/com/hadilq/guidomia/guidomia/impl/domain/usecase/GetFilteredCarsTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.impl.domain.usecase
17 |
18 | import com.hadilq.guidomia.guidomia.impl.data.repository.CarsRepository
19 | import com.hadilq.guidomia.guidomia.impl.domain.entity.CarEntityProvider
20 | import com.hadilq.guidomia.guidomia.impl.domain.entity.FilterEntity
21 | import com.hadilq.guidomia.guidomia.impl.domain.entity.MakeEntityProvider
22 | import com.hadilq.guidomia.guidomia.impl.domain.entity.ModelEntityProvider
23 | import io.mockk.every
24 | import io.mockk.impl.annotations.MockK
25 | import io.mockk.junit5.MockKExtension
26 | import kotlinx.coroutines.flow.first
27 | import kotlinx.coroutines.flow.flowOf
28 | import kotlinx.coroutines.runBlocking
29 | import org.junit.jupiter.api.Test
30 | import org.junit.jupiter.api.extension.ExtendWith
31 |
32 | @ExtendWith(MockKExtension::class)
33 | internal class GetFilteredCarsTest {
34 |
35 | @MockK
36 | lateinit var carsRepository: CarsRepository
37 |
38 | @Test
39 | fun `given_match_make then_return_match`() = runBlocking {
40 | val getFilteredCars = GetFilteredCars(carsRepository)
41 | val make = "Test Make"
42 | val filter = FilterEntity(MakeEntityProvider.provide(make), ModelEntityProvider.provide())
43 | val expected = listOf(
44 | CarEntityProvider.provide(
45 | make = MakeEntityProvider.provide("complete $make name"),
46 | model = ModelEntityProvider.provide()
47 | )
48 | )
49 | every { carsRepository.getCars() } returns flowOf(expected)
50 |
51 | val result = getFilteredCars.invoke(filter).first()
52 |
53 | assert(result == expected)
54 | }
55 |
56 | @Test
57 | fun `given_match_model then_return_match`() = runBlocking {
58 | val getFilteredCars = GetFilteredCars(carsRepository)
59 | val model = "Test Model"
60 | val filter = FilterEntity(MakeEntityProvider.provide(), ModelEntityProvider.provide(model))
61 | val expected = listOf(
62 | CarEntityProvider.provide(
63 | make = MakeEntityProvider.provide(),
64 | model = ModelEntityProvider.provide("complete $model name")
65 | )
66 | )
67 | every { carsRepository.getCars() } returns flowOf(expected)
68 |
69 | val result = getFilteredCars.invoke(filter).first()
70 |
71 | assert(result == expected)
72 | }
73 |
74 | @Test
75 | fun `given_not_match_make then_return_empty`() = runBlocking {
76 | val getFilteredCars = GetFilteredCars(carsRepository)
77 | val make = "Test Make"
78 | val filter = FilterEntity(MakeEntityProvider.provide(make), ModelEntityProvider.provide())
79 | val expected = listOf(
80 | CarEntityProvider.provide(
81 | make = MakeEntityProvider.provide("Some other make"),
82 | model = ModelEntityProvider.provide()
83 | )
84 | )
85 | every { carsRepository.getCars() } returns flowOf(expected)
86 |
87 | val result = getFilteredCars.invoke(filter).first()
88 |
89 | assert(result.isEmpty())
90 | }
91 |
92 | @Test
93 | fun `given_not_match_model then_return_empty`() = runBlocking {
94 | val getFilteredCars = GetFilteredCars(carsRepository)
95 | val model = "Test Model"
96 | val filter = FilterEntity(MakeEntityProvider.provide(), ModelEntityProvider.provide(model))
97 | val expected = listOf(
98 | CarEntityProvider.provide(
99 | make = MakeEntityProvider.provide(),
100 | model = ModelEntityProvider.provide("Some other model")
101 | )
102 | )
103 | every { carsRepository.getCars() } returns flowOf(expected)
104 |
105 | val result = getFilteredCars.invoke(filter).first()
106 |
107 | assert(result.isEmpty())
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/guidomia/public/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/guidomia/public/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | plugins {
17 | id("com.android.library")
18 | kotlin("android")
19 | }
20 |
21 | configureAndroidLibrary()
22 |
23 | dependencies {
24 | implementation(project(Modules.featureFlagsPublic))
25 |
26 | implementation(Depends.kotlinStdLib)
27 | implementation(Depends.appCompat)
28 | }
29 |
--------------------------------------------------------------------------------
/guidomia/public/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/guidomia/public/src/main/java/com/hadilq/guidomia/guidomia/api/GetGuidomiaNavigatorFactoryCommand.kt:
--------------------------------------------------------------------------------
1 | package com.hadilq.guidomia.guidomia.api
2 |
3 | import com.github.hadilq.commandku.api.Command
4 |
5 | class GetGuidomiaNavigatorFactoryCommand : Command
6 |
7 | class GetGuidomiaNavigatorFactoryCommandResult(
8 | val result: GuidomiaNavigatorFactory
9 | ) : Command
--------------------------------------------------------------------------------
/guidomia/public/src/main/java/com/hadilq/guidomia/guidomia/api/GuidomiaNavigator.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.api
17 |
18 | interface GuidomiaNavigator {
19 |
20 | fun commit()
21 | }
22 |
--------------------------------------------------------------------------------
/guidomia/public/src/main/java/com/hadilq/guidomia/guidomia/api/GuidomiaNavigatorFactory.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.guidomia.api
17 |
18 | import androidx.appcompat.app.AppCompatActivity
19 |
20 | interface GuidomiaNavigatorFactory {
21 |
22 | fun create(activity: AppCompatActivity): GuidomiaNavigator
23 | }
24 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | include(":app")
17 | include(":di:public")
18 | include(":core:public")
19 | include(":core:impl")
20 | include(":single-activity:public")
21 | include(":single-activity:impl")
22 | include(":guidomia:public")
23 | include(":guidomia:impl")
24 | include(":database:public")
25 | include(":database:impl")
26 | include(":feature-flags:public")
27 | include(":feature-flags:impl")
28 |
--------------------------------------------------------------------------------
/single-activity/impl/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/single-activity/impl/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | plugins {
17 | id("com.android.library")
18 | kotlin("android")
19 | kotlin("kapt")
20 | id("com.squareup.anvil") version Versions.anvil
21 | }
22 |
23 | configureAndroidLibrary()
24 |
25 | android {
26 | buildFeatures {
27 | viewBinding = true
28 | }
29 | }
30 |
31 | anvil {
32 | generateDaggerFactories = true
33 | }
34 |
35 | dependencies {
36 | implementation(project(Modules.singleActivityPublic))
37 | implementation(project(Modules.corePublic))
38 | implementation(project(Modules.diPublic))
39 | implementation(project(Modules.featureFlagsPublic))
40 | implementation(project(Modules.guidomiaPublic))
41 |
42 | kapt(Depends.lifecycleCompiler)
43 |
44 | implementation(Depends.kotlinStdLib)
45 | implementation(Depends.coreKtx)
46 | implementation(Depends.appCompat)
47 | implementation(Depends.material)
48 | implementation(Depends.constraintLayout)
49 | implementation(Depends.fragment)
50 | implementation(Depends.dagger)
51 |
52 | testImplementation(Depends.junit)
53 | androidTestImplementation(Depends.testExtJunit)
54 | androidTestImplementation(Depends.espressoCore)
55 | }
56 |
--------------------------------------------------------------------------------
/single-activity/impl/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/single-activity/impl/src/main/java/com/hadilq/guidomia/singleactivity/impl/NavigatorImpl.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.singleactivity.impl
17 |
18 | import android.os.Bundle
19 | import androidx.appcompat.app.AppCompatActivity
20 | import androidx.fragment.app.Fragment
21 | import androidx.fragment.app.commit
22 | import androidx.lifecycle.Lifecycle
23 | import androidx.lifecycle.LifecycleObserver
24 | import androidx.lifecycle.OnLifecycleEvent
25 | import com.hadilq.guidomia.di.api.AppScope
26 | import com.hadilq.guidomia.singleactivity.api.Navigator
27 | import com.hadilq.guidomia.singleactivity.api.NavigatorFactory
28 | import com.squareup.anvil.annotations.ContributesBinding
29 | import dagger.assisted.Assisted
30 | import dagger.assisted.AssistedFactory
31 | import dagger.assisted.AssistedInject
32 | import java.util.*
33 |
34 | @ContributesBinding(AppScope::class)
35 | @AssistedFactory
36 | interface NavigatorFactoryImpl : NavigatorFactory {
37 |
38 | override fun create(activity: AppCompatActivity): NavigatorImpl
39 | }
40 |
41 | class NavigatorImpl @AssistedInject constructor(
42 | @Assisted private val activity: AppCompatActivity
43 | ) : Navigator, LifecycleObserver {
44 |
45 | private var paused = true
46 | private var savedState = false
47 | private var fragmentsCache: Queue = ArrayDeque()
48 |
49 | init {
50 | activity.lifecycle.addObserver(this)
51 | activity.savedStateRegistry.registerSavedStateProvider(SAVED_STATE_KEY) {
52 | savedState = true
53 | paused = true
54 | fragmentsCache.clear()
55 | Bundle()
56 | }
57 | }
58 |
59 | @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
60 | fun onResume() {
61 | paused = false
62 | repeat(fragmentsCache.size) {
63 | val fragment = fragmentsCache.poll()
64 | if (fragment != null) {
65 | commit(fragment)
66 | }
67 | }
68 | }
69 |
70 | @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
71 | fun onPause() {
72 | paused = true
73 | }
74 |
75 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
76 | fun onDestroy() {
77 | fragmentsCache.clear()
78 | }
79 |
80 | override fun commit(fragment: Fragment) {
81 | if (paused) {
82 | if (savedState) {
83 | fragmentsCache.clear()
84 | } else {
85 | fragmentsCache.offer(fragment)
86 | }
87 | } else {
88 | activity.supportFragmentManager.commit {
89 | setReorderingAllowed(true)
90 | replace(R.id.fragment_content_main, fragment, fragment.tag)
91 | }
92 | }
93 | }
94 | }
95 |
96 | private const val SAVED_STATE_KEY = "SAVED_STATE_KEY"
97 |
--------------------------------------------------------------------------------
/single-activity/impl/src/main/java/com/hadilq/guidomia/singleactivity/impl/SingleActivity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.singleactivity.impl
17 |
18 | import android.os.Bundle
19 | import androidx.appcompat.app.AppCompatActivity
20 | import androidx.lifecycle.lifecycleScope
21 | import com.github.hadilq.commandku.api.CommandExecutor
22 | import com.hadilq.guidomia.core.api.viewBinding
23 | import com.hadilq.guidomia.di.api.SingleActivityScope
24 | import com.hadilq.guidomia.di.api.SingleIn
25 | import com.hadilq.guidomia.featureflags.api.FeatureFlag
26 | import com.hadilq.guidomia.featureflags.api.featureFlag
27 | import com.hadilq.guidomia.guidomia.api.GetGuidomiaNavigatorFactoryCommand
28 | import com.hadilq.guidomia.guidomia.api.GetGuidomiaNavigatorFactoryCommandResult
29 | import com.hadilq.guidomia.guidomia.api.GuidomiaNavigatorFactory
30 | import com.hadilq.guidomia.singleactivity.impl.databinding.ActivityMainBinding
31 | import com.hadilq.guidomia.singleactivity.impl.di.SingleActivityComponent
32 | import com.hadilq.guidomia.singleactivity.impl.di.SingleActivityComponentProvider
33 | import javax.inject.Inject
34 |
35 | @SingleIn(SingleActivityScope::class)
36 | class SingleActivity : AppCompatActivity() {
37 |
38 | private val component: SingleActivityComponent by lazy {
39 | (application as SingleActivityComponentProvider)
40 | .singleActivityComponentProvider
41 | .build()
42 | }
43 |
44 | @Inject
45 | internal lateinit var executor: CommandExecutor
46 |
47 | private var guidomiaNavigatorFactory: FeatureFlag? = null
48 |
49 | private val binding by viewBinding { ActivityMainBinding.inflate(layoutInflater) }
50 |
51 | override fun onCreate(savedInstanceState: Bundle?) {
52 | component.inject(this)
53 | super.onCreate(savedInstanceState)
54 | setContentView(binding.root)
55 |
56 | if (savedInstanceState == null) {
57 | openFirstPossiblePage()
58 | }
59 | }
60 |
61 | private fun openFirstPossiblePage() = lifecycleScope.launchWhenCreated {
62 | when (val flag = fetchFlag()) {
63 | is FeatureFlag.On -> flag.value.create(this@SingleActivity).commit()
64 | }
65 | }
66 |
67 | private suspend fun fetchFlag(): FeatureFlag =
68 | guidomiaNavigatorFactory ?: run {
69 | executor.featureFlag(
70 | GetGuidomiaNavigatorFactoryCommand(),
71 | GetGuidomiaNavigatorFactoryCommandResult::class,
72 | ).to { result }
73 | .also { guidomiaNavigatorFactory = it }
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/single-activity/impl/src/main/java/com/hadilq/guidomia/singleactivity/impl/di/SingleActivityComponent.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.singleactivity.impl.di
17 |
18 | import com.hadilq.guidomia.di.api.SingleActivityScope
19 | import com.hadilq.guidomia.di.api.SingleIn
20 | import com.hadilq.guidomia.singleactivity.impl.SingleActivity
21 | import com.squareup.anvil.annotations.MergeSubcomponent
22 | import dagger.Subcomponent
23 |
24 | @SingleIn(SingleActivityScope::class)
25 | @MergeSubcomponent(SingleActivityScope::class)
26 | interface SingleActivityComponent {
27 |
28 | @Subcomponent.Builder
29 | interface Builder {
30 | fun build(): SingleActivityComponent
31 | }
32 |
33 | fun inject(activity: SingleActivity)
34 | }
--------------------------------------------------------------------------------
/single-activity/impl/src/main/java/com/hadilq/guidomia/singleactivity/impl/di/SingleActivityComponentProvider.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.singleactivity.impl.di
17 |
18 | interface SingleActivityComponentProvider {
19 | val singleActivityComponentProvider: SingleActivityComponent.Builder
20 | }
--------------------------------------------------------------------------------
/single-activity/impl/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/single-activity/public/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/single-activity/public/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | plugins {
17 | id("com.android.library")
18 | kotlin("android")
19 | }
20 |
21 | configureAndroidLibrary()
22 |
23 | dependencies {
24 | implementation(project(Modules.corePublic))
25 |
26 | implementation(Depends.kotlinStdLib)
27 | implementation(Depends.appCompat)
28 | implementation(Depends.fragment)
29 | }
30 |
--------------------------------------------------------------------------------
/single-activity/public/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/single-activity/public/src/main/java/com/hadilq/guidomia/singleactivity/api/Navigator.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.singleactivity.api
17 |
18 | import androidx.fragment.app.Fragment
19 |
20 | interface Navigator {
21 | fun commit(fragment: Fragment)
22 | }
--------------------------------------------------------------------------------
/single-activity/public/src/main/java/com/hadilq/guidomia/singleactivity/api/NavigatorFactory.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Hadi Lashkari Ghouchani
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 |
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hadilq.guidomia.singleactivity.api
17 |
18 | import androidx.appcompat.app.AppCompatActivity
19 |
20 | interface NavigatorFactory {
21 |
22 | fun create(activity: AppCompatActivity): Navigator
23 | }
24 |
--------------------------------------------------------------------------------