├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── themes.xml
│ │ │ │ └── colors.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── xml
│ │ │ │ ├── backup_rules.xml
│ │ │ │ └── data_extraction_rules.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── iamkamrul
│ │ │ │ └── modularization
│ │ │ │ ├── JetpackApplication.kt
│ │ │ │ ├── di
│ │ │ │ └── RepositoryModule.kt
│ │ │ │ ├── navigation
│ │ │ │ └── AppNavigation.kt
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ ├── release
│ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── modularization
│ │ │ └── BaseUrlModule.kt
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── modularization
│ │ │ └── ExampleUnitTest.kt
│ ├── debug
│ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── modularization
│ │ │ └── BaseUrlModule.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── iamkamrul
│ │ └── modularization
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle.kts
├── common
├── .gitignore
├── consumer-rules.pro
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── common
│ │ │ └── utils
│ │ │ └── NavRoute.kt
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── common
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── iamkamrul
│ │ └── common
│ │ └── ExampleInstrumentedTest.kt
├── build.gradle.kts
└── proguard-rules.pro
├── core
├── data
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── iamkamrul
│ │ │ │ └── data
│ │ │ │ ├── apiservice
│ │ │ │ └── ApiService.kt
│ │ │ │ ├── module
│ │ │ │ └── ApiServiceModule.kt
│ │ │ │ ├── utils
│ │ │ │ ├── BaseMapper.kt
│ │ │ │ └── NetworkBoundResource.kt
│ │ │ │ ├── mapper
│ │ │ │ ├── ProfileMapper.kt
│ │ │ │ └── RepoListItemMapper.kt
│ │ │ │ └── repoimpl
│ │ │ │ └── GithubRepoImpl.kt
│ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── data
│ │ │ └── ExampleUnitTest.kt
│ ├── build.gradle.kts
│ └── proguard-rules.pro
├── di
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── iamkamrul
│ │ │ │ └── di
│ │ │ │ ├── qualifier
│ │ │ │ └── AppBaseUrl.kt
│ │ │ │ └── module
│ │ │ │ ├── RetrofitModule.kt
│ │ │ │ └── OkHttpModule.kt
│ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── di
│ │ │ └── ExampleUnitTest.kt
│ ├── build.gradle.kts
│ └── proguard-rules.pro
├── domain
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── iamkamrul
│ │ │ │ └── domain
│ │ │ │ ├── utils
│ │ │ │ ├── Result.kt
│ │ │ │ └── BaseUseCase.kt
│ │ │ │ ├── repository
│ │ │ │ └── GithubRepository.kt
│ │ │ │ └── usecase
│ │ │ │ ├── ProfileUseCase.kt
│ │ │ │ └── RepoListUseCase.kt
│ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── domain
│ │ │ └── ExampleUnitTest.kt
│ ├── build.gradle.kts
│ └── proguard-rules.pro
├── ui
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── iamkamrul
│ │ │ │ └── ui
│ │ │ │ └── component
│ │ │ │ └── ErrorMessageCompose.kt
│ │ ├── test
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── iamkamrul
│ │ │ │ └── ui
│ │ │ │ └── ExampleUnitTest.kt
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── ui
│ │ │ └── ExampleInstrumentedTest.kt
│ ├── proguard-rules.pro
│ └── build.gradle.kts
└── designsystem
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── res
│ │ │ └── font
│ │ │ │ ├── roboto_bold.ttf
│ │ │ │ ├── roboto_black.ttf
│ │ │ │ ├── roboto_italic.ttf
│ │ │ │ ├── roboto_light.ttf
│ │ │ │ ├── roboto_medium.ttf
│ │ │ │ ├── roboto_regular.ttf
│ │ │ │ ├── roboto_bold_italic.ttf
│ │ │ │ └── roboto_extra_light.ttf
│ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── designsystem
│ │ │ ├── theme
│ │ │ ├── Shape.kt
│ │ │ ├── AppColorScheme.kt
│ │ │ ├── Color.kt
│ │ │ ├── Type.kt
│ │ │ └── Theme.kt
│ │ │ └── component
│ │ │ └── ScaffoldTopAppbar.kt
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── designsystem
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── iamkamrul
│ │ └── designsystem
│ │ └── ExampleInstrumentedTest.kt
│ ├── proguard-rules.pro
│ └── build.gradle.kts
├── features
├── profile
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── iamkamrul
│ │ │ │ └── profile
│ │ │ │ ├── ProfileNavigation.kt
│ │ │ │ ├── ProfileViewModel.kt
│ │ │ │ └── ProfileScreen.kt
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── profile
│ │ │ └── ExampleInstrumentedTest.kt
│ ├── build.gradle.kts
│ └── proguard-rules.pro
└── repolist
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── iamkamrul
│ │ │ └── repolist
│ │ │ ├── RepoListNavigation.kt
│ │ │ ├── RepoListViewModel.kt
│ │ │ └── RepoListScreen.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── iamkamrul
│ │ └── repolist
│ │ └── ExampleInstrumentedTest.kt
│ ├── build.gradle.kts
│ └── proguard-rules.pro
├── model
├── entity
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── iamkamrul
│ │ └── entity
│ │ ├── ProfileEntity.kt
│ │ └── RepoItemEntity.kt
└── apiresponse
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── java
│ └── com
│ └── iamkamrul
│ └── apiresponse
│ ├── ProfileApiResponse.kt
│ └── RepoItemApiResponse.kt
├── .idea
├── .name
├── vcs.xml
├── kotlinc.xml
├── misc.xml
└── inspectionProfiles
│ └── Project_Default.xml
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── README.md
├── settings.gradle.kts
├── gradle.properties
├── .gitignore
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/common/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/common/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/data/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/data/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/di/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/di/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/domain/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/ui/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/ui/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/domain/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/features/profile/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/model/entity/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/designsystem/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/designsystem/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/features/profile/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/features/repolist/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/features/repolist/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/model/apiresponse/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | JetPackComposeModularization
--------------------------------------------------------------------------------
/core/domain/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | JetPackComposeModularization
3 |
--------------------------------------------------------------------------------
/common/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/core/data/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/core/di/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/core/ui/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/features/profile/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/core/designsystem/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/features/repolist/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/model/entity/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
2 | plugins {
3 | alias(libs.plugins.iamkamrul.jvm.library)
4 | }
5 | true
--------------------------------------------------------------------------------
/model/apiresponse/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
2 | plugins {
3 | alias(libs.plugins.iamkamrul.jvm.library)
4 | }
5 | true
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/core/designsystem/src/main/res/font/roboto_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/core/designsystem/src/main/res/font/roboto_bold.ttf
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/core/designsystem/src/main/res/font/roboto_black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/core/designsystem/src/main/res/font/roboto_black.ttf
--------------------------------------------------------------------------------
/core/designsystem/src/main/res/font/roboto_italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/core/designsystem/src/main/res/font/roboto_italic.ttf
--------------------------------------------------------------------------------
/core/designsystem/src/main/res/font/roboto_light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/core/designsystem/src/main/res/font/roboto_light.ttf
--------------------------------------------------------------------------------
/core/designsystem/src/main/res/font/roboto_medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/core/designsystem/src/main/res/font/roboto_medium.ttf
--------------------------------------------------------------------------------
/core/designsystem/src/main/res/font/roboto_regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/core/designsystem/src/main/res/font/roboto_regular.ttf
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/core/designsystem/src/main/res/font/roboto_bold_italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/core/designsystem/src/main/res/font/roboto_bold_italic.ttf
--------------------------------------------------------------------------------
/core/designsystem/src/main/res/font/roboto_extra_light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kamrul3288/JetPackCompose-Modularization-MVVM-Clean-Architecture/HEAD/core/designsystem/src/main/res/font/roboto_extra_light.ttf
--------------------------------------------------------------------------------
/common/src/main/java/com/iamkamrul/common/utils/NavRoute.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.common.utils
2 |
3 | object NavRoute {
4 | const val repoListScreen = "repoListScreen"
5 | const val profileScreen = "profileScreen"
6 | }
--------------------------------------------------------------------------------
/core/di/src/main/java/com/iamkamrul/di/qualifier/AppBaseUrl.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.di.qualifier
2 |
3 | import javax.inject.Qualifier
4 |
5 | @Qualifier
6 | @Retention(AnnotationRetention.RUNTIME)
7 | annotation class AppBaseUrl
--------------------------------------------------------------------------------
/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
2 | plugins {
3 | alias(libs.plugins.iamkamrul.android.library)
4 | }
5 | android {
6 | namespace = "com.iamkamrul.common"
7 | }
8 | dependencies {
9 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Sep 23 16:27:12 BDT 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/core/domain/src/main/java/com/iamkamrul/domain/utils/Result.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.domain.utils
2 |
3 |
4 | sealed interface Result {
5 | data object Loading:Result
6 | data class Success(val data: T) : Result
7 | data class Error(val message: String,val code:Int) : Result
8 | }
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JetPackCompose-Modularization-MVVM-Clean-Architecture
2 | A clean architecture approach (multi-module) using Jetpack compose, Kotlin, Navigation, MVVM, Hilt, Kotlin Coroutines, Flow and Retrofit.
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/features/profile/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
2 | plugins {
3 | alias(libs.plugins.iamkamrul.android.feature.compose)
4 | }
5 | android {
6 | namespace = "com.iamkamrul.profile"
7 | }
8 |
9 | dependencies{
10 | implementation(libs.image.coil.compose)
11 | }
--------------------------------------------------------------------------------
/features/repolist/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
2 | plugins {
3 | alias(libs.plugins.iamkamrul.android.feature.compose)
4 | }
5 |
6 | android {
7 | namespace = "com.iamkamrul.repolist"
8 | }
9 | dependencies{
10 | implementation(libs.image.coil.compose)
11 | }
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/core/data/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION")
2 | plugins {
3 | alias(libs.plugins.iamkamrul.android.library)
4 | alias(libs.plugins.iamkamrul.android.hilt)
5 | }
6 | android {
7 | namespace = "com.iamkamrul.data"
8 |
9 | }
10 |
11 | dependencies {
12 | implementation(projects.core.domain)
13 | implementation(projects.core.di)
14 | api(projects.model.apiresponse)
15 | }
--------------------------------------------------------------------------------
/core/domain/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION")
2 | plugins {
3 | alias(libs.plugins.iamkamrul.android.library)
4 | alias(libs.plugins.iamkamrul.android.hilt)
5 | }
6 |
7 | android {
8 | namespace = "com.iamkamrul.domain"
9 | }
10 |
11 | dependencies {
12 | api(projects.model.entity)
13 | implementation(libs.androidx.corektx)
14 | implementation(libs.kotlinx.coroutines.android)
15 | }
--------------------------------------------------------------------------------
/core/domain/src/main/java/com/iamkamrul/domain/utils/BaseUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.domain.utils
2 |
3 | import kotlinx.coroutines.flow.Flow
4 |
5 | interface UseCase
6 |
7 | interface ApiUseCaseParams : UseCase {
8 | suspend fun execute(params: Params): Flow>
9 | }
10 |
11 | interface ApiUseCaseNonParams : UseCase {
12 | suspend fun execute(): Flow>
13 | }
14 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/core/designsystem/src/main/java/com/iamkamrul/designsystem/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.designsystem.theme
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material3.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 | )
--------------------------------------------------------------------------------
/core/di/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
2 | plugins {
3 | alias(libs.plugins.iamkamrul.android.library)
4 | alias(libs.plugins.iamkamrul.android.hilt)
5 | alias(libs.plugins.iamkamrul.android.retrofit)
6 | }
7 | android {
8 | namespace = "com.iamkamrul.di"
9 | }
10 | dependencies {
11 | api(libs.log.timber)
12 | api(libs.bundles.network)
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/core/di/src/test/java/com/iamkamrul/di/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.di
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/core/ui/src/test/java/com/iamkamrul/ui/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.ui
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/common/src/test/java/com/iamkamrul/common/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.common
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/core/data/src/test/java/com/iamkamrul/data/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.data
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/iamkamrul/modularization/JetpackApplication.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.modularization
2 |
3 | import android.app.Application
4 | import dagger.hilt.android.HiltAndroidApp
5 | import timber.log.Timber
6 |
7 | @HiltAndroidApp
8 | class JetpackApplication : Application(){
9 | override fun onCreate() {
10 | super.onCreate()
11 | if (BuildConfig.DEBUG){
12 | Timber.plant(Timber.DebugTree())
13 | }
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/app/src/release/java/com/iamkamrul/modularization/BaseUrlModule.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.modularization
2 |
3 | import com.iamkamrul.di.qualifier.AppBaseUrl
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.hilt.InstallIn
7 | import dagger.hilt.components.SingletonComponent
8 |
9 | @Module
10 | @InstallIn(SingletonComponent::class)
11 | class BaseUrlModule{
12 | @Provides
13 | @AppBaseUrl
14 | fun provideBaseUrl():String = ""
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/core/domain/src/test/java/com/iamkamrul/domain/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.domain
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/iamkamrul/modularization/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.modularization
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/core/designsystem/src/test/java/com/iamkamrul/designsystem/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.designsystem
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/debug/java/com/iamkamrul/modularization/BaseUrlModule.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.modularization
2 |
3 | import com.iamkamrul.di.qualifier.AppBaseUrl
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.hilt.InstallIn
7 | import dagger.hilt.components.SingletonComponent
8 |
9 | @Module
10 | @InstallIn(SingletonComponent::class)
11 | class BaseUrlModule{
12 | @Provides
13 | @AppBaseUrl
14 | fun provideBaseUrl():String = "https://api.github.com/"
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/features/repolist/src/main/java/com/iamkamrul/repolist/RepoListNavigation.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.repolist
2 |
3 | import androidx.navigation.NavGraphBuilder
4 | import androidx.navigation.compose.composable
5 |
6 | const val repoListScreenRoute = "repoListScreenRoute"
7 |
8 | fun NavGraphBuilder.repoListScreen(
9 | onRepoItemClick:()->Unit
10 | ){
11 | composable(route = repoListScreenRoute){
12 | RepoListRoute(
13 | onRepoItemClick = onRepoItemClick
14 | )
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/iamkamrul/modularization/di/RepositoryModule.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.modularization.di
2 |
3 | import com.iamkamrul.data.repoimpl.GithubRepoImpl
4 | import com.iamkamrul.domain.repository.GithubRepository
5 | import dagger.Binds
6 | import dagger.Module
7 | import dagger.hilt.InstallIn
8 | import dagger.hilt.components.SingletonComponent
9 |
10 | @Module
11 | @InstallIn(SingletonComponent::class)
12 | interface RepositoryModule{
13 |
14 | @Binds
15 | fun bindGithubRepository(githubRepoImpl: GithubRepoImpl): GithubRepository
16 |
17 | }
--------------------------------------------------------------------------------
/core/domain/src/main/java/com/iamkamrul/domain/repository/GithubRepository.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.domain.repository
2 |
3 | import com.iamkamrul.domain.usecase.ProfileUseCase
4 | import com.iamkamrul.domain.usecase.RepoListUseCase
5 | import com.iamkamrul.entity.ProfileEntity
6 | import com.iamkamrul.entity.RepoItemEntity
7 | import kotlinx.coroutines.flow.Flow
8 | import com.iamkamrul.domain.utils.Result
9 |
10 |
11 | interface GithubRepository {
12 | suspend fun fetchRepoList(params: RepoListUseCase.Params): Flow>>
13 | suspend fun fetchProfile(params: ProfileUseCase.Params):Flow>
14 | }
--------------------------------------------------------------------------------
/features/profile/src/main/java/com/iamkamrul/profile/ProfileNavigation.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.profile
2 |
3 | import androidx.navigation.NavController
4 | import androidx.navigation.NavGraphBuilder
5 | import androidx.navigation.compose.composable
6 |
7 | const val profileScreenRoute = "profileScreenRoute"
8 |
9 | fun NavController.navigateToProfileScreen(){
10 | navigate(profileScreenRoute)
11 | }
12 |
13 | fun NavGraphBuilder.profileScreen(
14 | onBackBtnClick:()->Unit
15 | ){
16 | composable(route = profileScreenRoute){
17 | ProfileScreenRoute(
18 | onBackBtnClick = onBackBtnClick
19 | )
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/model/entity/src/main/java/com/iamkamrul/entity/ProfileEntity.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.entity
2 |
3 | data class ProfileEntity(
4 | val userAvatar: String,
5 | val userFullName: String,
6 | val userName: String,
7 | val about: String,
8 | val repoCount: Int,
9 | val followerCount: Int,
10 | val followingCount: Int,
11 | ) {
12 | constructor() : this(
13 | userAvatar = "https://www.google.com",
14 | userFullName = "Kamrul Hasan",
15 | userName = "kamrul3288",
16 | about = "Android Developer",
17 | repoCount = 24,
18 | followerCount = 24,
19 | followingCount = 24
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/core/data/src/main/java/com/iamkamrul/data/apiservice/ApiService.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.data.apiservice
2 |
3 | import com.iamkamrul.apiresponse.ProfileApiResponse
4 | import com.iamkamrul.apiresponse.RepoItemApiResponse
5 | import retrofit2.Response
6 | import retrofit2.http.GET
7 | import retrofit2.http.Path
8 |
9 | interface ApiService {
10 | @GET("/users/{username}/repos")
11 | suspend fun fetchRepoList(
12 | @Path("username")username:String
13 | ): Response>
14 |
15 | @GET("/users/{username}")
16 | suspend fun fetchProfile(
17 | @Path("username")username:String
18 | ):Response
19 | }
--------------------------------------------------------------------------------
/core/data/src/main/java/com/iamkamrul/data/module/ApiServiceModule.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.data.module
2 |
3 |
4 | import com.iamkamrul.data.apiservice.ApiService
5 | import com.iamkamrul.di.qualifier.AppBaseUrl
6 | import dagger.Module
7 | import dagger.Provides
8 | import dagger.hilt.InstallIn
9 | import dagger.hilt.components.SingletonComponent
10 | import retrofit2.Retrofit
11 | import javax.inject.Singleton
12 |
13 | @Module
14 | @InstallIn(SingletonComponent::class)
15 | object ApiServiceModule {
16 | @Provides
17 | @Singleton
18 | fun provideApiService(@AppBaseUrl retrofit: Retrofit): ApiService {
19 | return retrofit.create(ApiService::class.java)
20 | }
21 | }
--------------------------------------------------------------------------------
/core/data/src/main/java/com/iamkamrul/data/utils/BaseMapper.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.data.utils
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import kotlinx.coroutines.flow.map
5 | import com.iamkamrul.domain.utils.Result
6 |
7 | interface Mapper{
8 | fun mapFromApiResponse(type:R):E
9 | }
10 |
11 | fun mapFromApiResponse(result: Flow>, mapper: Mapper): Flow> {
12 | return result.map {
13 | when(it){
14 | is Result.Success-> Result.Success(mapper.mapFromApiResponse(it.data))
15 | is Result.Error->Result.Error(it.message,it.code)
16 | is Result.Loading -> Result.Loading
17 | }
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/core/domain/src/main/java/com/iamkamrul/domain/usecase/ProfileUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.domain.usecase
2 |
3 | import com.iamkamrul.domain.repository.GithubRepository
4 | import com.iamkamrul.domain.utils.ApiUseCaseParams
5 | import com.iamkamrul.domain.utils.Result
6 | import com.iamkamrul.entity.ProfileEntity
7 | import kotlinx.coroutines.flow.Flow
8 | import javax.inject.Inject
9 |
10 | class ProfileUseCase @Inject constructor(
11 | private val repository: GithubRepository
12 | ):ApiUseCaseParams{
13 | data class Params(val userName:String)
14 | override suspend fun execute(params: Params): Flow> {
15 | return repository.fetchProfile(params)
16 | }
17 | }
--------------------------------------------------------------------------------
/core/domain/src/main/java/com/iamkamrul/domain/usecase/RepoListUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.domain.usecase
2 |
3 | import com.iamkamrul.domain.repository.GithubRepository
4 | import com.iamkamrul.domain.utils.ApiUseCaseParams
5 | import com.iamkamrul.domain.utils.Result
6 | import com.iamkamrul.entity.RepoItemEntity
7 | import kotlinx.coroutines.flow.Flow
8 | import javax.inject.Inject
9 |
10 | class RepoListUseCase @Inject constructor(
11 | private val repository: GithubRepository
12 | ):ApiUseCaseParams>{
13 | override suspend fun execute(params: Params): Flow>> {
14 | return repository.fetchRepoList(params)
15 | }
16 | data class Params(val userName:String)
17 | }
--------------------------------------------------------------------------------
/model/entity/src/main/java/com/iamkamrul/entity/RepoItemEntity.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.entity
2 |
3 | data class RepoItemEntity(
4 | val userAvatarUrl:String,
5 | val userName:String,
6 | val repoName:String,
7 | val repoFullName:String,
8 | val repoDescription:String,
9 | val language:String,
10 | val forksCount:Int,
11 | val stargazers_count:Int,
12 | ){
13 | constructor():this(
14 | userAvatarUrl = "https://www.google.com/",
15 | userName = "Kamrul Hasan",
16 | repoName = "Kamrul3288",
17 | repoFullName = "Kamrul3288/JetpackCompose",
18 | repoDescription = "It's an awsome repository",
19 | language = "Kotlin",
20 | forksCount = 100,
21 | stargazers_count = 786
22 | )
23 | }
--------------------------------------------------------------------------------
/core/ui/src/androidTest/java/com/iamkamrul/ui/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.ui
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.iamkamrul.ui.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/common/src/androidTest/java/com/iamkamrul/common/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.common
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.iamkamrul.common.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/common/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
--------------------------------------------------------------------------------
/core/di/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
--------------------------------------------------------------------------------
/core/ui/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
--------------------------------------------------------------------------------
/features/profile/src/androidTest/java/com/iamkamrul/profile/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.profile
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.iamkamrul.profile.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/iamkamrul/modularization/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.modularization
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.iamkamrul.modularization", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/core/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.
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
--------------------------------------------------------------------------------
/core/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.
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/repolist/src/androidTest/java/com/iamkamrul/repolist/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.repolist
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.iamkamrul.repolist.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/core/designsystem/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
--------------------------------------------------------------------------------
/features/profile/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
--------------------------------------------------------------------------------
/features/repolist/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
--------------------------------------------------------------------------------
/core/designsystem/src/androidTest/java/com/iamkamrul/designsystem/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.designsystem
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.iamkamrul.designsystem.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/core/designsystem/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
2 | plugins {
3 | alias(libs.plugins.iamkamrul.android.library)
4 | alias(libs.plugins.iamkamrul.android.library.compose)
5 | }
6 |
7 | android {
8 | namespace = "com.iamkamrul.designsystem"
9 | }
10 |
11 | dependencies {
12 | api(libs.androidx.corektx)
13 | api(libs.androidx.compose.foundation)
14 | api(libs.androidx.compose.material.iconsExtended)
15 | api(libs.androidx.compose.material3)
16 | api(libs.androidx.compose.ui.tooling.preview)
17 | api(libs.androidx.compose.ui)
18 | api(libs.androidx.compose.ui.graphics)
19 | debugApi(libs.androidx.compose.ui.tooling)
20 |
21 | testImplementation(libs.test.junit)
22 | androidTestImplementation(libs.test.extjunit)
23 | androidTestImplementation(libs.test.espresso)
24 | }
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | includeBuild("build-logic")
3 | repositories {
4 | google()
5 | mavenCentral()
6 | gradlePluginPortal()
7 | }
8 | }
9 | dependencyResolutionManagement {
10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
11 | repositories {
12 | google()
13 | mavenCentral()
14 | maven("https://www.jitpack.io")
15 | }
16 | }
17 |
18 | rootProject.name = "JetPackComposeModularization"
19 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
20 | include (":app")
21 | include(":core:di")
22 | include(":core:domain")
23 | include(":core:data")
24 | include(":model:entity")
25 | include(":model:apiresponse")
26 |
27 | include(":common")
28 | include(":core:designsystem")
29 | include(":core:ui")
30 |
31 | include(":features:repolist")
32 | include(":features:profile")
33 |
34 |
--------------------------------------------------------------------------------
/core/ui/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
2 | plugins {
3 | alias(libs.plugins.iamkamrul.android.library)
4 | alias(libs.plugins.iamkamrul.android.library.compose)
5 | }
6 |
7 | android {
8 | namespace = "com.iamkamrul.ui"
9 | }
10 |
11 | dependencies {
12 | implementation(projects.core.designsystem)
13 |
14 |
15 | api(libs.androidx.corektx)
16 | api(libs.androidx.compose.foundation)
17 | api(libs.androidx.compose.material.iconsExtended)
18 | api(libs.androidx.compose.material3)
19 | api(libs.androidx.compose.ui.tooling.preview)
20 | api(libs.androidx.compose.ui)
21 | api(libs.androidx.compose.ui.graphics)
22 | debugApi(libs.androidx.compose.ui.tooling)
23 |
24 | testImplementation(libs.test.junit)
25 | androidTestImplementation(libs.test.extjunit)
26 | androidTestImplementation(libs.test.espresso)
27 | }
--------------------------------------------------------------------------------
/core/data/src/main/java/com/iamkamrul/data/mapper/ProfileMapper.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.data.mapper
2 |
3 | import com.iamkamrul.apiresponse.ProfileApiResponse
4 | import com.iamkamrul.data.utils.Mapper
5 | import com.iamkamrul.entity.ProfileEntity
6 | import javax.inject.Inject
7 |
8 | class ProfileMapper @Inject constructor():Mapper {
9 | override fun mapFromApiResponse(type: ProfileApiResponse): ProfileEntity {
10 | return ProfileEntity(
11 | userAvatar = type.avatar_url ?:"https://www.pullrequest.com/blog/github-code-review-service/images/github-logo_hub2899c31b6ca7aed8d6a218f0e752fe4_46649_1200x1200_fill_box_center_2.png",
12 | userFullName = type.name ?: "NAME_NOT_FOUND",
13 | userName = type.login ?: "",
14 | about = type.bio ?: "BIO_NOT_FOUND",
15 | repoCount = type.public_repos ?: 0,
16 | followerCount = type.followers ?: 0,
17 | followingCount = type.following ?: 0
18 | )
19 | }
20 | }
--------------------------------------------------------------------------------
/model/apiresponse/src/main/java/com/iamkamrul/apiresponse/ProfileApiResponse.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.apiresponse
2 |
3 | data class ProfileApiResponse(
4 | val avatar_url: String?,
5 | val bio: String?,
6 | val blog: String?,
7 | val company: String?,
8 | val created_at: String?,
9 | val email: String?,
10 | val events_url: String?,
11 | val followers: Int?,
12 | val followers_url: String?,
13 | val following: Int?,
14 | val following_url: String?,
15 | val gists_url: String?,
16 | val gravatar_id: String?,
17 | val hireable: Boolean?,
18 | val html_url: String?,
19 | val id: Int?,
20 | val location: String?,
21 | val login: String?,
22 | val name: String?,
23 | val node_id: String?,
24 | val organizations_url: String?,
25 | val public_gists: Int?,
26 | val public_repos: Int?,
27 | val received_events_url: String?,
28 | val repos_url: String?,
29 | val site_admin: Boolean?,
30 | val starred_url: String?,
31 | val subscriptions_url: String?,
32 | val twitter_username: String?,
33 | val type: String?,
34 | val updated_at: String?,
35 | val url: String?
36 | )
--------------------------------------------------------------------------------
/core/designsystem/src/main/java/com/iamkamrul/designsystem/theme/AppColorScheme.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.designsystem.theme
2 |
3 | import androidx.compose.material3.MaterialTheme
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.ReadOnlyComposable
6 | import androidx.compose.runtime.compositionLocalOf
7 | import androidx.compose.ui.graphics.Color
8 |
9 |
10 | data class AppColor(
11 | val white: Color = Color.Unspecified,
12 | val black: Color = Color.Unspecified,
13 | val topAppBar: Color = Color.Unspecified,
14 | val secondaryBackground: Color = Color.Unspecified
15 | )
16 | internal val LocalAppColor = compositionLocalOf { AppColor() }
17 |
18 | val MaterialTheme.color: AppColor
19 | @Composable
20 | @ReadOnlyComposable
21 | get() = LocalAppColor.current
22 |
23 |
24 |
25 | internal val LocalLightColorScheme = AppColor(
26 | white = White,
27 | black = Black,
28 | topAppBar = White,
29 | secondaryBackground = Black95,
30 | )
31 |
32 | internal val LocalDarkColorScheme = AppColor(
33 | white = Black,
34 | black = White,
35 | topAppBar = Black10,
36 | secondaryBackground = Black
37 | )
38 |
39 |
--------------------------------------------------------------------------------
/core/data/src/main/java/com/iamkamrul/data/mapper/RepoListItemMapper.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.data.mapper
2 |
3 | import com.iamkamrul.apiresponse.RepoItemApiResponse
4 | import com.iamkamrul.data.utils.Mapper
5 | import com.iamkamrul.entity.RepoItemEntity
6 | import javax.inject.Inject
7 |
8 | class RepoListItemMapper @Inject constructor() : Mapper,List>{
9 | override fun mapFromApiResponse(type: List): List {
10 | return type.map {
11 | RepoItemEntity(
12 | userAvatarUrl = it.owner?.avatar_url ?: "https://www.pullrequest.com/blog/github-code-review-service/images/github-logo_hub2899c31b6ca7aed8d6a218f0e752fe4_46649_1200x1200_fill_box_center_2.png",
13 | userName = it.owner?.login ?: "NO_USER_NAME_FOUND",
14 | repoName = it.name ?: "EMPTY_REPO_NAME",
15 | repoFullName = it.full_name ?: "EMPTY_REPO_NAME",
16 | repoDescription = it.description ?: "No description found",
17 | language = it.language ?: "Not Found",
18 | forksCount = it.forks_count ?: 0,
19 | stargazers_count = it.stargazers_count ?: 0
20 | )
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/iamkamrul/modularization/navigation/AppNavigation.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.modularization.navigation
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.lifecycle.Lifecycle
6 | import androidx.navigation.NavController
7 | import androidx.navigation.NavHostController
8 | import androidx.navigation.compose.NavHost
9 | import androidx.navigation.compose.rememberNavController
10 | import com.iamkamrul.profile.navigateToProfileScreen
11 | import com.iamkamrul.profile.profileScreen
12 | import com.iamkamrul.repolist.repoListScreen
13 | import com.iamkamrul.repolist.repoListScreenRoute
14 |
15 | @Composable
16 | fun AppNavigation(
17 | modifier: Modifier = Modifier,
18 | navController: NavHostController = rememberNavController(),
19 | startDestination:String = repoListScreenRoute
20 | ){
21 | NavHost(
22 | navController = navController,
23 | startDestination = startDestination,
24 | modifier = modifier
25 | ) {
26 | repoListScreen(onRepoItemClick = navController::navigateToProfileScreen)
27 | profileScreen(onBackBtnClick = navController::popBackStackOrIgnore)
28 | }
29 | }
30 |
31 | fun NavController.popBackStackOrIgnore() {
32 | if (currentBackStackEntry?.lifecycle?.currentState == Lifecycle.State.RESUMED) {
33 | popBackStack()
34 | }
35 | }
--------------------------------------------------------------------------------
/core/di/src/main/java/com/iamkamrul/di/module/RetrofitModule.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.di.module
2 |
3 | import com.google.gson.Gson
4 | import com.google.gson.GsonBuilder
5 | import com.iamkamrul.di.qualifier.AppBaseUrl
6 | import dagger.Module
7 | import dagger.Provides
8 | import dagger.hilt.InstallIn
9 | import dagger.hilt.components.SingletonComponent
10 | import okhttp3.OkHttpClient
11 | import retrofit2.Retrofit
12 | import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
13 | import retrofit2.converter.gson.GsonConverterFactory
14 |
15 |
16 | @Module
17 | @InstallIn(SingletonComponent::class)
18 | object RetrofitModule {
19 |
20 | @Provides
21 | @AppBaseUrl
22 | fun provideRetrofit(
23 | @AppBaseUrl baseUrl: String,
24 | okHttpClient: OkHttpClient,
25 | factory: GsonConverterFactory
26 | ): Retrofit {
27 | return Retrofit.Builder()
28 | .baseUrl(baseUrl)
29 | .client(okHttpClient)
30 | .addConverterFactory(factory)
31 | .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
32 | .build()
33 | }
34 |
35 | @Provides
36 | fun provideConverterFactory(gson: Gson): GsonConverterFactory {
37 | return GsonConverterFactory.create(gson)
38 | }
39 |
40 | @Provides
41 | fun provideGson(): Gson {
42 | val gsonBuilder = GsonBuilder()
43 | return gsonBuilder.create()
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/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 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/app/src/main/java/com/iamkamrul/modularization/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.modularization
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.activity.enableEdgeToEdge
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.material3.MaterialTheme
9 | import androidx.compose.material3.Surface
10 | import androidx.compose.ui.Modifier
11 | import androidx.navigation.compose.rememberNavController
12 | import com.iamkamrul.modularization.navigation.AppNavigation
13 | import com.iamkamrul.designsystem.theme.JetPackComposeModularizationTheme
14 | import dagger.hilt.android.AndroidEntryPoint
15 |
16 | @AndroidEntryPoint
17 | class MainActivity : ComponentActivity() {
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | enableEdgeToEdge()
20 | super.onCreate(savedInstanceState)
21 | setContent {
22 | JetPackComposeModularizationTheme {
23 | val navController = rememberNavController()
24 | Surface(
25 | modifier = Modifier.fillMaxSize(),
26 | color = MaterialTheme.colorScheme.background
27 | ) {
28 | AppNavigation(
29 | navController = navController
30 | )
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | .idea/
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 | local.properties
11 |
12 | # Built application files
13 | *.apk
14 | *.aar
15 | *.ap_
16 | *.aab
17 |
18 | # Files for the ART/Dalvik VM
19 | *.dex
20 |
21 | # Java class files
22 | *.class
23 |
24 | # Generated files
25 | bin/
26 | gen/
27 | out/
28 | # Uncomment the following line in case you need and you don't have the release build type files in your app
29 | # release/
30 |
31 | # Gradle files
32 | .gradle/
33 | build/
34 |
35 | # Proguard folder generated by Eclipse
36 | proguard/
37 |
38 | # Log Files
39 | *.log
40 |
41 | # Android Studio Navigation editor temp files
42 | .navigation/
43 |
44 | # Android Studio captures folder
45 | captures/
46 |
47 | # Keystore files
48 | # Uncomment the following lines if you do not want to check your keystore files in.
49 | #*.jks
50 | #*.keystore
51 |
52 | # External native build folder generated in Android Studio 2.2 and later
53 | .cxx/
54 |
55 | # Google Services (e.g. APIs or Firebase)
56 | # google-services.json
57 |
58 | # Freeline
59 | freeline.py
60 | freeline/
61 | freeline_project_description.json
62 |
63 | # fastlane
64 | fastlane/report.xml
65 | fastlane/Preview.html
66 | fastlane/screenshots
67 | fastlane/test_output
68 | fastlane/readme.md
69 |
70 | # Version control
71 | vcs.xml
72 |
73 | # lint
74 | lint/intermediates/
75 | lint/generated/
76 | lint/outputs/
77 | lint/tmp/
78 |
--------------------------------------------------------------------------------
/core/data/src/main/java/com/iamkamrul/data/repoimpl/GithubRepoImpl.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.data.repoimpl
2 |
3 | import com.iamkamrul.data.apiservice.ApiService
4 | import com.iamkamrul.data.mapper.ProfileMapper
5 | import com.iamkamrul.data.mapper.RepoListItemMapper
6 | import com.iamkamrul.data.utils.NetworkBoundResource
7 | import com.iamkamrul.data.utils.mapFromApiResponse
8 | import com.iamkamrul.domain.repository.GithubRepository
9 | import com.iamkamrul.domain.usecase.ProfileUseCase
10 | import com.iamkamrul.domain.usecase.RepoListUseCase
11 | import com.iamkamrul.domain.utils.Result
12 | import com.iamkamrul.entity.ProfileEntity
13 | import com.iamkamrul.entity.RepoItemEntity
14 | import kotlinx.coroutines.flow.Flow
15 | import javax.inject.Inject
16 |
17 | class GithubRepoImpl @Inject constructor(
18 | private val apiService: ApiService,
19 | private val networkBoundResources: NetworkBoundResource,
20 | private val repositoryListItemMapper: RepoListItemMapper,
21 | private val profileMapper: ProfileMapper
22 | ):GithubRepository{
23 |
24 | override suspend fun fetchRepoList(params: RepoListUseCase.Params): Flow>> {
25 | return mapFromApiResponse(
26 | result = networkBoundResources.downloadData {
27 | apiService.fetchRepoList(params.userName)
28 | },repositoryListItemMapper
29 | )
30 | }
31 |
32 | override suspend fun fetchProfile(params: ProfileUseCase.Params): Flow> {
33 | return mapFromApiResponse(
34 | result = networkBoundResources.downloadData {
35 | apiService.fetchProfile(params.userName)
36 | },profileMapper
37 | )
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/core/di/src/main/java/com/iamkamrul/di/module/OkHttpModule.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.di.module
2 |
3 |
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.hilt.InstallIn
7 | import dagger.hilt.components.SingletonComponent
8 | import okhttp3.OkHttpClient
9 | import okhttp3.logging.HttpLoggingInterceptor
10 | import timber.log.Timber
11 | import java.util.concurrent.TimeUnit
12 | import javax.inject.Singleton
13 |
14 | @Module
15 | @InstallIn(SingletonComponent::class)
16 | object OkHttpModule {
17 |
18 | @Provides
19 | @Singleton
20 | fun provideLoggerInterceptor(): HttpLoggingInterceptor {
21 | val interceptor = HttpLoggingInterceptor { message -> Timber.e(message) }
22 | interceptor.apply { interceptor.level = HttpLoggingInterceptor.Level.HEADERS }
23 | interceptor.apply { interceptor.level = HttpLoggingInterceptor.Level.BODY }
24 | return interceptor
25 | }
26 |
27 | @Provides
28 | @Singleton
29 | fun provideOkHttpClient(
30 | loggerInterceptor: HttpLoggingInterceptor,
31 | ): OkHttpClient {
32 | val timeOut = 30
33 | val httpClient = OkHttpClient().newBuilder()
34 | .connectTimeout(timeOut.toLong(), TimeUnit.SECONDS)
35 | .readTimeout(timeOut.toLong(), TimeUnit.SECONDS)
36 | .writeTimeout(timeOut.toLong(), TimeUnit.SECONDS)
37 |
38 | httpClient.addInterceptor(loggerInterceptor)
39 | httpClient.addInterceptor { chain ->
40 | val original = chain.request()
41 | val requestBuilder = original.newBuilder()
42 | .addHeader("Accept", "application/json")
43 | val request = requestBuilder.build()
44 | chain.proceed(request)
45 | }
46 | return httpClient.build()
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/core/ui/src/main/java/com/iamkamrul/ui/component/ErrorMessageCompose.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.ui.component
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.Spacer
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.foundation.layout.fillMaxWidth
9 | import androidx.compose.foundation.layout.height
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.foundation.shape.RoundedCornerShape
12 | import androidx.compose.material3.Button
13 | import androidx.compose.material3.MaterialTheme
14 | import androidx.compose.material3.Text
15 | import androidx.compose.runtime.Composable
16 | import androidx.compose.ui.Alignment
17 | import androidx.compose.ui.Modifier
18 | import androidx.compose.ui.text.style.TextAlign
19 | import androidx.compose.ui.unit.dp
20 | import com.iamkamrul.designsystem.theme.color
21 |
22 | @Composable
23 | fun NetworkErrorMessage(
24 | message:String,
25 | modifier: Modifier = Modifier,
26 | onClickRefresh:()->Unit
27 | ){
28 | Column(
29 | verticalArrangement = Arrangement.Center,
30 | horizontalAlignment = Alignment.CenterHorizontally,
31 | modifier = modifier
32 | .background(MaterialTheme.color.white)
33 | .fillMaxSize()
34 | .padding(16.dp)
35 | ) {
36 |
37 | Text(text = message, style = MaterialTheme.typography.titleLarge, textAlign = TextAlign.Center)
38 | Spacer(modifier = modifier.height(32.dp))
39 | Button(
40 | modifier = modifier.fillMaxWidth().height(50.dp),
41 | onClick = onClickRefresh,
42 | shape = RoundedCornerShape(10.dp)
43 | ) {
44 | Text(text = "Refresh")
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/features/profile/src/main/java/com/iamkamrul/profile/ProfileViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.profile
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.iamkamrul.domain.usecase.ProfileUseCase
6 | import com.iamkamrul.domain.utils.Result
7 | import com.iamkamrul.entity.ProfileEntity
8 | import dagger.hilt.android.lifecycle.HiltViewModel
9 | import kotlinx.coroutines.flow.*
10 | import kotlinx.coroutines.launch
11 | import javax.inject.Inject
12 |
13 | @HiltViewModel
14 | class ProfileViewModel @Inject constructor(
15 | private val profileUseCase: ProfileUseCase
16 | ): ViewModel(){
17 | private val _profileUiState = MutableStateFlow(ProfileUiState.Loading)
18 | val profileUiState get() = _profileUiState.asStateFlow()
19 |
20 |
21 | init {
22 | fetchProfile()
23 | }
24 |
25 | private fun fetchProfile(){
26 | viewModelScope.launch {
27 | profileUseCase.execute(ProfileUseCase.Params(userName = "kamrul3288")).collect{ response->
28 | when(response){
29 | is Result.Error -> _profileUiState.value = ProfileUiState.Error(response.message)
30 | Result.Loading -> _profileUiState.value = ProfileUiState.Loading
31 | is Result.Success -> _profileUiState.value = ProfileUiState.Success(response.data)
32 | }
33 | }
34 | }
35 | }
36 |
37 | fun handleAction(action: ProfileUiAction){
38 | when(action){
39 | ProfileUiAction.FetchProfile -> fetchProfile()
40 | }
41 | }
42 | }
43 |
44 |
45 |
46 | sealed interface ProfileUiState{
47 | data object Loading : ProfileUiState
48 | data class Success(val data:ProfileEntity): ProfileUiState
49 | data class Error(val message:String) : ProfileUiState
50 | }
51 |
52 | sealed interface ProfileUiAction{
53 | data object FetchProfile:ProfileUiAction
54 | }
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/features/repolist/src/main/java/com/iamkamrul/repolist/RepoListViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.repolist
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.iamkamrul.domain.usecase.RepoListUseCase
6 | import com.iamkamrul.domain.utils.Result
7 | import com.iamkamrul.entity.RepoItemEntity
8 | import dagger.hilt.android.lifecycle.HiltViewModel
9 | import kotlinx.coroutines.flow.*
10 | import kotlinx.coroutines.launch
11 | import javax.inject.Inject
12 |
13 | @HiltViewModel
14 | class RepoListViewModel @Inject constructor(
15 | private val repoListUseCase: RepoListUseCase
16 | ): ViewModel(){
17 | private val _repoListUiState = MutableStateFlow(RepoListUiState.Loading)
18 | val repoListUiState get() = _repoListUiState.asStateFlow()
19 |
20 | init {
21 | fetchRepoList()
22 | }
23 |
24 | private fun fetchRepoList(){
25 | viewModelScope.launch {
26 | repoListUseCase.execute(RepoListUseCase.Params(userName = "kamrul3288")).collect{response->
27 | when(response){
28 | is Result.Error -> _repoListUiState.value = RepoListUiState.Error(response.message)
29 | is Result.Loading -> _repoListUiState.value = RepoListUiState.Loading
30 | is Result.Success ->{
31 | if (response.data.isEmpty()){
32 | _repoListUiState.value = RepoListUiState.RepoListEmpty
33 | return@collect
34 | }
35 | _repoListUiState.value = RepoListUiState.HasRepoList(response.data)
36 | }
37 | }
38 | }
39 | }
40 | }
41 |
42 | fun handleAction(action: RepoListUiAction){
43 | when(action){
44 | RepoListUiAction.FetchRepoList -> fetchRepoList()
45 | }
46 | }
47 | }
48 |
49 | sealed interface RepoListUiState{
50 | data object Loading:RepoListUiState
51 | data class HasRepoList(val repoList:List):RepoListUiState
52 | data object RepoListEmpty:RepoListUiState
53 | data class Error(val message:String):RepoListUiState
54 | }
55 |
56 | sealed interface RepoListUiAction{
57 | data object FetchRepoList:RepoListUiAction
58 | }
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.iamkamrul.android.application)
3 | alias(libs.plugins.iamkamrul.android.hilt)
4 | alias(libs.plugins.iamkamrul.android.application.compose)
5 | }
6 |
7 | android {
8 | namespace = "com.iamkamrul.modularization"
9 | defaultConfig {
10 | applicationId = "com.iamkamrul.compose"
11 | versionCode = 1
12 | versionName = "1.0.0"
13 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
14 | vectorDrawables {
15 | useSupportLibrary = true
16 | }
17 | }
18 |
19 | buildTypes {
20 | debug {
21 | isMinifyEnabled = false
22 | isShrinkResources = false
23 | proguardFiles(
24 | getDefaultProguardFile("proguard-android-optimize.txt"),
25 | "proguard-rules.pro"
26 | )
27 | }
28 | debug {
29 | isMinifyEnabled = false
30 | isShrinkResources = false
31 | proguardFiles(
32 | getDefaultProguardFile("proguard-android-optimize.txt"),
33 | "proguard-rules.pro"
34 | )
35 | }
36 | }
37 | buildFeatures{
38 | buildConfig = true
39 | }
40 | packaging {
41 | resources {
42 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
43 | }
44 | }
45 | }
46 | dependencies {
47 | implementation(projects.core.di)
48 | implementation(projects.core.domain)
49 | implementation(projects.core.data)
50 | implementation(projects.common)
51 | implementation(projects.core.ui)
52 | implementation(projects.core.designsystem)
53 |
54 | implementation(projects.features.repolist)
55 | implementation(projects.features.profile)
56 |
57 | implementation(libs.androidx.compose.activity)
58 | implementation(libs.androidx.compose.navigation)
59 | implementation(libs.androidx.compose.hilt.navigation)
60 | implementation(libs.androidx.lifecycle.runtimeCompose)
61 | implementation(libs.androidx.lifecycle.viewModelCompose)
62 |
63 | testImplementation(libs.test.junit)
64 | androidTestImplementation(libs.test.extjunit)
65 | androidTestImplementation(libs.test.espresso)
66 | androidTestImplementation(libs.test.compose.ui.junit)
67 | debugImplementation(libs.androidx.compose.ui.manifest)
68 | }
--------------------------------------------------------------------------------
/core/designsystem/src/main/java/com/iamkamrul/designsystem/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.designsystem.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Orange10 = Color(0xFF6B261E)
6 | val Orange20 = Color(0xFF9D2D20)
7 | val Orange30 = Color(0xFFD3301D)
8 | val Orange40 = Color(0xFFF04935)
9 | val Orange50 = Color(0xFFF35B49)
10 | val Orange60 = Color(0xFFF87C6D)
11 | val Orange70 = Color(0xFFFB9084)
12 | val Orange80 = Color(0xFFFCA197)
13 | val Orange90 = Color(0xFFFDB3AA)
14 | val Orange95 = Color(0xFFFED2CD)
15 | val Orange99 = Color(0xFFFED2CD)
16 |
17 |
18 | val BlueGray10 = Color(0xFF1D192B)
19 | val BlueGray20 = Color(0xFF332D41)
20 | val BlueGray30 = Color(0xFF4A4458)
21 | val BlueGray40 = Color(0xFF625B71)
22 | val BlueGray50 = Color(0xFF7A7289)
23 | val BlueGray60 = Color(0xFF958DA5)
24 | val BlueGray70 = Color(0xFFB0A7C0)
25 | val BlueGray80 = Color(0xFFCCC2DC)
26 | val BlueGray90 = Color(0xFFE8DEF8)
27 | val BlueGray95 = Color(0xFFF6EDFF)
28 | val BlueGray99 = Color(0xFFFFFBFE)
29 |
30 |
31 | val Red10 = Color(0xFF410E0B)
32 | val Red20 = Color(0xFF601410)
33 | val Red30 = Color(0xFF8C1D18)
34 | val Red40 = Color(0xFFB3261E)
35 | val Red50 = Color(0xFFDC362E)
36 | val Red60 = Color(0xFFE46962)
37 | val Red70 = Color(0xFFEC928E)
38 | val Red80 = Color(0xFFF2B8B5)
39 |
40 | val Green10 = Color(0xFF051905)
41 | val Green20 = Color(0xFF0A480A)
42 | val Green30 = Color(0xFF075F07)
43 | val Green40 = Color(0xFF027D02)
44 | val Green50 = Color(0xFF029702)
45 | val Green60 = Color(0xFF02B102)
46 | val Green70 = Color(0xFF01CB01)
47 | val Green80 = Color(0xFF00E500)
48 | val Green90 = Color(0xFF5FF45F)
49 | val Green95 = Color(0xFF96FB96)
50 | val Green99 = Color(0xFFC0FCC0)
51 |
52 | val White = Color(0xFFFFFFFF)
53 | val White10 = Color(0xFFE6E6E6)
54 | val White20 = Color(0xFFCCCCCC)
55 | val White30 = Color(0xFFB3B3B3)
56 | val White40 = Color(0xFF999999)
57 | val White50 = Color(0xFF808080)
58 | val White60 = Color(0xFF666666)
59 | val White70 = Color(0xFF4D4D4D)
60 | val White80 = Color(0xFF333333)
61 | val White90 = Color(0xFF1A1A1A)
62 | val White95 = Color(0xFF0D0D0D)
63 | val White99 = Color(0xFF030303)
64 |
65 |
66 | val Black = Color(0xFF000000)
67 | val Black10 = Color(0xFF1A1A1A)
68 | val Black20 = Color(0xFF333333)
69 | val Black30 = Color(0xFF4D4D4D)
70 | val Black40 = Color(0xFF666666)
71 | val Black50 = Color(0xFF808080)
72 | val Black60 = Color(0xFF999999)
73 | val Black70 = Color(0xFFB3B3B3)
74 | val Black80 = Color(0xFFCCCCCC)
75 | val Black90 = Color(0xFFE6E6E6)
76 | val Black95 = Color(0xFFF2F2F2)
77 | val Black99 = Color(0xFFFCFCFC)
78 |
79 |
80 | val Gray10 = Color(0xFFCFCFCF)
81 | val Gray20 = Color(0xFFD1D1D1)
82 | val Gray30 = Color(0xFFD9D9D9)
83 | val Gray40 = Color(0xFFDEDEDE)
84 | val Gray50 = Color(0xFFE3E3E3)
85 | val Gray60 = Color(0xFFE3E3E3)
86 | val Gray70 = Color(0xFFEDEDED)
87 | val Gray80 = Color(0xFFF2F2F2)
88 | val Gray90 = Color(0xFFF7F7F7)
89 | val Gray95 = Color(0xFFFAFAFA)
90 | val Gray99 = Color(0xFFFCFCFC)
91 |
--------------------------------------------------------------------------------
/core/data/src/main/java/com/iamkamrul/data/utils/NetworkBoundResource.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.data.utils
2 |
3 | import com.google.gson.JsonParser
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.withContext
7 | import okhttp3.ResponseBody
8 | import retrofit2.HttpException
9 | import retrofit2.Response
10 | import java.io.IOException
11 | import java.net.SocketTimeoutException
12 | import com.iamkamrul.domain.utils.Result
13 | import kotlinx.coroutines.flow.Flow
14 | import kotlinx.coroutines.flow.catch
15 | import kotlinx.coroutines.flow.flow
16 | import timber.log.Timber
17 | import javax.inject.Inject
18 |
19 | class NetworkBoundResource @Inject constructor(){
20 | private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
21 |
22 | suspend fun downloadData(api : suspend () -> Response): Flow> {
23 | return withContext(ioDispatcher) {
24 | flow {
25 | emit(Result.Loading)
26 | val response:Response = api()
27 | if (response.isSuccessful){
28 | response.body()?.let {
29 | emit(Result.Success(data = it))
30 | }?: emit(Result.Error(message = "Unknown error occurred", code = 0))
31 | }else{
32 | emit(Result.Error(message = parserErrorBody(response.errorBody()), code = response.code()))
33 | }
34 |
35 | }.catch { error->
36 | Timber.e(error.localizedMessage)
37 | emit(Result.Error(message = message(error), code = code(error)))
38 | }
39 | }
40 | }
41 |
42 | private fun parserErrorBody(response: ResponseBody?):String{
43 | return response?.let {
44 | val errorMessage = JsonParser.parseString(it.string()).asJsonObject["message"].asString
45 | errorMessage.ifEmpty { "Whoops! Something went wrong. Please try again." }
46 | errorMessage
47 | }?:"Whoops! Unknown error occurred. Please try again"
48 | }
49 | private fun message(throwable: Throwable?):String{
50 | when (throwable) {
51 | is SocketTimeoutException -> return "Whoops! Connection time out. Please try again"
52 | is IOException -> return "Whoops! No Internet Connection. Please try again"
53 | is HttpException -> return try {
54 | val errorJsonString = throwable.response()?.errorBody()?.string()
55 | val errorMessage = JsonParser.parseString(errorJsonString).asJsonObject["message"].asString
56 | errorMessage.ifEmpty { "Whoops! Something went wrong. Please try again." }
57 | }catch (e:Exception){
58 | "Whoops! Unknown error occurred. Please try again"
59 | }
60 | }
61 | return "Whoops! Unknown error occurred. Please try again"
62 | }
63 | private fun code(throwable: Throwable?):Int{
64 | return if (throwable is HttpException) (throwable).code()
65 | else 0
66 | }
67 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/model/apiresponse/src/main/java/com/iamkamrul/apiresponse/RepoItemApiResponse.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.apiresponse
2 |
3 | data class RepoItemApiResponse(
4 | val archive_url: String?,
5 | val archived: Boolean?,
6 | val assignees_url: String?,
7 | val blobs_url: String?,
8 | val branches_url: String?,
9 | val clone_url: String?,
10 | val collaborators_url: String?,
11 | val comments_url: String?,
12 | val commits_url: String?,
13 | val compare_url: String?,
14 | val contents_url: String?,
15 | val contributors_url: String?,
16 | val created_at: String?,
17 | val default_branch: String?,
18 | val deployments_url: String?,
19 | val description: String?,
20 | val disabled: Boolean?,
21 | val downloads_url: String?,
22 | val events_url: String?,
23 | val fork: Boolean?,
24 | val forks_count: Int?,
25 | val forks_url: String?,
26 | val full_name: String?,
27 | val git_commits_url: String?,
28 | val git_refs_url: String?,
29 | val git_tags_url: String?,
30 | val git_url: String?,
31 | val has_downloads: Boolean?,
32 | val has_issues: Boolean?,
33 | val has_pages: Boolean?,
34 | val has_projects: Boolean?,
35 | val has_wiki: Boolean?,
36 | val homepage: String?,
37 | val hooks_url: String?,
38 | val html_url: String?,
39 | val id: Int?,
40 | val is_template: Boolean?,
41 | val issue_comment_url: String?,
42 | val issue_events_url: String?,
43 | val issues_url: String?,
44 | val keys_url: String?,
45 | val labels_url: String?,
46 | val language: String?,
47 | val languages_url: String?,
48 | val merges_url: String?,
49 | val milestones_url: String?,
50 | val mirror_url: String?,
51 | val name: String?,
52 | val node_id: String?,
53 | val notifications_url: String?,
54 | val open_issues_count: Int?,
55 | val owner: ReposItemOwner?,
56 | val permissions: ReposItemPermissions?,
57 | val `private`: Boolean?,
58 | val pulls_url: String?,
59 | val pushed_at: String?,
60 | val releases_url: String?,
61 | val size: Int?,
62 | val ssh_url: String?,
63 | val stargazers_count: Int?,
64 | val stargazers_url: String?,
65 | val statuses_url: String?,
66 | val subscribers_url: String?,
67 | val subscription_url: String?,
68 | val svn_url: String?,
69 | val tags_url: String?,
70 | val teams_url: String?,
71 | val template_repository: Any?,
72 | val topics: List?,
73 | val trees_url: String?,
74 | val updated_at: String?,
75 | val url: String?,
76 | val visibility: String?,
77 | val watchers_count: Int?
78 | )
79 |
80 | data class ReposItemOwner(
81 | val avatar_url: String?,
82 | val events_url: String?,
83 | val followers_url: String?,
84 | val following_url: String?,
85 | val gists_url: String?,
86 | val gravatar_id: String?,
87 | val html_url: String?,
88 | val id: Int?,
89 | val login: String?,
90 | val node_id: String?,
91 | val organizations_url: String?,
92 | val received_events_url: String?,
93 | val repos_url: String?,
94 | val site_admin: Boolean?,
95 | val starred_url: String?,
96 | val subscriptions_url: String?,
97 | val type: String?,
98 | val url: String?
99 | )
100 |
101 | data class ReposItemPermissions(
102 | val admin: Boolean?,
103 | val pull: Boolean?,
104 | val push: Boolean?
105 | )
--------------------------------------------------------------------------------
/core/designsystem/src/main/java/com/iamkamrul/designsystem/component/ScaffoldTopAppbar.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.designsystem.component
2 |
3 | import androidx.compose.foundation.layout.PaddingValues
4 | import androidx.compose.material.icons.Icons
5 | import androidx.compose.material.icons.outlined.ArrowBack
6 | import androidx.compose.material3.CenterAlignedTopAppBar
7 | import androidx.compose.material3.ExperimentalMaterial3Api
8 | import androidx.compose.material3.Icon
9 | import androidx.compose.material3.IconButton
10 | import androidx.compose.material3.MaterialTheme
11 | import androidx.compose.material3.Scaffold
12 | import androidx.compose.material3.Surface
13 | import androidx.compose.material3.Text
14 | import androidx.compose.material3.TopAppBarDefaults
15 | import androidx.compose.material3.contentColorFor
16 | import androidx.compose.runtime.Composable
17 | import androidx.compose.ui.graphics.Color
18 | import androidx.compose.ui.graphics.painter.Painter
19 | import androidx.compose.ui.graphics.vector.rememberVectorPainter
20 | import androidx.compose.ui.unit.dp
21 | import com.iamkamrul.designsystem.theme.color
22 |
23 | @OptIn(ExperimentalMaterial3Api::class)
24 | @Composable
25 | fun ScaffoldTopAppbar(
26 | containerColor: Color = MaterialTheme.colorScheme.background,
27 | contentColor: Color = contentColorFor(containerColor),
28 | title:String,
29 | onNavigationIconClick:()->Unit,
30 | navigationIcon: Painter = rememberVectorPainter(image = Icons.Outlined.ArrowBack),
31 | snackbarHost: @Composable () -> Unit = {},
32 | bottomBar: @Composable () -> Unit = {},
33 | content: @Composable (PaddingValues) -> Unit,
34 | ){
35 | Scaffold(
36 | containerColor = containerColor,
37 | contentColor = contentColor,
38 | snackbarHost = snackbarHost,
39 | topBar = {
40 | Surface(shadowElevation = 1.dp) {
41 | CenterAlignedTopAppBar(
42 | title = {
43 | Text(text = title)
44 | },
45 | colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
46 | containerColor = MaterialTheme.color.topAppBar,
47 | navigationIconContentColor = MaterialTheme.color.black,
48 | titleContentColor = MaterialTheme.color.black,
49 | actionIconContentColor = MaterialTheme.color.black,
50 | ),
51 | navigationIcon = {
52 | IconButton(onClick = onNavigationIconClick) {
53 | Icon(
54 | painter = navigationIcon,
55 | contentDescription = "navigationIcon"
56 | )
57 | }
58 | },
59 | )
60 | }
61 | },
62 | bottomBar = bottomBar,
63 | content = content
64 | )
65 | }
66 |
67 | @OptIn(ExperimentalMaterial3Api::class)
68 | @Composable
69 | fun ScaffoldTopAppbar(
70 | containerColor: Color = MaterialTheme.colorScheme.background,
71 | contentColor: Color = contentColorFor(containerColor),
72 | title:String,
73 | snackbarHost: @Composable () -> Unit = {},
74 | bottomBar: @Composable () -> Unit = {},
75 | content: @Composable (PaddingValues) -> Unit,
76 |
77 | ){
78 | Scaffold(
79 | snackbarHost = snackbarHost,
80 | containerColor = containerColor,
81 | contentColor = contentColor,
82 | topBar = {
83 | Surface(
84 | shadowElevation = 2.dp
85 | ) {
86 | CenterAlignedTopAppBar(
87 | title = {
88 | Text(text = title)
89 | },
90 | colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
91 | containerColor = MaterialTheme.color.topAppBar,
92 | navigationIconContentColor = MaterialTheme.color.black,
93 | titleContentColor = MaterialTheme.color.black,
94 | actionIconContentColor = MaterialTheme.color.black,
95 | )
96 | )
97 | }
98 | },
99 | bottomBar = bottomBar,
100 | content = content
101 | )
102 | }
103 |
--------------------------------------------------------------------------------
/core/designsystem/src/main/java/com/iamkamrul/designsystem/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.designsystem.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.Font
6 | import androidx.compose.ui.text.font.FontFamily
7 | import androidx.compose.ui.text.font.FontStyle
8 | import androidx.compose.ui.text.font.FontWeight
9 | import androidx.compose.ui.unit.sp
10 | import com.iamkamrul.designsystem.R
11 |
12 | internal val helveticaFamily = FontFamily(
13 | Font(R.font.roboto_extra_light,FontWeight.ExtraLight, FontStyle.Normal),
14 | Font(R.font.roboto_light,FontWeight.Light, FontStyle.Normal),
15 | Font(R.font.roboto_regular,FontWeight.Normal, FontStyle.Normal),
16 | Font(R.font.roboto_italic,FontWeight.Normal, FontStyle.Italic),
17 | Font(R.font.roboto_medium,FontWeight.Medium, FontStyle.Normal),
18 | Font(R.font.roboto_bold,FontWeight.Bold, FontStyle.Normal),
19 | Font(R.font.roboto_bold_italic,FontWeight.Bold, FontStyle.Italic),
20 | Font(R.font.roboto_black,FontWeight.Black, FontStyle.Normal),
21 |
22 | )
23 |
24 | val Typography = Typography(
25 | displayLarge = TextStyle(
26 | fontFamily = helveticaFamily,
27 | fontWeight = FontWeight.Normal,
28 | fontSize = 57.sp,
29 | lineHeight = 64.sp,
30 | letterSpacing = 0.5.sp
31 | ),
32 | displayMedium = TextStyle(
33 | fontFamily = helveticaFamily,
34 | fontWeight = FontWeight.Normal,
35 | fontSize = 45.sp,
36 | lineHeight = 52.sp,
37 | letterSpacing = 0.5.sp
38 | ),
39 | displaySmall = TextStyle(
40 | fontFamily = helveticaFamily,
41 | fontWeight = FontWeight.Normal,
42 | fontSize = 36.sp,
43 | lineHeight = 44.sp,
44 | letterSpacing = 0.5.sp
45 | ),
46 | headlineLarge = TextStyle(
47 | fontFamily = helveticaFamily,
48 | fontWeight = FontWeight.Normal,
49 | fontSize = 32.sp,
50 | lineHeight = 40.sp,
51 | letterSpacing = 0.5.sp
52 | ),
53 | headlineMedium = TextStyle(
54 | fontFamily = helveticaFamily,
55 | fontWeight = FontWeight.Normal,
56 | fontSize = 28.sp,
57 | lineHeight = 36.sp,
58 | letterSpacing = 0.5.sp
59 | ),
60 | headlineSmall = TextStyle(
61 | fontFamily = helveticaFamily,
62 | fontWeight = FontWeight.Normal,
63 | fontSize = 24.sp,
64 | lineHeight = 32.sp,
65 | letterSpacing = 0.5.sp
66 | ),
67 | titleLarge = TextStyle(
68 | fontFamily = helveticaFamily,
69 | fontWeight = FontWeight.Medium,
70 | fontSize = 22.sp,
71 | lineHeight = 28.sp,
72 | letterSpacing = 0.5.sp
73 | ),
74 | titleMedium = TextStyle(
75 | fontFamily = helveticaFamily,
76 | fontWeight = FontWeight.Medium,
77 | fontSize = 16.sp,
78 | lineHeight = 24.sp,
79 | letterSpacing = 0.5.sp
80 | ),
81 | titleSmall = TextStyle(
82 | fontFamily = helveticaFamily,
83 | fontWeight = FontWeight.Medium,
84 | fontSize = 14.sp,
85 | lineHeight = 20.sp,
86 | letterSpacing = 0.5.sp
87 | ),
88 | bodyLarge = TextStyle(
89 | fontFamily = helveticaFamily,
90 | fontWeight = FontWeight.Normal,
91 | fontSize = 16.sp,
92 | lineHeight = 24.sp,
93 | letterSpacing = 0.5.sp
94 | ),
95 | bodyMedium = TextStyle(
96 | fontFamily = helveticaFamily,
97 | fontWeight = FontWeight.Normal,
98 | fontSize = 12.sp,
99 | lineHeight = 16.sp,
100 | letterSpacing = 0.5.sp
101 | ),
102 | labelLarge = TextStyle(
103 | fontFamily = helveticaFamily,
104 | fontWeight = FontWeight.Medium,
105 | fontSize = 14.sp,
106 | lineHeight = 20.sp,
107 | letterSpacing = 0.5.sp
108 | ),
109 | labelMedium = TextStyle(
110 | fontFamily = helveticaFamily,
111 | fontWeight = FontWeight.Medium,
112 | fontSize = 12.sp,
113 | lineHeight = 16.sp,
114 | letterSpacing = 0.5.sp
115 | ),
116 | labelSmall = TextStyle(
117 | fontFamily = helveticaFamily,
118 | fontWeight = FontWeight.Medium,
119 | fontSize = 11.sp,
120 | lineHeight = 16.sp,
121 | letterSpacing = 0.5.sp
122 | ),
123 | )
--------------------------------------------------------------------------------
/features/profile/src/main/java/com/iamkamrul/profile/ProfileScreen.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.profile
2 | import androidx.compose.foundation.Image
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.border
5 | import androidx.compose.foundation.layout.*
6 | import androidx.compose.foundation.shape.CircleShape
7 | import androidx.compose.material3.CircularProgressIndicator
8 | import androidx.compose.material3.Text
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.runtime.getValue
11 | import androidx.compose.ui.Alignment
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.draw.clip
14 | import androidx.compose.ui.graphics.Color
15 | import androidx.compose.ui.text.font.FontWeight
16 | import androidx.compose.ui.unit.dp
17 | import androidx.compose.ui.unit.sp
18 | import androidx.hilt.navigation.compose.hiltViewModel
19 | import androidx.lifecycle.compose.collectAsStateWithLifecycle
20 | import coil.compose.rememberAsyncImagePainter
21 | import com.iamkamrul.designsystem.component.ScaffoldTopAppbar
22 | import com.iamkamrul.ui.component.NetworkErrorMessage
23 | import com.iamkamrul.entity.ProfileEntity
24 |
25 | @Composable
26 | internal fun ProfileScreenRoute(
27 | viewModel:ProfileViewModel = hiltViewModel(),
28 | onBackBtnClick:()->Unit
29 | ){
30 | val profileUiState by viewModel.profileUiState.collectAsStateWithLifecycle()
31 | ProfileScreen(
32 | profileUiState = profileUiState,
33 | onRefreshProfile = viewModel::handleAction,
34 | onBackBtnClick = onBackBtnClick
35 | )
36 | }
37 |
38 | @Composable
39 | private fun ProfileScreen(
40 | profileUiState: ProfileUiState,
41 | onRefreshProfile:(ProfileUiAction)->Unit,
42 | onBackBtnClick:()->Unit
43 | ){
44 | ScaffoldTopAppbar(
45 | title = "Profile",
46 | onNavigationIconClick = onBackBtnClick
47 | ) {
48 | val modifier = Modifier.padding(it)
49 | Box(
50 | modifier = modifier.fillMaxSize(),
51 | contentAlignment = Alignment.Center
52 | ){
53 | when(profileUiState){
54 | is ProfileUiState.Error -> NetworkErrorMessage(message = profileUiState.message){
55 | onRefreshProfile(ProfileUiAction.FetchProfile)
56 | }
57 | ProfileUiState.Loading -> CircularProgressIndicator()
58 | is ProfileUiState.Success -> ProfileContentView(data = profileUiState.data)
59 | }
60 | }
61 | }
62 | }
63 |
64 | @Composable
65 | private fun ProfileContentView(
66 | data: ProfileEntity,
67 | modifier: Modifier = Modifier,
68 | ){
69 | Column(
70 | modifier = modifier
71 | .padding(16.dp)
72 | .background(Color.White)
73 | .fillMaxSize(),
74 | horizontalAlignment = Alignment.CenterHorizontally
75 | ) {
76 | Image(
77 | painter = rememberAsyncImagePainter(model = data.userAvatar),
78 | contentDescription = "",
79 | modifier = modifier
80 | .size(80.dp)
81 | .aspectRatio(1f)
82 | .clip(CircleShape)
83 | .border(1.dp, Color.Gray, CircleShape)
84 | )
85 |
86 | Spacer(modifier = modifier.height(16.dp))
87 | Text(text = data.userFullName, fontSize = 16.sp, fontWeight = FontWeight.Bold)
88 | Text(text = data.userName)
89 | Spacer(modifier = modifier.height(16.dp))
90 | Text(text = data.about)
91 |
92 | Spacer(modifier = modifier.height(8.dp))
93 | Spacer(modifier = modifier
94 | .fillMaxWidth()
95 | .height(1.dp)
96 | .background(Color.LightGray))
97 | Spacer(modifier = modifier.height(8.dp))
98 |
99 | Row(modifier = modifier.fillMaxWidth()) {
100 | Column(
101 | modifier = modifier.weight(1f),
102 | horizontalAlignment = Alignment.CenterHorizontally
103 | ) {
104 | Text(text = data.repoCount.toString(), fontSize = 20.sp, fontWeight = FontWeight.Bold)
105 | Text(text = "Repository")
106 | }
107 |
108 | Column(
109 | modifier = modifier.weight(1f),
110 | horizontalAlignment = Alignment.CenterHorizontally
111 | ) {
112 | Text(text = data.followerCount.toString(), fontSize = 20.sp, fontWeight = FontWeight.Bold)
113 | Text(text = "Follower")
114 | }
115 |
116 |
117 | Column(
118 | modifier = modifier.weight(1f),
119 | horizontalAlignment = Alignment.CenterHorizontally
120 | ) {
121 | Text(text = data.followingCount.toString(), fontSize = 20.sp, fontWeight = FontWeight.Bold)
122 | Text(text = "Following")
123 | }
124 | }
125 | }
126 | }
127 |
128 |
--------------------------------------------------------------------------------
/core/designsystem/src/main/java/com/iamkamrul/designsystem/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.designsystem.theme
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.material3.darkColorScheme
6 | import androidx.compose.material3.lightColorScheme
7 |
8 | import android.app.Activity
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.runtime.CompositionLocalProvider
11 | import androidx.compose.runtime.SideEffect
12 | import androidx.compose.ui.graphics.Color.Companion.Red
13 | import androidx.compose.ui.graphics.toArgb
14 | import androidx.compose.ui.platform.LocalView
15 | import androidx.core.view.WindowCompat
16 |
17 | private val darkColorScheme = darkColorScheme(
18 | //The primary color could be used for the app bar, buttons, and other important UI elements.
19 | primary = Orange40,
20 | //Color used for text and icons displayed on top of the primary color.
21 | onPrimary = White,
22 | //The secondary color could be used for tabs, headers, and other secondary UI elements.
23 | //secondary = PurpleGrey40,
24 | //The onSecondary color could be used for text and icons that appear on top of secondary-colored backgrounds.
25 | //onSecondary = White,
26 | //The Background color could be used for backgrounds.
27 | background = Black,
28 | //The onBackground color could be used for text and icons that appear on top of background backgrounds.
29 | onBackground = White,
30 | //The surface color that affect surfaces of components, such as cards, sheets, and menus.
31 | surface = Black10,
32 | onSurface = White,
33 | surfaceVariant = Black10,
34 | onSurfaceVariant = White,
35 | //It refers to the color or style used to represent an error state in the UI. This color is typically applied to components or indicators that highlight invalid input, validation errors, or other error conditions in the application. The error color helps draw attention to and communicate the presence of errors to the user.
36 | error = Red,
37 | // It refers top of error text and icon colors
38 | onError = White,
39 | // It refers to the border or outline around a UI component. The outline is a visual representation that defines the shape and boundaries of the component. It can be used to provide visual emphasis, highlight selected or focused components, or convey interactivity.
40 | //outline = Pink40,
41 | )
42 |
43 | private val lightColorScheme = lightColorScheme(
44 | //The primary color could be used for the app bar, buttons, and other important UI elements.
45 | primary = Orange40,
46 | //Color used for text and icons displayed on top of the primary color.
47 | onPrimary = White,
48 | //The secondary color could be used for tabs, headers, and other secondary UI elements.
49 | //secondary = PurpleGrey40,
50 | //The onSecondary color could be used for text and icons that appear on top of secondary-colored backgrounds.
51 | //onSecondary = White,
52 | //The Background color could be used for backgrounds.
53 | background = White,
54 | //The onBackground color could be used for text and icons that appear on top of background backgrounds.
55 | onBackground = Black,
56 | //The surface color that affect surfaces of components, such as cards, sheets, and menus.
57 | surface = White,
58 | onSurface = Black,
59 | surfaceVariant = White,
60 | onSurfaceVariant = Black,
61 | //It refers to the color or style used to represent an error state in the UI. This color is typically applied to components or indicators that highlight invalid input, validation errors, or other error conditions in the application. The error color helps draw attention to and communicate the presence of errors to the user.
62 | error = Red40,
63 | // It refers top of error text and icon colors
64 | onError = White,
65 | // It refers to the border or outline around a UI component. The outline is a visual representation that defines the shape and boundaries of the component. It can be used to provide visual emphasis, highlight selected or focused components, or convey interactivity.
66 | //outline = Pink40
67 | )
68 |
69 | @Composable
70 | fun JetPackComposeModularizationTheme(
71 | darkTheme: Boolean = isSystemInDarkTheme(),
72 | content: @Composable () -> Unit
73 | ) {
74 | val colorScheme = when {
75 | darkTheme -> darkColorScheme
76 | else -> lightColorScheme
77 | }
78 | val view = LocalView.current
79 | if (!view.isInEditMode) {
80 | SideEffect {
81 | val window = (view.context as Activity).window
82 | val statusBarColor = if(darkTheme) Black10 else White
83 | window.statusBarColor = statusBarColor.toArgb()
84 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
85 | }
86 | }
87 | val appColorScheme = when {
88 | darkTheme -> LocalDarkColorScheme
89 | else -> LocalLightColorScheme
90 | }
91 | CompositionLocalProvider(values = arrayOf(LocalAppColor provides appColorScheme)) {
92 | MaterialTheme(
93 | colorScheme = colorScheme,
94 | typography = Typography,
95 | content = content
96 | )
97 | }
98 |
99 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/features/repolist/src/main/java/com/iamkamrul/repolist/RepoListScreen.kt:
--------------------------------------------------------------------------------
1 | package com.iamkamrul.repolist
2 | import androidx.compose.foundation.Image
3 | import androidx.compose.foundation.clickable
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Box
6 | import androidx.compose.foundation.layout.Column
7 | import androidx.compose.foundation.layout.Row
8 | import androidx.compose.foundation.layout.Spacer
9 | import androidx.compose.foundation.layout.aspectRatio
10 | import androidx.compose.foundation.layout.fillMaxSize
11 | import androidx.compose.foundation.layout.fillMaxWidth
12 | import androidx.compose.foundation.layout.height
13 | import androidx.compose.foundation.layout.padding
14 | import androidx.compose.foundation.layout.size
15 | import androidx.compose.foundation.layout.width
16 | import androidx.compose.foundation.lazy.LazyColumn
17 | import androidx.compose.foundation.lazy.items
18 | import androidx.compose.foundation.shape.CircleShape
19 | import androidx.compose.material.icons.Icons
20 | import androidx.compose.material.icons.outlined.ForkRight
21 | import androidx.compose.material.icons.outlined.Language
22 | import androidx.compose.material.icons.outlined.StarBorder
23 | import androidx.compose.material3.Card
24 | import androidx.compose.material3.CardDefaults
25 | import androidx.compose.material3.CircularProgressIndicator
26 | import androidx.compose.material3.Icon
27 | import androidx.compose.material3.MaterialTheme
28 | import androidx.compose.material3.Text
29 | import androidx.compose.runtime.Composable
30 | import androidx.compose.runtime.getValue
31 | import androidx.compose.ui.Alignment
32 | import androidx.compose.ui.Modifier
33 | import androidx.compose.ui.draw.clip
34 | import androidx.compose.ui.graphics.RectangleShape
35 | import androidx.compose.ui.unit.dp
36 | import androidx.hilt.navigation.compose.hiltViewModel
37 | import androidx.lifecycle.compose.collectAsStateWithLifecycle
38 | import coil.compose.rememberAsyncImagePainter
39 | import com.iamkamrul.designsystem.component.ScaffoldTopAppbar
40 | import com.iamkamrul.designsystem.theme.color
41 | import com.iamkamrul.entity.RepoItemEntity
42 | import com.iamkamrul.ui.component.NetworkErrorMessage
43 |
44 | @Composable
45 | internal fun RepoListRoute(
46 | viewModel:RepoListViewModel = hiltViewModel(),
47 | onRepoItemClick:()->Unit
48 | ){
49 | val repoListUiSate by viewModel.repoListUiState.collectAsStateWithLifecycle()
50 |
51 | RepoListScreen(
52 | repoListUiSate = repoListUiSate,
53 | onRepoItemClick = onRepoItemClick,
54 | onRefreshRepoList = viewModel::handleAction
55 | )
56 | }
57 |
58 | @Composable
59 | fun RepoListScreen(
60 | repoListUiSate: RepoListUiState,
61 | onRepoItemClick:()->Unit,
62 | onRefreshRepoList: (RepoListUiAction) -> Unit
63 | ){
64 | ScaffoldTopAppbar(
65 | title = "Repo List",
66 | containerColor = MaterialTheme.color.secondaryBackground
67 | ) {
68 | val modifier = Modifier.padding(it)
69 | Box(
70 | modifier = modifier.fillMaxSize(),
71 | contentAlignment = Alignment.Center
72 | ) {
73 | when(repoListUiSate){
74 | is RepoListUiState.Error -> {
75 | NetworkErrorMessage(
76 | message = repoListUiSate.message,
77 | onClickRefresh = {
78 | onRefreshRepoList(RepoListUiAction.FetchRepoList)
79 | }
80 | )
81 | }
82 | is RepoListUiState.HasRepoList -> {
83 | LazyColumn{
84 | items(items = repoListUiSate.repoList){repoItem->
85 | RepoListItem(
86 | repoItem = repoItem,
87 | onItemClick = onRepoItemClick
88 | )
89 | }
90 | }
91 |
92 | }
93 | RepoListUiState.Loading -> CircularProgressIndicator()
94 | RepoListUiState.RepoListEmpty -> Text(text = "No Repo List Found")
95 | }
96 | }
97 | }
98 | }
99 |
100 | @Composable
101 | private fun RepoListItem(
102 | modifier: Modifier = Modifier,
103 | repoItem: RepoItemEntity,
104 | onItemClick:()->Unit
105 | ){
106 | Card(
107 | modifier = modifier.padding(bottom = 10.dp).clickable { onItemClick()},
108 | shape = RectangleShape,
109 | colors = CardDefaults.cardColors(containerColor = MaterialTheme.color.white)
110 | ) {
111 | Column(
112 | modifier = modifier.padding(16.dp).fillMaxWidth()
113 | ) {
114 | Row(
115 | verticalAlignment = Alignment.CenterVertically
116 | ) {
117 | Image(
118 | painter = rememberAsyncImagePainter(model = repoItem.userAvatarUrl),
119 | contentDescription = "",
120 | modifier = modifier.size(80.dp)
121 | .aspectRatio(1f)
122 | .clip(CircleShape)
123 | )
124 | Spacer(modifier = modifier.width(16.dp))
125 | Column {
126 | Text(text = repoItem.repoName,style = MaterialTheme.typography.titleMedium)
127 | Text(text = repoItem.userName,style = MaterialTheme.typography.bodyLarge)
128 | }
129 | }
130 |
131 | Spacer(modifier = modifier.height(16.dp))
132 | Text(text = repoItem.repoFullName,style = MaterialTheme.typography.bodyMedium)
133 | Text(text = repoItem.repoDescription, style = MaterialTheme.typography.bodyMedium)
134 |
135 | Spacer(modifier = modifier.height(16.dp))
136 | Row {
137 | Row(
138 | modifier = modifier.weight(1f),
139 | verticalAlignment = Alignment.CenterVertically
140 | ) {
141 | Icon(
142 | imageVector = Icons.Outlined.Language,
143 | contentDescription = ""
144 | )
145 | Spacer(modifier = modifier.width(4.dp))
146 | Text(text = repoItem.language,style = MaterialTheme.typography.labelLarge)
147 | }
148 | Row(
149 | modifier = modifier.weight(1f),
150 | horizontalArrangement = Arrangement.Center,
151 | verticalAlignment = Alignment.CenterVertically
152 | ) {
153 | Icon(imageVector = Icons.Outlined.StarBorder, contentDescription = "")
154 | Spacer(modifier = modifier.width(4.dp))
155 | Text(text = "${repoItem.stargazers_count} Star", style = MaterialTheme.typography.labelLarge)
156 | }
157 | Row(
158 | modifier = modifier.weight(1f),
159 | horizontalArrangement = Arrangement.End,
160 | verticalAlignment = Alignment.CenterVertically
161 | ) {
162 | Icon(imageVector = Icons.Outlined.ForkRight, contentDescription = "")
163 | Spacer(modifier = modifier.width(4.dp))
164 | Text(text = "${repoItem.forksCount} Forked", style = MaterialTheme.typography.labelLarge)
165 | }
166 | }
167 | }
168 | }
169 | }
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | agp = "8.7.2"
3 | kotlin = "2.0.20"
4 | secrets = "2.0.1"
5 | ksp = "2.0.20-1.0.25"
6 | hilt = "2.49"
7 | gms = "4.4.2"
8 | firebasecrashlyticsplugin = "3.0.2"
9 |
10 | #Android Compose Dep Version
11 | composeActivity = "1.9.3"
12 | composeBom = "2024.11.00"
13 | composeCompiler = "1.5.4"
14 | composeNavigation = "2.8.4"
15 | composeFoundation = "1.5.1"
16 | accompanist = "0.30.1"
17 | constraintlayoutCompose = "1.1.0"
18 | composeHiltNavigation = "1.2.0"
19 |
20 | #Android MDC Core Dep Version
21 | androidxAppcompat = "1.7.0"
22 | androidxCoreKtx = "1.15.0"
23 | material = "1.12.0"
24 | recyclerview = "1.3.2"
25 | activity = "1.9.3"
26 | fragment = "1.8.5"
27 | constraintLayout = "2.2.0"
28 | cardview = "1.0.0"
29 | browser ="1.7.0"
30 | swiperefreshlayout = "1.1.0"
31 | navigationComponent = "2.8.4"
32 | navigationComponentHilt = "1.1.0"
33 | lifecycle = "2.8.7"
34 | preference = "1.2.1"
35 |
36 | retrofit2 = "2.9.0"
37 | okhHttp3 = "4.12.0"
38 | gson = "2.10.1"
39 | rxjava3 = "3.1.7"
40 | rxjava3Android = "3.0.2"
41 | lottie = "6.1.0"
42 | timber = "5.0.1"
43 | dateced = "1.1.1"
44 | alerter = "7.2.4"
45 | photoview = "2.3.0"
46 | circleimage = "3.1.0"
47 | coil = "2.5.0"
48 | dotsindicator = "4.3"
49 | appupdate = "2.1.0"
50 | facebook = "latest.release"
51 | maps = "19.0.0"
52 | mapsLocation = "21.3.0"
53 | mapsPlaces = "4.1.0"
54 | firebase = "33.6.0"
55 | room = "2.6.1"
56 | playstorereview = "2.0.2"
57 | desugar = "2.1.3"
58 | kotlinxCoroutines = "1.9.0"
59 | viewstate = "1.1.1"
60 | customview = "1.1.2"
61 | customTabs ="3.0.3"
62 | sdp = "1.1.0"
63 | junit = "4.13.2"
64 | extjunit = "1.2.1"
65 | espresso = "3.6.1"
66 | org-jetbrains-kotlin-android = "2.0.20"
67 |
68 | [bundles]
69 | network = ["retrofit2.core","retrofit2.rx3adapter","retrofit2.gsonconverter","gson","okhHttp3.core","okhHttp3.interceptor","kotlinx-coroutines-android"]
70 | rxJava3 = ["rxjava3.core","rxjava3.android"]
71 |
72 | [libraries]
73 | androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
74 | androidx-compose-ui = { group = "androidx.compose.ui", name = "ui"}
75 | androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
76 | androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
77 | androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
78 | androidx-compose-ui-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
79 | androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
80 | androidx-compose-material-iconsExtended= { group = "androidx.compose.material", name = "material-icons-extended"}
81 | androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation"}
82 | androidx-compose-activity = {group = "androidx.activity", name="activity-compose", version.ref="composeActivity"}
83 | androidx-compose-navigation = {group = "androidx.navigation", name="navigation-compose", version.ref="composeNavigation"}
84 | androidx-compose-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout-compose", version.ref = "constraintlayoutCompose" }
85 | androidx-compose-hilt-navigation = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "composeHiltNavigation" }
86 |
87 | androidx-appcompat = {group = "androidx.appcompat", name = "appcompat", version.ref="androidxAppcompat"}
88 | androidx-corektx = {group = "androidx.core", name = "core-ktx", version.ref="androidxCoreKtx"}
89 | androidx-constraintlayout = {group = "androidx.constraintlayout", name = "constraintlayout", version.ref="constraintLayout"}
90 | androidx-material = {group = "com.google.android.material", name = "material", version.ref="material"}
91 | androidx-fragment = {group = "androidx.fragment", name = "fragment-ktx", version.ref="fragment"}
92 | androidx-activity = {group = "androidx.activity", name = "activity-ktx", version.ref="activity"}
93 | androidx-cardview = {group = "androidx.cardview", name = "cardview", version.ref="cardview"}
94 | androidx-browser = {group = "androidx.browser", name = "browser", version.ref="browser"}
95 | androidx-recyclerview = {group = "androidx.recyclerview", name = "recyclerview", version.ref="recyclerview"}
96 | androidx-preference = {group = "androidx.preference", name = "preference", version.ref="preference"}
97 | androidx-swiperefreshlayout = {group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref="swiperefreshlayout"}
98 |
99 | androidx-lifecycle-viewmodel-ktx = {group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref="lifecycle"}
100 | androidx-lifecycle-viewmodel-savedstate = {group = "androidx.lifecycle", name = "lifecycle-viewmodel-savedstate", version.ref="lifecycle"}
101 | androidx-lifecycle-livedata = {group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref="lifecycle"}
102 | androidx-lifecycle-runtime = {group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref="lifecycle"}
103 | androidx-lifecycle-commonjava8 = {group = "androidx.lifecycle", name = "lifecycle-common-java8", version.ref="lifecycle"}
104 | androidx-lifecycle-service = {group = "androidx.lifecycle", name = "lifecycle-service", version.ref="lifecycle"}
105 | androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
106 | androidx-lifecycle-runtimeCompose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" }
107 |
108 | desugar = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugar" }
109 |
110 | androidx-navigation-ktx = {group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref="navigationComponent"}
111 | androidx-navigation-ui = {group = "androidx.navigation", name = "navigation-ui-ktx", version.ref="navigationComponent"}
112 |
113 | hilt-android = {group = "com.google.dagger", name = "hilt-android", version.ref="hilt"}
114 | hilt-compiler = {group = "com.google.dagger", name = "hilt-android-compiler", version.ref="hilt"}
115 |
116 | retrofit2-core = {group = "com.squareup.retrofit2", name = "retrofit", version.ref="retrofit2"}
117 | retrofit2-rx3adapter = {group = "com.squareup.retrofit2", name = "adapter-rxjava3", version.ref="retrofit2"}
118 | retrofit2-gsonconverter = {group = "com.squareup.retrofit2", name = "converter-gson", version.ref="retrofit2"}
119 | gson = {group = "com.google.code.gson", name = "gson", version.ref="gson"}
120 | kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }
121 |
122 | alerter-tapadoo = { group = "com.github.tapadoo", name = "alerter", version.ref = "alerter"}
123 |
124 | okhHttp3-core = {group = "com.squareup.okhttp3", name = "okhttp", version.ref="okhHttp3"}
125 | okhHttp3-interceptor = {group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref="okhHttp3"}
126 |
127 | rxjava3-core = {group = "io.reactivex.rxjava3", name = "rxjava", version.ref="rxjava3"}
128 | rxjava3-android = {group = "io.reactivex.rxjava3", name = "rxandroid", version.ref="rxjava3Android"}
129 |
130 | image-photoviewzoom = {group = "com.github.chrisbanes", name = "PhotoView", version.ref="photoview"}
131 | image-circleimage = {group = "de.hdodenhof", name = "circleimageview", version.ref="circleimage"}
132 | image-coil-compose = {group = "io.coil-kt", name = "coil-compose", version.ref="coil"}
133 | image-coil = {group = "io.coil-kt", name = "coil", version.ref="coil"}
134 | indicator-dot = {group = "com.tbuonomo", name = "dotsindicator", version.ref="dotsindicator"}
135 |
136 | log-timber = {group = "com.jakewharton.timber", name = "timber", version.ref="timber"}
137 | animation-lottie = {group = "com.airbnb.android", name = "lottie", version.ref="lottie"}
138 | dimension-sdp = {group = "com.intuit.sdp", name = "sdp-android", version.ref="sdp"}
139 | dimension-ssp = {group = "com.intuit.ssp", name = "ssp-android", version.ref="sdp"}
140 |
141 | kamrul3288-viewstate = {group = "com.github.kamrul3288", name = "viewstatelayout", version.ref="viewstate"}
142 | kamrul3288-customview = {group = "com.github.kamrul3288", name = "customview-android", version.ref="customview"}
143 | kamrul3288-dateced = {group = "com.github.kamrul3288", name = "dateced", version.ref="dateced"}
144 |
145 | appupdate-core = {group = "com.google.android.play", name = "app-update", version.ref="appupdate"}
146 | appupdate-ktx = {group = "com.google.android.play", name = "app-update-ktx", version.ref="appupdate"}
147 |
148 | review-core = {group = "com.google.android.play", name = "review", version.ref="playstorereview"}
149 | review-ktx = {group = "com.google.android.play", name = "review-ktx", version.ref="playstorereview"}
150 |
151 | room-ktx = {group = "androidx.room", name = "room-ktx", version.ref="room"}
152 | room-runtime = {group = "androidx.room", name = "room-runtime", version.ref="room"}
153 | room-compiler = {group = "androidx.room", name = "room-compiler", version.ref="room"}
154 | room-common = {group = "androidx.room", name = "room-common", version.ref="room"}
155 |
156 | firebase-bom = {group = "com.google.firebase", name = "firebase-bom", version.ref="firebase"}
157 | firebase-messaging = {group = "com.google.firebase", name = "firebase-messaging-ktx"}
158 | firebase-analytics = {group = "com.google.firebase", name = "firebase-analytics-ktx"}
159 | firebase-crashlytics = {group = "com.google.firebase", name = "firebase-crashlytics-ktx"}
160 |
161 | maps-sdk = {group = "com.google.android.gms", name = "play-services-maps", version.ref="maps"}
162 | maps-location = {group = "com.google.android.gms", name = "play-services-location", version.ref="mapsLocation"}
163 | maps-places = {group = "com.google.android.libraries.places", name = "places", version.ref="mapsPlaces"}
164 |
165 | facebook-sdk = {group = "com.facebook.android", name = "facebook-android-sdk", version.ref="facebook"}
166 | facebook-marketing = {group = "com.facebook.android", name = "facebook-marketing", version.ref="facebook"}
167 |
168 | customtabs = {group = "de.peilicke.sascha", name = "android-customtabs", version.ref="customTabs"}
169 | test-junit = {group = "junit", name = "junit", version.ref="junit"}
170 | test-extjunit = {group = "androidx.test.ext", name = "junit", version.ref="extjunit"}
171 | test-espresso = {group = "androidx.test.espresso", name = "espresso-core", version.ref="espresso"}
172 | test-compose-ui-junit = { group = "androidx.compose.ui", name = "ui-test-junit4"}
173 |
174 | # Dependencies of the included build-logic
175 | android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "agp" }
176 | kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
177 | firebase-crashlytics-gradlePlugin = { group = "com.google.firebase", name = "firebase-crashlytics-gradle", version.ref = "firebasecrashlyticsplugin" }
178 | ksp-gradlePlugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
179 |
180 | [plugins]
181 | com-android-application = { id = "com.android.application", version.ref = "agp" }
182 | com-android-library = { id = "com.android.library", version.ref = "agp" }
183 | com-google-firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebasecrashlyticsplugin" }
184 | com-google-gms-google-services = { id = "com.google.gms.google-services", version.ref = "gms" }
185 | com-google-dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
186 | org-jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
187 | org-jetbrains-kotlin-plugin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
188 | androidx-navigation-safeargs = { id = "androidx.navigation.safeargs", version.ref = "navigationComponent" }
189 | com-google-devtools-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
190 | secrets-gradle-plugin = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" }
191 | org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "org-jetbrains-kotlin-android" }
192 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
193 |
194 | # Plugins defined by this project
195 | iamkamrul-android-application = { id = "iamkamrul.android.application", version = "unspecified" }
196 | iamkamrul-android-application-compose = { id = "iamkamrul.android.application.compose", version = "unspecified" }
197 | iamkamrul-android-application-firebase = { id = "iamkamrul.android.application.firebase", version = "unspecified" }
198 | iamkamrul-android-hilt = { id = "iamkamrul.android.hilt", version = "unspecified" }
199 | iamkamrul-android-library = { id = "iamkamrul.android.library", version = "unspecified" }
200 | iamkamrul-android-library-compose = { id = "iamkamrul.android.library.compose", version = "unspecified" }
201 | iamkamrul-android-feature-compose = { id = "iamkamrul.android.feature.compose", version = "unspecified" }
202 | iamkamrul-android-room = { id = "iamkamrul.android.room", version = "unspecified" }
203 | iamkamrul-android-retrofit = { id = "iamkamrul.android.retrofit", version = "unspecified" }
204 | iamkamrul-jvm-library = { id = "iamkamrul.jvm.library", version = "unspecified" }
205 |
206 |
--------------------------------------------------------------------------------