├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── 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 │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── themes.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── values-night │ │ │ │ └── themes.xml │ │ │ ├── navigation │ │ │ │ └── nav_graph.xml │ │ │ ├── layout │ │ │ │ ├── activity_main.xml │ │ │ │ └── fragment_main.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── smarttoolfactory │ │ │ │ └── projectboilerplate │ │ │ │ ├── ui │ │ │ │ └── theme │ │ │ │ │ ├── Color.kt │ │ │ │ │ ├── Shape.kt │ │ │ │ │ ├── Type.kt │ │ │ │ │ └── Theme.kt │ │ │ │ ├── MyApplication.kt │ │ │ │ ├── MainViewModel.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ └── MainFragment.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── smarttoolfactory │ │ │ └── projectboilerplate │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── smarttoolfactory │ │ └── projectboilerplate │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle.kts ├── features └── feature │ ├── .gitignore │ ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── smarttoolfactory │ │ │ │ └── feature │ │ │ │ ├── model │ │ │ │ └── FeatureDependency.kt │ │ │ │ ├── di │ │ │ │ ├── FeatureComponent.kt │ │ │ │ └── FeatureModule.kt │ │ │ │ ├── viewmodel │ │ │ │ └── FeatureViewModel.kt │ │ │ │ └── FeatureFragment.kt │ │ ├── AndroidManifest.xml │ │ └── res │ │ │ ├── navigation │ │ │ └── nav_graph_feature.xml │ │ │ └── layout │ │ │ └── fragment_feature.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── smarttoolfactory │ │ │ └── feature │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── smarttoolfactory │ │ └── feature │ │ └── ExampleInstrumentedTest.kt │ └── build.gradle.kts ├── libraries ├── core │ ├── .gitignore │ ├── consumer-rules.pro │ ├── src │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── smarttoolfactory │ │ │ │ │ └── core │ │ │ │ │ ├── error │ │ │ │ │ └── NavigationException.kt │ │ │ │ │ ├── di │ │ │ │ │ ├── scope │ │ │ │ │ │ └── FeatureScope.kt │ │ │ │ │ ├── CoreModuleDependencies.kt │ │ │ │ │ ├── CoreModule.kt │ │ │ │ │ └── DataModule.kt │ │ │ │ │ ├── CoreDependency.kt │ │ │ │ │ ├── util │ │ │ │ │ ├── LifecycleOwnerExtension.kt │ │ │ │ │ ├── NavHostExtension.kt │ │ │ │ │ ├── FlowViewStateExtension.kt │ │ │ │ │ └── Event.kt │ │ │ │ │ ├── viewmodel │ │ │ │ │ └── NavControllerViewModel.kt │ │ │ │ │ ├── viewstate │ │ │ │ │ └── ViewState.kt │ │ │ │ │ └── ui │ │ │ │ │ ├── fragment │ │ │ │ │ ├── BaseViewBindingFragment.kt │ │ │ │ │ ├── navhost │ │ │ │ │ │ ├── BaseDynamicNavHostFragment.kt │ │ │ │ │ │ ├── BaseNavHostFragment.kt │ │ │ │ │ │ └── NavHostContainerFragment.kt │ │ │ │ │ └── DynamicNavigationFragment.kt │ │ │ │ │ ├── adapter │ │ │ │ │ └── BaseListAdapter.kt │ │ │ │ │ └── widget │ │ │ │ │ └── NestedScrollableHost.kt │ │ │ └── AndroidManifest.xml │ │ ├── test │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── smarttoolfactory │ │ │ │ └── core │ │ │ │ └── ExampleUnitTest.kt │ │ └── androidTest │ │ │ └── java │ │ │ └── com │ │ │ └── smarttoolfactory │ │ │ └── core │ │ │ └── ExampleInstrumentedTest.kt │ ├── proguard-rules.pro │ └── build.gradle.kts ├── data │ ├── .gitignore │ ├── consumer-rules.pro │ ├── src │ │ └── main │ │ │ ├── java │ │ │ └── com │ │ │ │ └── smarttoolfactory │ │ │ │ └── data │ │ │ │ ├── api │ │ │ │ └── SampleApi.kt │ │ │ │ ├── model │ │ │ │ ├── remote │ │ │ │ │ └── SampleData.kt │ │ │ │ ├── local │ │ │ │ │ └── SampleDataEntity.kt │ │ │ │ └── Mappables.kt │ │ │ │ ├── db │ │ │ │ ├── dao │ │ │ │ │ ├── SampleDao.kt │ │ │ │ │ └── BaseDao.kt │ │ │ │ └── SampleDatabase.kt │ │ │ │ ├── constant │ │ │ │ └── Constants.kt │ │ │ │ ├── source │ │ │ │ ├── RemoteDataSource.kt │ │ │ │ └── LocalDataSource.kt │ │ │ │ ├── repository │ │ │ │ └── Repository.kt │ │ │ │ ├── mapper │ │ │ │ └── Mappers.kt │ │ │ │ └── di │ │ │ │ ├── DatabaseModule.kt │ │ │ │ └── NetworkModule.kt │ │ │ └── AndroidManifest.xml │ ├── proguard-rules.pro │ ├── schemas │ │ └── com.smarttoolfactory.data.db.SampleDatabase │ │ │ └── 1.json │ └── build.gradle.kts ├── domain │ ├── .gitignore │ ├── consumer-rules.pro │ ├── src │ │ └── main │ │ │ ├── java │ │ │ └── com │ │ │ │ └── smarttoolfactory │ │ │ │ └── domain │ │ │ │ ├── error │ │ │ │ └── EmptyDataException.kt │ │ │ │ ├── base │ │ │ │ └── Disposable.kt │ │ │ │ ├── dispatcher │ │ │ │ └── UseCaseDispatchers.kt │ │ │ │ └── usecase │ │ │ │ └── UseCase.kt │ │ │ └── AndroidManifest.xml │ ├── proguard-rules.pro │ └── build.gradle.kts └── test-utils │ ├── .gitignore │ ├── consumer-rules.pro │ ├── src │ ├── test │ │ ├── resources │ │ │ └── response.json │ │ └── java │ │ │ └── com │ │ │ └── smarttoolfactory │ │ │ └── test_utils │ │ │ └── ExampleUnitTest.kt │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── smarttoolfactory │ │ │ └── test_utils │ │ │ ├── TestConstants.kt │ │ │ ├── rule │ │ │ ├── MockWebServerRule.kt │ │ │ └── TestCoroutineRule.kt │ │ │ ├── util │ │ │ ├── LiveDataTestUtil.kt │ │ │ └── ReadResourceUtil.kt │ │ │ ├── extension │ │ │ └── TestCoroutineExtension.kt │ │ │ └── test_observer │ │ │ ├── LiveDataTestObserver.kt │ │ │ └── FlowTestObserver.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── smarttoolfactory │ │ └── test_utils │ │ └── ExampleInstrumentedTest.kt │ ├── proguard-rules.pro │ └── build.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle.kts ├── .gitignore ├── scripts └── git-hooks │ └── pre-commit ├── gradle.properties ├── gradlew.bat ├── gradlew ├── config └── detekt │ └── detekt.yml └── README.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /features/feature/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /libraries/core/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /libraries/core/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libraries/data/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /libraries/data/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libraries/domain/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /libraries/domain/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libraries/test-utils/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /libraries/test-utils/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libraries/test-utils/src/test/resources/response.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/api/SampleApi.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.api 2 | 3 | interface SampleApi 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Android-DaggerHilt-DynamicFetureModule-Boilerplate/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Android-DaggerHilt-DynamicFetureModule-Boilerplate/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Android-DaggerHilt-DynamicFetureModule-Boilerplate/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Android-DaggerHilt-DynamicFetureModule-Boilerplate/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Android-DaggerHilt-DynamicFetureModule-Boilerplate/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Android-DaggerHilt-DynamicFetureModule-Boilerplate/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | include( 2 | ":app", 3 | ":libraries:core", 4 | ":libraries:data", 5 | ":libraries:domain", 6 | ":libraries:test-utils", 7 | ":features:feature" 8 | ) 9 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Android-DaggerHilt-DynamicFetureModule-Boilerplate/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Android-DaggerHilt-DynamicFetureModule-Boilerplate/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Android-DaggerHilt-DynamicFetureModule-Boilerplate/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Android-DaggerHilt-DynamicFetureModule-Boilerplate/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /libraries/domain/src/main/java/com/smarttoolfactory/domain/error/EmptyDataException.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.domain.error 2 | 3 | class EmptyDataException(message: String) : Exception(message) 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Android-DaggerHilt-DynamicFetureModule-Boilerplate/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /libraries/core/src/main/java/com/smarttoolfactory/core/error/NavigationException.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.core.error 2 | 3 | class NavigationException(override val message: String?) : Exception(message) 4 | -------------------------------------------------------------------------------- /libraries/core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /libraries/data/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /libraries/domain/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /libraries/test-utils/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /features/feature/src/main/java/com/smarttoolfactory/feature/model/FeatureDependency.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.feature.model 2 | 3 | import android.content.Context 4 | 5 | class FeatureDependency(private val context: Context) 6 | -------------------------------------------------------------------------------- /libraries/test-utils/src/main/java/com/smarttoolfactory/test_utils/TestConstants.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.test_utils 2 | 3 | const val RESPONSE_JSON_PATH = "response.json" 4 | const val SERVER_INTERNAL_ERROR_MESSAGE = "Unexpected error occurred" 5 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/model/remote/SampleData.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.model.remote 2 | 3 | import com.smarttoolfactory.data.model.DataTransferObject 4 | 5 | data class SampleData(val id: Int) : DataTransferObject 6 | -------------------------------------------------------------------------------- /libraries/core/src/main/java/com/smarttoolfactory/core/di/scope/FeatureScope.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.core.di.scope 2 | 3 | import javax.inject.Scope 4 | 5 | @Scope 6 | @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) 7 | annotation class FeatureScope 8 | -------------------------------------------------------------------------------- /libraries/core/src/main/java/com/smarttoolfactory/core/CoreDependency.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.core 2 | 3 | /** 4 | * MOCK DEPENDENCY for instructing how to inject it to a feature module from **core module**, delete it for production 5 | */ 6 | class CoreDependency 7 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/db/dao/SampleDao.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.db.dao 2 | 3 | import androidx.room.Dao 4 | import com.smarttoolfactory.data.model.local.SampleDataEntity 5 | 6 | @Dao 7 | interface SampleDao : BaseDao 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Aug 02 11:05:47 TRT 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Project Boilerplate Code 3 | Dynamic Feature 4 | Feature 5 | Photos 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/* 5 | /.idea/caches 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | /.idea/navEditor.xml 10 | /.idea/assetWizardSettings.xml 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | .cxx 16 | local.properties 17 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/smarttoolfactory/projectboilerplate/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.projectboilerplate.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple200 = Color(0xFFBB86FC) 6 | val Purple500 = Color(0xFF6200EE) 7 | val Purple700 = Color(0xFF3700B3) 8 | val Teal200 = Color(0xFF03DAC5) 9 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/model/local/SampleDataEntity.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.model.local 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | import com.smarttoolfactory.data.model.IEntity 6 | 7 | @Entity 8 | data class SampleDataEntity(@PrimaryKey val id: Int) : IEntity 9 | -------------------------------------------------------------------------------- /libraries/domain/src/main/java/com/smarttoolfactory/domain/base/Disposable.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.domain.base 2 | 3 | /** 4 | * Interface for adding common behavior for any class that has items that need to be disposed 5 | * such as RxJava Observables or Coroutines jobs. 6 | */ 7 | interface Disposable { 8 | 9 | fun dispose() 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/smarttoolfactory/projectboilerplate/MyApplication.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.projectboilerplate 2 | 3 | import android.app.Application 4 | import dagger.hilt.android.HiltAndroidApp 5 | 6 | @HiltAndroidApp 7 | class MyApplication : Application() { 8 | 9 | override fun onCreate() { 10 | super.onCreate() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/constant/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.constant 2 | 3 | /* 4 | Web Service Constants 5 | */ 6 | 7 | // Base Url 8 | const val BASE_URL = "http://www.example.com/" 9 | 10 | /* 11 | DBConstants 12 | */ 13 | const val DATABASE_NAME = "sample.db" 14 | const val DATABASE_VERSION = 1 15 | -------------------------------------------------------------------------------- /libraries/core/src/main/java/com/smarttoolfactory/core/util/LifecycleOwnerExtension.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.core.util 2 | 3 | import androidx.lifecycle.LifecycleOwner 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.Observer 6 | 7 | fun LifecycleOwner.observe(liveData: LiveData, predicate: (T) -> Unit) { 8 | liveData.observe(this, Observer { it?.let { predicate(it) } }) 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/smarttoolfactory/projectboilerplate/ui/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.projectboilerplate.ui.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val Shapes = Shapes( 8 | small = RoundedCornerShape(4.dp), 9 | medium = RoundedCornerShape(4.dp), 10 | large = RoundedCornerShape(0.dp) 11 | ) 12 | -------------------------------------------------------------------------------- /libraries/core/src/main/java/com/smarttoolfactory/core/viewmodel/NavControllerViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.core.viewmodel 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.navigation.NavController 6 | import com.smarttoolfactory.core.util.Event 7 | 8 | class NavControllerViewModel : ViewModel() { 9 | val currentNavController = MutableLiveData>() 10 | } 11 | -------------------------------------------------------------------------------- /libraries/core/src/test/java/com/smarttoolfactory/core/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.core 2 | 3 | import junit.framework.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/test/java/com/smarttoolfactory/projectboilerplate/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.projectboilerplate 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /features/feature/src/test/java/com/smarttoolfactory/feature/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.feature 2 | 3 | import junit.framework.TestCase.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /libraries/test-utils/src/test/java/com/smarttoolfactory/test_utils/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.test_utils 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/smarttoolfactory/projectboilerplate/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.projectboilerplate 2 | 3 | import androidx.lifecycle.ViewModel 4 | import com.smarttoolfactory.domain.usecase.UseCase 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import javax.inject.Inject 7 | import kotlinx.coroutines.CoroutineScope 8 | 9 | @HiltViewModel 10 | class MainViewModel @Inject constructor( 11 | private val coroutineScope: CoroutineScope, 12 | private val useCase: UseCase 13 | ) : ViewModel() 14 | -------------------------------------------------------------------------------- /features/feature/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /libraries/core/src/main/java/com/smarttoolfactory/core/viewstate/ViewState.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.core.viewstate 2 | 3 | class ViewState( 4 | val status: Status, 5 | val data: T? = null, 6 | val error: Throwable? = null 7 | ) { 8 | 9 | fun isSuccess() = status == Status.SUCCESS 10 | 11 | fun isLoading() = status == Status.LOADING 12 | 13 | fun getErrorMessage() = error?.message 14 | 15 | fun shouldShowErrorMessage() = error != null && status == Status.ERROR 16 | } 17 | 18 | enum class Status { 19 | LOADING, 20 | SUCCESS, 21 | ERROR 22 | } 23 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/source/RemoteDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.source 2 | 3 | import com.smarttoolfactory.data.api.SampleApi 4 | import com.smarttoolfactory.data.model.remote.SampleData 5 | import javax.inject.Inject 6 | 7 | interface RemoteDataSource { 8 | suspend fun getSampleDataList(): List 9 | } 10 | 11 | class RemoteDataSourceImpl @Inject constructor(private val api: SampleApi) : RemoteDataSource { 12 | override suspend fun getSampleDataList(): List { 13 | TODO("Not yet implemented") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /scripts/git-hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | echo "Running static analysis..." 5 | 6 | 7 | # Inspect code using KtLint, and Detekt 8 | 9 | # Run KtLint only 10 | #./gradlew app:ktlintCheck --daemon 11 | 12 | # Format code using KtLint, then run Detekt and KtLint static analysis 13 | ./gradlew app:ktlintFormat app:detekt app:ktlintCheck --daemon 14 | 15 | status=$? 16 | 17 | 18 | if [ "$status" = 0 ] ; then 19 | 20 | echo "Static analysis found no problems." 21 | 22 | exit 0 23 | 24 | else 25 | 26 | echo 1>&2 "Static analysis found violations it could not fix." 27 | 28 | exit 1 29 | 30 | fi 31 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/source/LocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.source 2 | 3 | import com.smarttoolfactory.data.db.dao.SampleDao 4 | import com.smarttoolfactory.data.model.local.SampleDataEntity 5 | import javax.inject.Inject 6 | 7 | interface LocalDataSource { 8 | suspend fun getSampleEntities(): List 9 | } 10 | 11 | class LocalDataSourceImpl @Inject constructor(private val sampleDao: SampleDao) : LocalDataSource { 12 | 13 | override suspend fun getSampleEntities(): List { 14 | TODO("Not yet implemented") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/db/SampleDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.db 2 | 3 | import androidx.room.Database 4 | import androidx.room.RoomDatabase 5 | import com.smarttoolfactory.data.constant.DATABASE_VERSION 6 | import com.smarttoolfactory.data.db.dao.SampleDao 7 | import com.smarttoolfactory.data.model.local.SampleDataEntity 8 | 9 | @Database( 10 | entities = [ 11 | SampleDataEntity::class, 12 | ], 13 | version = DATABASE_VERSION, 14 | exportSchema = true 15 | ) 16 | abstract class SampleDatabase : RoomDatabase() { 17 | abstract fun sampleDao(): SampleDao 18 | } 19 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/model/Mappables.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.model 2 | 3 | /** 4 | * Interface to create common behavior between entities to be able to use generics to create [Mapper] 5 | */ 6 | interface IEntity : Mappable 7 | 8 | /** 9 | * Interface to create common behavior between DTOs to be able to use generics to create [Mapper] 10 | */ 11 | interface DataTransferObject : Mappable 12 | 13 | /** 14 | * Marker interface to mark classes that implement, or interfaces that extend this interface 15 | * as convertible from one [Mappable] type to another 16 | */ 17 | interface Mappable 18 | -------------------------------------------------------------------------------- /libraries/core/src/main/java/com/smarttoolfactory/core/util/NavHostExtension.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.core.util 2 | 3 | import androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment 4 | import androidx.navigation.fragment.NavHostFragment 5 | import com.smarttoolfactory.core.ui.fragment.navhost.FieldProperty 6 | import com.smarttoolfactory.core.viewmodel.NavControllerViewModel 7 | 8 | var DynamicNavHostFragment.viewModel: NavControllerViewModel by FieldProperty { 9 | NavControllerViewModel() 10 | } 11 | 12 | var NavHostFragment.viewModel: NavControllerViewModel by FieldProperty { 13 | NavControllerViewModel() 14 | } 15 | -------------------------------------------------------------------------------- /libraries/domain/src/main/java/com/smarttoolfactory/domain/dispatcher/UseCaseDispatchers.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.domain.dispatcher 2 | 3 | import kotlinx.coroutines.CoroutineDispatcher 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | /** 7 | * Class for providing dispatcher for different thread operations. This class is useful 8 | * in tests to control every thread in one place. 9 | */ 10 | 11 | data class UseCaseDispatchers( 12 | val ioDispatcher: CoroutineDispatcher = Dispatchers.IO, 13 | val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default, 14 | val mainDispatcher: CoroutineDispatcher = Dispatchers.Main 15 | ) 16 | -------------------------------------------------------------------------------- /features/feature/src/main/res/navigation/nav_graph_feature.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 13 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/repository/Repository.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.repository 2 | 3 | import com.smarttoolfactory.data.model.remote.SampleData 4 | import com.smarttoolfactory.data.source.LocalDataSource 5 | import com.smarttoolfactory.data.source.RemoteDataSource 6 | import javax.inject.Inject 7 | 8 | interface Repository { 9 | 10 | suspend fun geSampleDataFromRemote(): List 11 | } 12 | 13 | class RepositoryImpl @Inject constructor( 14 | private val remoteDataSource: RemoteDataSource, 15 | private val localDataSource: LocalDataSource 16 | ) : Repository { 17 | 18 | override suspend fun geSampleDataFromRemote(): List { 19 | TODO("Not yet implemented") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libraries/core/src/main/java/com/smarttoolfactory/core/di/CoreModuleDependencies.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.core.di 2 | 3 | import com.smarttoolfactory.core.CoreDependency 4 | import com.smarttoolfactory.domain.usecase.UseCase 5 | import dagger.hilt.EntryPoint 6 | import dagger.hilt.InstallIn 7 | import dagger.hilt.components.SingletonComponent 8 | 9 | /** 10 | * This component is required for adding dependencies to Dynamic Feature Modules by 11 | * adding [CoreModule] as dependent component 12 | */ 13 | @EntryPoint 14 | @InstallIn(SingletonComponent::class) 15 | interface CoreModuleDependencies { 16 | 17 | /* 18 | Provision methods to provide dependencies to components that depend on this component 19 | */ 20 | fun useCase(): UseCase 21 | fun coreDependency(): CoreDependency 22 | } 23 | -------------------------------------------------------------------------------- /libraries/domain/src/main/java/com/smarttoolfactory/domain/usecase/UseCase.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.domain.usecase 2 | 3 | import com.smarttoolfactory.data.model.remote.SampleData 4 | import com.smarttoolfactory.data.repository.Repository 5 | import com.smarttoolfactory.domain.dispatcher.UseCaseDispatchers 6 | import javax.inject.Inject 7 | import kotlinx.coroutines.flow.Flow 8 | import kotlinx.coroutines.flow.flow 9 | import kotlinx.coroutines.flow.flowOn 10 | 11 | class UseCase @Inject constructor( 12 | private val repository: Repository, 13 | private val useCaseDispatchers: UseCaseDispatchers 14 | ) { 15 | 16 | fun getListFlow(): Flow> { 17 | return flow { emit(repository.geSampleDataFromRemote()) } 18 | .flowOn(useCaseDispatchers.defaultDispatcher) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /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.kts. 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 -------------------------------------------------------------------------------- /libraries/core/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.kts. 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 -------------------------------------------------------------------------------- /libraries/data/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.kts. 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 -------------------------------------------------------------------------------- /libraries/core/src/androidTest/java/com/smarttoolfactory/core/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.core 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("com.smarttoolfactory.core.test", appContext.packageName) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /libraries/domain/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.kts. 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 -------------------------------------------------------------------------------- /libraries/test-utils/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.kts. 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 -------------------------------------------------------------------------------- /features/feature/src/androidTest/java/com/smarttoolfactory/feature/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.feature 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import junit.framework.TestCase.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("com.smarttoolfactory.feature", appContext.packageName) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/smarttoolfactory/projectboilerplate/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.projectboilerplate 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("com.smarttoolfactory.projectboilerplate", appContext.packageName) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /features/feature/src/main/java/com/smarttoolfactory/feature/di/FeatureComponent.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.feature.di 2 | 3 | import android.app.Application 4 | import androidx.fragment.app.Fragment 5 | import com.smarttoolfactory.core.di.CoreModuleDependencies 6 | import com.smarttoolfactory.feature.FeatureFragment 7 | import dagger.BindsInstance 8 | import dagger.Component 9 | 10 | @Component( 11 | dependencies = [CoreModuleDependencies::class], 12 | modules = [FeatureModule::class] 13 | ) 14 | interface FeatureComponent { 15 | 16 | fun inject(fragment: FeatureFragment) 17 | 18 | @Component.Factory 19 | interface Factory { 20 | fun create( 21 | coreModuleDependencies: CoreModuleDependencies, 22 | @BindsInstance fragment: Fragment, 23 | @BindsInstance application: Application 24 | ): FeatureComponent 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /libraries/test-utils/src/androidTest/java/com/smarttoolfactory/test_utils/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | // package com.smarttoolfactory.test_utils 2 | // 3 | // import androidx.test.ext.junit.runners.AndroidJUnit4 4 | // import androidx.test.platform.app.InstrumentationRegistry 5 | // import org.junit.Test 6 | // import org.junit.runner.RunWith 7 | // 8 | // /** 9 | // * Instrumented test, which will execute on an Android device. 10 | // * 11 | // * See [testing documentation](http://d.android.com/tools/testing). 12 | // */ 13 | // @RunWith(AndroidJUnit4::class) 14 | // class ExampleInstrumentedTest { 15 | // @Test 16 | // fun useAppContext() { 17 | // // Context of the app under test. 18 | // val appContext = InstrumentationRegistry.getInstrumentation().targetContext 19 | // assertEquals("com.smarttoolfactory.test_shared.test", appContext.packageName) 20 | // } 21 | // } 22 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /libraries/core/src/main/java/com/smarttoolfactory/core/util/FlowViewStateExtension.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.core.util 2 | 3 | import com.smarttoolfactory.core.viewstate.Status 4 | import com.smarttoolfactory.core.viewstate.ViewState 5 | import kotlinx.coroutines.CoroutineDispatcher 6 | import kotlinx.coroutines.Dispatchers 7 | import kotlinx.coroutines.flow.Flow 8 | import kotlinx.coroutines.flow.catch 9 | import kotlinx.coroutines.flow.emitAll 10 | import kotlinx.coroutines.flow.flowOf 11 | import kotlinx.coroutines.flow.flowOn 12 | import kotlinx.coroutines.flow.map 13 | 14 | fun Flow.convertToFlowViewState( 15 | dispatcher: CoroutineDispatcher = Dispatchers.Default 16 | ): Flow> { 17 | return this 18 | .map { list -> ViewState(status = Status.SUCCESS, data = list) } 19 | .catch { cause: Throwable -> emitAll(flowOf(ViewState(Status.ERROR, error = cause))) } 20 | .flowOn(dispatcher) 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/smarttoolfactory/projectboilerplate/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.projectboilerplate.ui.theme 2 | 3 | import androidx.compose.material.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | body1 = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp 15 | ) 16 | /* Other default text styles to override 17 | button = TextStyle( 18 | fontFamily = FontFamily.Default, 19 | fontWeight = FontWeight.W500, 20 | fontSize = 14.sp 21 | ), 22 | caption = TextStyle( 23 | fontFamily = FontFamily.Default, 24 | fontWeight = FontWeight.Normal, 25 | fontSize = 12.sp 26 | ) 27 | */ 28 | ) 29 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/mapper/Mappers.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.mapper 2 | 3 | import com.smarttoolfactory.data.model.IEntity 4 | import com.smarttoolfactory.data.model.local.SampleDataEntity 5 | import com.smarttoolfactory.data.model.remote.SampleData 6 | import javax.inject.Inject 7 | 8 | /** 9 | * Mapper for transforming objects between REST and database or REST/db and domain 10 | * as [IEntity] which are Non-nullable to Non-nullable 11 | */ 12 | interface Mapper { 13 | fun map(input: I): O 14 | } 15 | 16 | /** 17 | * Mapper for transforming objects between REST and database or REST/db and domain 18 | * as [List] of [IEntity] which are Non-nullable to Non-nullable 19 | */ 20 | interface ListMapper : Mapper, List> 21 | 22 | class SampleDataMapper @Inject constructor() : Mapper { 23 | override fun map(input: SampleData): SampleDataEntity { 24 | return SampleDataEntity(input.id) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/db/dao/BaseDao.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.db.dao 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Delete 5 | import androidx.room.Insert 6 | import androidx.room.OnConflictStrategy 7 | import androidx.room.Transaction 8 | import androidx.room.Update 9 | import com.smarttoolfactory.data.model.IEntity 10 | 11 | @Dao 12 | interface BaseDao { 13 | 14 | /* 15 | Insert Single entity 16 | */ 17 | @Insert(onConflict = OnConflictStrategy.REPLACE) 18 | suspend fun insert(entity: T): Long 19 | 20 | /* 21 | Insert Multiple entities 22 | */ 23 | @Transaction 24 | @Insert(onConflict = OnConflictStrategy.REPLACE) 25 | suspend fun insert(entities: List): List 26 | 27 | /* 28 | Update 29 | */ 30 | @Update 31 | suspend fun update(entity: T): Int 32 | 33 | /* 34 | Delete 35 | */ 36 | @Delete 37 | suspend fun delete(entity: T): Int 38 | } 39 | -------------------------------------------------------------------------------- /libraries/test-utils/src/main/java/com/smarttoolfactory/test_utils/rule/MockWebServerRule.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.test_utils.rule 2 | 3 | import okhttp3.mockwebserver.MockWebServer 4 | import org.junit.rules.TestRule 5 | import org.junit.runner.Description 6 | import org.junit.runners.model.Statement 7 | 8 | /** 9 | * Test rule for JUnit4 to invoke actions which are 10 | * start [MockWebServer], 11 | * run the test , 12 | * and shut [MockWebServer] down after the test is run. 13 | */ 14 | class MockWebServerRule : TestRule { 15 | 16 | val mockWebServer = MockWebServer() 17 | 18 | override fun apply( 19 | base: Statement, 20 | description: Description 21 | ): Statement { 22 | 23 | return object : Statement() { 24 | 25 | @Throws(Throwable::class) 26 | override fun evaluate() { 27 | mockWebServer.start() 28 | base.evaluate() 29 | mockWebServer.shutdown() 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/res/navigation/nav_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 15 | 20 | 21 | 25 | 26 | -------------------------------------------------------------------------------- /libraries/data/src/main/java/com/smarttoolfactory/data/di/DatabaseModule.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.data.di 2 | 3 | import android.app.Application 4 | import androidx.room.Room 5 | import com.smarttoolfactory.data.constant.DATABASE_NAME 6 | import com.smarttoolfactory.data.db.SampleDatabase 7 | import com.smarttoolfactory.data.db.dao.SampleDao 8 | import dagger.Module 9 | import dagger.Provides 10 | import dagger.hilt.InstallIn 11 | import dagger.hilt.components.SingletonComponent 12 | import javax.inject.Singleton 13 | 14 | @InstallIn(SingletonComponent::class) 15 | @Module 16 | class DatabaseModule { 17 | 18 | @Singleton 19 | @Provides 20 | fun provideDatabase(application: Application): SampleDatabase { 21 | return Room.databaseBuilder( 22 | application, 23 | SampleDatabase::class.java, 24 | DATABASE_NAME 25 | ).build() 26 | } 27 | 28 | @Singleton 29 | @Provides 30 | fun provideSessionTokenDao(appDatabase: SampleDatabase): SampleDao = 31 | appDatabase.sampleDao() 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 21 | 22 | -------------------------------------------------------------------------------- /libraries/core/src/main/java/com/smarttoolfactory/core/di/CoreModule.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.core.di 2 | 3 | import com.smarttoolfactory.core.CoreDependency 4 | import com.smarttoolfactory.domain.dispatcher.UseCaseDispatchers 5 | import dagger.Module 6 | import dagger.Provides 7 | import dagger.hilt.InstallIn 8 | import dagger.hilt.components.SingletonComponent 9 | import javax.inject.Singleton 10 | import kotlinx.coroutines.CoroutineScope 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.SupervisorJob 13 | 14 | @InstallIn(SingletonComponent::class) 15 | @Module(includes = [DataModule::class]) 16 | class CoreModule { 17 | 18 | @Singleton 19 | @Provides 20 | fun provideCoreDependency() = CoreDependency() 21 | 22 | @Singleton 23 | @Provides 24 | fun provideCoroutineScope() = CoroutineScope(Dispatchers.Main.immediate + SupervisorJob()) 25 | 26 | @Singleton 27 | @Provides 28 | fun provideUseCaseDispatchers(): UseCaseDispatchers { 29 | return UseCaseDispatchers(Dispatchers.IO, Dispatchers.Default, Dispatchers.Main) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /libraries/data/schemas/com.smarttoolfactory.data.db.SampleDatabase/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 1, 5 | "identityHash": "7d3b882d544656a1b1c9bf27028f57a4", 6 | "entities": [ 7 | { 8 | "tableName": "SampleDataEntity", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, PRIMARY KEY(`id`))", 10 | "fields": [ 11 | { 12 | "fieldPath": "id", 13 | "columnName": "id", 14 | "affinity": "INTEGER", 15 | "notNull": true 16 | } 17 | ], 18 | "primaryKey": { 19 | "columnNames": [ 20 | "id" 21 | ], 22 | "autoGenerate": false 23 | }, 24 | "indices": [], 25 | "foreignKeys": [] 26 | } 27 | ], 28 | "views": [], 29 | "setupQueries": [ 30 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 31 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7d3b882d544656a1b1c9bf27028f57a4')" 32 | ] 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /libraries/core/src/main/java/com/smarttoolfactory/core/ui/fragment/BaseViewBindingFragment.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.smarttoolfactory.core.ui.fragment 3 | 4 | import androidx.fragment.app.Fragment 5 | import androidx.viewbinding.ViewBinding 6 | 7 | /** 8 | * BaseFragment to avoid writing data-binding code over again for each fragment. 9 | * 10 | * Generic approach forces Fragments to have specified number of ViewModels if added as generic parameter 11 | * 12 | * LifeCycle of Fragments 13 | * 14 | * * onAttach() 15 | * * onCreate() 16 | * * onCreateView() -> View is created or Fragment returned from back stack 17 | * * onViewCreated() 18 | * * onStart() 19 | * * onResume() 20 | * * onPause() 21 | * * onStop() 22 | * * onDestroyView() fragment sent to back stack / Back navigation -> onCreateView() is called 23 | * * onDestroy() 24 | * * onDetach() 25 | */ 26 | abstract class BaseViewBindingFragment : Fragment() { 27 | 28 | /** 29 | * Generic nullable [ViewBinding] that is set to null in [BaseFragment.onDestroyView] 30 | */ 31 | internal var binding: ViewBinding? = null 32 | 33 | override fun onDestroyView() { 34 | binding = null 35 | super.onDestroyView() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /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 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 21 | 22 |