├── .gitignore
├── .idea
├── checkstyle-idea.xml
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── gradle.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── build.gradle
├── buildSrc
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── dependencies
│ ├── Config.kt
│ ├── Dependencies.kt
│ └── Versions.kt
├── core
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── ahmedadel
│ │ └── robustandroid
│ │ └── core
│ │ ├── analytics
│ │ ├── AnalyticsManager.kt
│ │ └── di
│ │ │ └── AnalyticsModule.kt
│ │ ├── application
│ │ └── di
│ │ │ └── ApplicationModule.kt
│ │ ├── di
│ │ ├── CoreComponent.kt
│ │ ├── CoreComponentWrapper.kt
│ │ ├── FeatureScope.kt
│ │ ├── PresentationScope.kt
│ │ ├── ViewModelFactoryModule.kt
│ │ ├── ViewModelKey.kt
│ │ └── scheduler
│ │ │ ├── BaseSchedulerProvider.kt
│ │ │ ├── BaseSchedulerProviderModule.kt
│ │ │ ├── SchedulerProvider.kt
│ │ │ └── TrampolineSchedulerProvider.kt
│ │ └── sharedpreferences
│ │ ├── SharedPreferencesWrapper.kt
│ │ └── di
│ │ └── SharedPreferencesModule.kt
│ └── res
│ └── values
│ └── strings.xml
├── datalayer
├── datasource
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── ahmedadel
│ │ │ └── robustandroid
│ │ │ └── datalayer
│ │ │ └── datasource
│ │ │ ├── both
│ │ │ └── di
│ │ │ │ ├── DataSourceComponent.kt
│ │ │ │ ├── DataSourceComponentWrapper.kt
│ │ │ │ └── DataSourceScope.kt
│ │ │ ├── local
│ │ │ └── di
│ │ │ │ ├── LocalComponent.kt
│ │ │ │ ├── LocalComponentWrapper.kt
│ │ │ │ └── LocalModule.kt
│ │ │ └── remote
│ │ │ └── di
│ │ │ ├── RemoteComponent.kt
│ │ │ ├── RemoteComponentWrapper.kt
│ │ │ └── RemoteModule.kt
│ │ └── res
│ │ └── values
│ │ └── strings.xml
├── local
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── ahmedadel
│ │ │ │ └── robustandroid
│ │ │ │ └── datalayer
│ │ │ │ └── local
│ │ │ │ ├── DatabaseManager.kt
│ │ │ │ └── dao
│ │ │ │ ├── movie
│ │ │ │ └── MovieDao.kt
│ │ │ │ ├── person
│ │ │ │ └── PersonDao.kt
│ │ │ │ └── tv
│ │ │ │ └── TVDao.kt
│ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── ahmedadel
│ │ └── robustandroid
│ │ └── datalayer
│ │ └── local
│ │ └── ExampleUnitTest.java
└── remote
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── ahmedadel
│ │ └── robustandroid
│ │ └── datalayer
│ │ └── remote
│ │ ├── ApiKeyInterceptor.kt
│ │ └── ApiService.kt
│ └── res
│ └── values
│ └── strings.xml
├── feature
├── movie
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── ahmedadel
│ │ │ │ └── robustandroid
│ │ │ │ └── feature
│ │ │ │ └── movie
│ │ │ │ ├── di
│ │ │ │ ├── MovieComponent.kt
│ │ │ │ ├── MovieComponentWrapper.kt
│ │ │ │ └── MovieModule.kt
│ │ │ │ ├── entity
│ │ │ │ └── MovieEntity.kt
│ │ │ │ ├── mapper
│ │ │ │ └── MovieMapper.kt
│ │ │ │ ├── repository
│ │ │ │ └── MovieRepository.kt
│ │ │ │ └── usecase
│ │ │ │ └── MovieUseCase.kt
│ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── ahmedadel
│ │ └── robustandroid
│ │ └── feature
│ │ └── movie
│ │ └── ExampleUnitTest.java
├── person
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── ahmedadel
│ │ │ │ └── robustandroid
│ │ │ │ └── feature
│ │ │ │ └── person
│ │ │ │ ├── di
│ │ │ │ ├── PersonComponent.kt
│ │ │ │ ├── PersonComponentWrapper.kt
│ │ │ │ └── PersonModule.kt
│ │ │ │ ├── entity
│ │ │ │ └── PersonEntity.kt
│ │ │ │ ├── mapper
│ │ │ │ └── PersonMapper.kt
│ │ │ │ ├── repository
│ │ │ │ └── PersonRepository.kt
│ │ │ │ └── usecase
│ │ │ │ └── PersonUseCase.kt
│ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── ahmedadel
│ │ └── robustandroid
│ │ └── feature
│ │ └── person
│ │ └── ExampleUnitTest.java
└── tv
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── ahmedadel
│ │ │ └── robustandroid
│ │ │ └── feature
│ │ │ └── tv
│ │ │ ├── di
│ │ │ ├── TVComponent.kt
│ │ │ ├── TVComponentWrapper.kt
│ │ │ └── TVModule.kt
│ │ │ ├── entity
│ │ │ └── TVEntity.kt
│ │ │ ├── mapper
│ │ │ └── TVMapper.kt
│ │ │ ├── repository
│ │ │ └── TVRepository.kt
│ │ │ └── usecase
│ │ │ └── TVUseCase.kt
│ └── res
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── com
│ └── ahmedadel
│ └── robustandroid
│ └── feature
│ └── tv
│ └── ExampleUnitTest.java
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── models
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── ahmedadel
│ │ └── robustandroid
│ │ └── models
│ │ ├── EntityModel.kt
│ │ ├── UiModel.kt
│ │ ├── local
│ │ ├── movie
│ │ │ └── MovieLocal.kt
│ │ ├── person
│ │ │ └── PersonLocal.kt
│ │ └── tv
│ │ │ └── TVLocal.kt
│ │ ├── mappers
│ │ ├── MapFromEntityToUi.kt
│ │ └── MapFromRemoteToEntity.kt
│ │ └── remote
│ │ ├── movie
│ │ ├── MovieListRemote.kt
│ │ └── MovieRemote.kt
│ │ ├── person
│ │ ├── PersonListRemote.kt
│ │ └── PersonRemote.kt
│ │ └── tv
│ │ ├── TVListRemote.kt
│ │ └── TVRemote.kt
│ └── res
│ └── values
│ └── strings.xml
├── presentation
├── mvi
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── ahmedadel
│ │ │ │ └── robustandroid
│ │ │ │ └── presentation
│ │ │ │ └── mvi
│ │ │ │ ├── MviAction.kt
│ │ │ │ ├── MviIntent.kt
│ │ │ │ ├── MviProcessor.kt
│ │ │ │ ├── MviResult.kt
│ │ │ │ ├── MviView.kt
│ │ │ │ ├── MviViewModel.kt
│ │ │ │ ├── MviViewState.kt
│ │ │ │ ├── movie
│ │ │ │ ├── MovieDetails.kt
│ │ │ │ ├── MovieDetailsProcessor.kt
│ │ │ │ ├── MovieDetailsViewModel.kt
│ │ │ │ ├── di
│ │ │ │ │ ├── MovieDetailsComponent.kt
│ │ │ │ │ ├── MovieDetailsComponentWrapper.kt
│ │ │ │ │ ├── MovieDetailsModule.kt
│ │ │ │ │ └── MovieDetailsViewModelModule.kt
│ │ │ │ ├── mapper
│ │ │ │ │ └── MovieMapper.kt
│ │ │ │ └── uimodel
│ │ │ │ │ └── MovieUiModel.kt
│ │ │ │ └── tv
│ │ │ │ ├── TVDetails.kt
│ │ │ │ ├── TVDetailsProcessor.kt
│ │ │ │ ├── TVDetailsViewModel.kt
│ │ │ │ ├── di
│ │ │ │ ├── TVDetailsComponent.kt
│ │ │ │ ├── TVDetailsComponentWrapper.kt
│ │ │ │ ├── TVDetailsModule.kt
│ │ │ │ └── TVDetailsViewModelModule.kt
│ │ │ │ ├── mapper
│ │ │ │ └── TVMapper.kt
│ │ │ │ └── uimodel
│ │ │ │ └── TVUiModel.kt
│ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── ahmedadel
│ │ └── robustandroid
│ │ └── presentation
│ │ └── mvi
│ │ └── ExampleUnitTest.java
├── mvp
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── ahmedadel
│ │ │ │ └── robustandroid
│ │ │ │ └── presentation
│ │ │ │ └── mvp
│ │ │ │ ├── BasePresenter.kt
│ │ │ │ ├── BaseView.kt
│ │ │ │ ├── movielist
│ │ │ │ ├── MovieListContract.kt
│ │ │ │ ├── MovieListModel.kt
│ │ │ │ ├── MovieListPresenter.kt
│ │ │ │ ├── di
│ │ │ │ │ ├── MovieListComponent.kt
│ │ │ │ │ ├── MovieListComponentWrapper.kt
│ │ │ │ │ └── MovieListModule.kt
│ │ │ │ ├── mapper
│ │ │ │ │ └── MovieMapper.kt
│ │ │ │ └── uimodel
│ │ │ │ │ └── MovieUiModel.kt
│ │ │ │ ├── personlist
│ │ │ │ ├── PersonListContract.kt
│ │ │ │ ├── PersonListModel.kt
│ │ │ │ ├── PersonListPresenter.kt
│ │ │ │ ├── di
│ │ │ │ │ ├── PersonListComponent.kt
│ │ │ │ │ ├── PersonListComponentWrapper.kt
│ │ │ │ │ └── PersonListModule.kt
│ │ │ │ ├── mapper
│ │ │ │ │ └── PersonMapper.kt
│ │ │ │ └── uimodel
│ │ │ │ │ └── PersonUiModel.kt
│ │ │ │ └── tvlist
│ │ │ │ ├── TVListContract.kt
│ │ │ │ ├── TVListModel.kt
│ │ │ │ ├── TVListPresenter.kt
│ │ │ │ ├── di
│ │ │ │ ├── TVListComponent.kt
│ │ │ │ ├── TVListComponentWrapper.kt
│ │ │ │ └── TVListModule.kt
│ │ │ │ ├── mapper
│ │ │ │ └── TVMapper.kt
│ │ │ │ └── uimodel
│ │ │ │ └── TVUiModel.kt
│ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── ahmedadel
│ │ └── robustandroid
│ │ └── presentation
│ │ └── mvp
│ │ └── ExampleUnitTest.java
└── mvvm
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── ahmedadel
│ │ │ └── robustandroid
│ │ │ └── presentation
│ │ │ └── mvvm
│ │ │ ├── BaseViewModel.kt
│ │ │ ├── ViewState.kt
│ │ │ └── home
│ │ │ ├── HomeViewModel.kt
│ │ │ ├── di
│ │ │ ├── HomeMappersModule.kt
│ │ │ ├── HomeUseCasesComponent.kt
│ │ │ ├── HomeViewModelComponent.kt
│ │ │ ├── HomeViewModelComponentWrapper.kt
│ │ │ └── HomeViewModelModule.kt
│ │ │ ├── mapper
│ │ │ ├── MovieMapper.kt
│ │ │ ├── PersonMapper.kt
│ │ │ └── TVMapper.kt
│ │ │ └── uimodel
│ │ │ ├── MovieUiModel.kt
│ │ │ ├── PersonUiModel.kt
│ │ │ └── TVUiModel.kt
│ └── res
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── com
│ └── ahmedadel
│ └── robustandroid
│ └── presentation
│ └── mvvm
│ └── ExampleUnitTest.java
├── settings.gradle
└── ui
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── com
│ └── ahmedadel
│ └── robustandroid
│ └── ExampleInstrumentedTest.kt
├── main
├── AndroidManifest.xml
├── java
│ └── com
│ │ └── ahmedadel
│ │ └── robustandroid
│ │ ├── BaseActivity.kt
│ │ ├── MainActivity.kt
│ │ ├── di
│ │ └── ActivityScope.kt
│ │ ├── home
│ │ ├── HomeActivity.kt
│ │ ├── adapter
│ │ │ ├── HomeMovieAdapter.kt
│ │ │ ├── HomePersonAdapter.kt
│ │ │ └── HomeTVAdapter.kt
│ │ └── di
│ │ │ ├── HomeActivityComponent.kt
│ │ │ ├── HomeActivityComponentWrapper.kt
│ │ │ └── HomeActivityModule.kt
│ │ ├── moviedetails
│ │ ├── MovieDetailsActivity.kt
│ │ ├── adapter
│ │ │ ├── RecommendationMoviesAdapter.kt
│ │ │ └── SimilarMoviesAdapter.kt
│ │ └── di
│ │ │ ├── MovieDetailsActivityComponent.kt
│ │ │ ├── MovieDetailsActivityComponentWrapper.kt
│ │ │ └── MovieDetailsActivityModule.kt
│ │ ├── movielist
│ │ ├── MovieListActivity.kt
│ │ ├── adapter
│ │ │ └── MovieListAdapter.kt
│ │ └── di
│ │ │ ├── MovieListActivityComponent.kt
│ │ │ ├── MovieListActivityComponentWrapper.kt
│ │ │ └── MovieListActivityModule.kt
│ │ ├── personlist
│ │ ├── PersonListActivity.kt
│ │ ├── adapter
│ │ │ └── PersonListAdapter.kt
│ │ └── di
│ │ │ ├── PersonListActivityComponent.kt
│ │ │ ├── PersonListActivityComponentWrapper.kt
│ │ │ └── PersonListActivityModule.kt
│ │ ├── tvdetails
│ │ ├── TVDetailsActivity.kt
│ │ └── di
│ │ │ ├── TVDetailsActivityComponent.kt
│ │ │ └── TVDetailsActivityComponentWrapper.kt
│ │ ├── tvlist
│ │ ├── TVListActivity.kt
│ │ ├── adapter
│ │ │ └── TVListAdapter.kt
│ │ └── di
│ │ │ ├── TVListActivityComponent.kt
│ │ │ ├── TVListActivityComponentWrapper.kt
│ │ │ └── TVListActivityModule.kt
│ │ └── widget
│ │ └── EndlessRecyclerViewScrollListener.kt
├── res-screens
│ ├── home
│ │ └── layout
│ │ │ ├── activity_home.xml
│ │ │ ├── movie_list_item.xml
│ │ │ ├── person_list_item.xml
│ │ │ └── tv_list_item.xml
│ ├── movie-details
│ │ └── layout
│ │ │ └── activity_movie_details.xml
│ ├── movie-list
│ │ └── layout
│ │ │ ├── activity_movie_list.xml
│ │ │ └── activity_movie_list_item.xml
│ ├── person-list
│ │ └── layout
│ │ │ ├── activity_person_list.xml
│ │ │ └── activity_person_list_item.xml
│ ├── tv-details
│ │ └── layout
│ │ │ └── activity_tv_details.xml
│ └── tv-list
│ │ └── layout
│ │ ├── activity_tv_list.xml
│ │ └── activity_tv_list_item.xml
└── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ ├── ic_launcher_background.xml
│ └── tmdb.png
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
│ └── xml
│ └── backup_descriptor.xml
└── test
└── java
└── com
└── ahmedadel
└── robustandroid
└── ExampleUnitTest.kt
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 |
--------------------------------------------------------------------------------
/.idea/checkstyle-idea.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
32 |
33 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | import dependencies.*
3 |
4 | buildscript {
5 |
6 | repositories {
7 | jcenter()
8 | google()
9 | mavenCentral()
10 | }
11 |
12 | dependencies {
13 |
14 | classpath Dependencies.AndroidGradlePlugin
15 | classpath Dependencies.KotlinPlugin
16 | // NOTE: Do not place your application dependencies here; they belong
17 | // in the individual module build.gradle files
18 |
19 | }
20 | }
21 |
22 | allprojects {
23 | repositories {
24 | jcenter()
25 | google()
26 | mavenCentral()
27 | }
28 | }
29 |
30 | task clean(type: Delete) {
31 | delete rootProject.buildDir
32 | }
33 |
--------------------------------------------------------------------------------
/buildSrc/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | }
4 | repositories {
5 | jcenter()
6 | google()
7 | mavenCentral()
8 | }
9 |
--------------------------------------------------------------------------------
/buildSrc/src/main/java/dependencies/Config.kt:
--------------------------------------------------------------------------------
1 | package dependencies
2 |
3 | /**
4 | * Created at Tito on 3/13/19
5 | *
6 | * Class that contains common configurations used allover the application like staging and
7 | * productions base apis.
8 | *
9 | */
10 |
11 | @Suppress("unused")
12 | object Config {
13 | const val PROD_API_BASE_URL = "https://api.themoviedb.org/"
14 | const val STAGING_API_BASE_URL = "https://api.themoviedb.org/"
15 | const val API_KEY = "fc47660226072874be57974ff797a0cd"
16 | const val IMAGE_URL = "https://image.tmdb.org/t/p/w200/"
17 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/dependencies/Versions.kt:
--------------------------------------------------------------------------------
1 | package dependencies
2 |
3 | /**
4 | * Created at Tito on 3/13/19
5 | *
6 | * Version that will be used in Dependencies kotlin file.
7 | */
8 |
9 | @Suppress("unused")
10 | object Versions {
11 |
12 | const val versionName = "1.0"
13 | const val versionCode = 1
14 |
15 | const val compileSdkVersion = 28
16 | const val minSdkVersion = 17
17 | const val targetSdkVersion = 28
18 |
19 | const val androidGradlePlugin = "3.3.2"
20 |
21 | object AndroidX {
22 | const val main = "1.0.0"
23 | const val material = "1.0.0"
24 | const val multiDex = "2.0.0"
25 | const val constraintLayout = "1.1.3"
26 | const val androidArc = "2.0.0"
27 | }
28 |
29 | object Testing {
30 | const val mockito = "2.10.0"
31 | const val espresso = "3.1.2-alpha01"
32 | const val runner = "1.1.2-alpha01"
33 | const val junit = "4.12"
34 | const val junitPlatform = "1.0.0"
35 | }
36 |
37 | object Kotlin {
38 | const val std = "1.3.21"
39 | }
40 |
41 | object Google {
42 | const val playServices = "12.0.1"
43 | const val firebase = "12.0.1"
44 | const val dagger = "2.16"
45 | }
46 |
47 | object RX {
48 | const val rxAndroid = "2.0.1"
49 | const val rxJava = "2.1.9"
50 | const val rxRelay = "2.0.0"
51 | const val rxIdler = "0.9.0"
52 | }
53 |
54 | object Retrofit {
55 | const val retrofit = "2.3.0"
56 | const val okHttp = "3.11.0"
57 | }
58 |
59 | const val OK_LOG = "2.3.0"
60 |
61 | const val MOSHI = "1.4.0"
62 |
63 | const val BUTTER_KNIFE = "9.0.0-SNAPSHOT"
64 |
65 | const val LEAK_CANARY = "1.5.4"
66 |
67 | const val GLIDE = "4.8.0"
68 |
69 | }
--------------------------------------------------------------------------------
/core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/core/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.*
2 |
3 | apply {
4 | plugin("com.android.library")
5 | plugin("kotlin-android-extensions")
6 | plugin("kotlin-android")
7 | plugin("kotlin-kapt")
8 | }
9 |
10 | android {
11 | compileSdkVersion Versions.compileSdkVersion
12 |
13 | defaultConfig {
14 |
15 | minSdkVersion Versions.minSdkVersion
16 | targetSdkVersion Versions.targetSdkVersion
17 |
18 | versionCode Versions.versionCode
19 | versionName Versions.versionName
20 |
21 | }
22 |
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 |
30 | }
31 |
32 | dependencies {
33 |
34 | implementation fileTree(dir: 'libs', include: ['*.jar'])
35 |
36 | implementation Dependencies.AppCompact
37 |
38 | implementation Dependencies.KotlinStdLib
39 |
40 | implementation Dependencies.Dagger
41 | kapt Dependencies.DaggerKapt
42 |
43 | implementation Dependencies.RxJava
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/core/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
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
22 |
--------------------------------------------------------------------------------
/core/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/analytics/AnalyticsManager.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.analytics
2 |
3 | import android.app.Application
4 | import android.util.Log
5 |
6 | /**
7 | * Created at Tito on 3/13/19
8 | *
9 | * Dummy class as a simulation for analytics part.
10 | */
11 |
12 | @Suppress("unused")
13 | class AnalyticsManager(private val application: Application) {
14 |
15 | fun logScreenView(screenName: String) {
16 | Log.d("Teamwork Analytics", "Logged screen name: $screenName")
17 | }
18 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/analytics/di/AnalyticsModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.analytics.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.core.analytics.AnalyticsManager
5 | import dagger.Module
6 | import dagger.Provides
7 | import javax.inject.Singleton
8 |
9 | /**
10 | * Created at Tito on 3/13/19
11 | *
12 | * Dagger Module that provides AnalyticsManager.
13 | */
14 |
15 | @Module
16 | class AnalyticsModule(private val application: Application) {
17 |
18 | @Provides
19 | @Singleton
20 | internal fun provideAnalyticsManager(): AnalyticsManager = AnalyticsManager(application)
21 |
22 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/application/di/ApplicationModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.application.di
2 |
3 | import android.app.Application
4 | import dagger.Module
5 | import dagger.Provides
6 | import javax.inject.Singleton
7 |
8 | /**
9 | * Created at Tito on 3/13/19
10 | *
11 | * Dagger Module that provides Application class.
12 | */
13 |
14 | @Module
15 | class ApplicationModule(private val application: Application) {
16 |
17 | @Provides
18 | @Singleton
19 | fun provideApplication(): Application = application
20 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/di/CoreComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.di
2 |
3 | import android.app.Application
4 | import android.content.SharedPreferences
5 | import com.ahmedadel.robustandroid.core.analytics.AnalyticsManager
6 | import com.ahmedadel.robustandroid.core.analytics.di.AnalyticsModule
7 | import com.ahmedadel.robustandroid.core.application.di.ApplicationModule
8 | import com.ahmedadel.robustandroid.core.sharedpreferences.di.SharedPreferencesModule
9 | import dagger.Component
10 | import javax.inject.Singleton
11 |
12 | /**
13 | * Created at Tito on 3/13/19
14 | *
15 | * This component makes dagger implements the initialization of mentioned modules.
16 | */
17 |
18 | @Singleton
19 | @Component(
20 | modules = [
21 | ApplicationModule::class,
22 | SharedPreferencesModule::class,
23 | AnalyticsModule::class
24 | ]
25 | )
26 | interface CoreComponent {
27 |
28 | fun application(): Application
29 |
30 | fun sharedPreferences(): SharedPreferences
31 |
32 | fun analyticsManager(): AnalyticsManager
33 |
34 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/di/CoreComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.core.analytics.di.AnalyticsModule
5 | import com.ahmedadel.robustandroid.core.application.di.ApplicationModule
6 | import com.ahmedadel.robustandroid.core.sharedpreferences.di.SharedPreferencesModule
7 |
8 | /**
9 | * Created at Tito on 3/13/19
10 | *
11 | * Wrapper core component class that will be used to initialize the desired modules from core android module
12 | */
13 |
14 | open class CoreComponentWrapper private constructor() {
15 |
16 | private lateinit var component: CoreComponent
17 |
18 | private fun initializeComponent(application: Application) {
19 | component = DaggerCoreComponent.builder()
20 | .applicationModule(ApplicationModule(application))
21 | .sharedPreferencesModule(SharedPreferencesModule(application))
22 | .analyticsModule(AnalyticsModule(application))
23 | .build()
24 | }
25 |
26 | companion object {
27 |
28 | private var wrapper: CoreComponentWrapper? = null
29 |
30 | @Synchronized
31 | private fun getInstance(application: Application): CoreComponentWrapper {
32 | if (wrapper == null) {
33 | if (wrapper == null) {
34 | wrapper = CoreComponentWrapper()
35 | wrapper!!.initializeComponent(application)
36 | }
37 | }
38 | return wrapper!!
39 | }
40 |
41 | fun getComponent(application: Application) = getInstance(application).component
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/di/FeatureScope.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.di
2 |
3 | import javax.inject.Scope
4 |
5 | /**
6 | * Created at Tito on 3/15/19
7 | *
8 | * Custom scope for features.
9 | */
10 |
11 | @Scope
12 | annotation class FeatureScope
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/di/PresentationScope.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.di
2 |
3 | import javax.inject.Scope
4 |
5 | /**
6 | * Created at Tito on 3/15/19
7 | *
8 | * Custom scope for presentation
9 | */
10 |
11 | @Scope
12 | annotation class PresentationScope
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/di/ViewModelFactoryModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.di
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import dagger.Module
6 | import dagger.Provides
7 | import javax.inject.Provider
8 | import javax.inject.Singleton
9 |
10 | /**
11 | * Created at Tito on 3/16/19
12 | */
13 |
14 | @Module
15 | class ViewModelFactoryModule {
16 |
17 | @Provides
18 | @Suppress("UNCHECKED_CAST")
19 | fun provideViewModelFactory(providers: Map, @JvmSuppressWildcards Provider>) =
20 | object : ViewModelProvider.Factory {
21 | override fun create(modelClass: Class): T {
22 | return requireNotNull(providers[modelClass as Class]).get() as T
23 | }
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/di/ViewModelKey.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.di
2 |
3 | import androidx.lifecycle.ViewModel
4 | import dagger.MapKey
5 | import kotlin.reflect.KClass
6 |
7 | /**
8 | * Created at Tito on 3/16/19
9 | *
10 | *
11 | * Key for each view model for dagger.
12 | */
13 |
14 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
15 | @Retention(AnnotationRetention.RUNTIME)
16 | @MapKey
17 | annotation class ViewModelKey(val value: KClass)
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/di/scheduler/BaseSchedulerProvider.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.di.scheduler
2 |
3 | import io.reactivex.Scheduler
4 |
5 | /**
6 | * Created at Tito on 3/16/19
7 | *
8 | * Base class for scheduler provider.
9 | */
10 |
11 | interface BaseSchedulerProvider {
12 |
13 | fun io(): Scheduler
14 |
15 | fun ui(): Scheduler
16 |
17 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/di/scheduler/BaseSchedulerProviderModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.di.scheduler
2 |
3 | import com.ahmedadel.robustandroid.core.di.PresentationScope
4 | import dagger.Module
5 | import dagger.Provides
6 |
7 | /**
8 | * Created at Tito on 3/16/19
9 | *
10 | * Provider class for making background and foreground threads that related to RX stuff. this class will be helpful
11 | * for unit testing purpose to change the scheduler provider during the unit testing phase and not making the
12 | * scheduler provider attached to the architecture.
13 | */
14 |
15 | @Module
16 | class BaseSchedulerProviderModule {
17 |
18 | @Provides
19 | @PresentationScope
20 | fun providesTaskBaseScheduler(): BaseSchedulerProvider = SchedulerProvider()
21 |
22 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/di/scheduler/SchedulerProvider.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.di.scheduler
2 |
3 | import io.reactivex.Scheduler
4 | import io.reactivex.android.schedulers.AndroidSchedulers
5 | import io.reactivex.schedulers.Schedulers
6 |
7 | /**
8 | * Created at Tito on 3/16/19
9 | *
10 | * This class that will be used in the default behaviour of the app.
11 | */
12 |
13 | class SchedulerProvider : BaseSchedulerProvider {
14 |
15 | override fun io(): Scheduler {
16 | return Schedulers.io()
17 | }
18 |
19 | override fun ui(): Scheduler {
20 | return AndroidSchedulers.mainThread()
21 | }
22 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/di/scheduler/TrampolineSchedulerProvider.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.di.scheduler
2 |
3 | import io.reactivex.Scheduler
4 | import io.reactivex.schedulers.Schedulers
5 |
6 | /**
7 | * Created at Tito on 3/16/19
8 | *
9 | * This scheduler will be used in unit testing phase.
10 | */
11 |
12 | class TrampolineSchedulerProvider : BaseSchedulerProvider {
13 |
14 | override fun io(): Scheduler {
15 | return Schedulers.trampoline()
16 | }
17 |
18 | override fun ui(): Scheduler {
19 | return Schedulers.trampoline()
20 | }
21 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/sharedpreferences/SharedPreferencesWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.sharedpreferences
2 |
3 | import android.content.SharedPreferences
4 | import javax.inject.Inject
5 | import javax.inject.Singleton
6 |
7 | /**
8 | * Created at Tito on 3/13/19
9 | *
10 | * Wrapper class for dealing with SharedPreferences in an easy way.
11 | */
12 |
13 | @Suppress("unused")
14 | @Singleton
15 | class SharedPrefWrapper @Inject constructor(private val sharedPreferences: SharedPreferences) {
16 |
17 | fun saveString(key: String, value: String) {
18 | sharedPreferences.edit().putString(key, value).apply()
19 | }
20 |
21 | fun deleteKey(key: String) {
22 | sharedPreferences.edit().remove(key).apply()
23 | }
24 |
25 | fun getString(key: String, defValue: String = ""): String {
26 | return sharedPreferences.getString(key, defValue) ?: ""
27 | }
28 |
29 | fun getStringOrNull(key: String): String? {
30 | return sharedPreferences.getString(key, null)
31 | }
32 |
33 | fun saveInt(key: String, value: Int) {
34 | sharedPreferences.edit().putInt(key, value).apply()
35 | }
36 |
37 | fun getInt(key: String, defValue: Int = -1): Int {
38 | return sharedPreferences.getInt(key, defValue)
39 | }
40 |
41 | fun saveBoolean(key: String, value: Boolean) {
42 | sharedPreferences.edit().putBoolean(key, value).apply()
43 | }
44 |
45 | fun getBoolean(key: String, defValue: Boolean = false): Boolean {
46 | return sharedPreferences.getBoolean(key, defValue)
47 | }
48 |
49 | fun clear() {
50 | sharedPreferences.edit().clear().apply()
51 | }
52 |
53 | companion object {
54 | const val SHARED_PREFERENCE_KEY = "ROBUST_ANDROID_APP"
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/core/src/main/java/com/ahmedadel/robustandroid/core/sharedpreferences/di/SharedPreferencesModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.core.sharedpreferences.di
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import android.content.SharedPreferences
6 | import com.ahmedadel.robustandroid.core.sharedpreferences.SharedPrefWrapper.Companion.SHARED_PREFERENCE_KEY
7 | import dagger.Module
8 | import dagger.Provides
9 | import javax.inject.Singleton
10 |
11 | /**
12 | * Created at Tito on 3/13/19
13 | *
14 | * Dagger module that provides SharedPreferences Class
15 | */
16 |
17 | @Module
18 | class SharedPreferencesModule(private val application: Application) {
19 |
20 | @Provides
21 | @Singleton
22 | fun provideSharedPreferences(): SharedPreferences =
23 | application.getSharedPreferences(SHARED_PREFERENCE_KEY, Context.MODE_PRIVATE)
24 |
25 | }
--------------------------------------------------------------------------------
/core/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | core
3 |
4 |
--------------------------------------------------------------------------------
/datalayer/datasource/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/datalayer/datasource/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.*
2 |
3 | apply {
4 | plugin("com.android.library")
5 | plugin("kotlin-android-extensions")
6 | plugin("kotlin-android")
7 | plugin("kotlin-kapt")
8 | }
9 |
10 | android {
11 |
12 | compileSdkVersion Versions.compileSdkVersion
13 |
14 | defaultConfig {
15 |
16 | minSdkVersion Versions.minSdkVersion
17 | targetSdkVersion Versions.targetSdkVersion
18 |
19 | versionCode Versions.versionCode
20 | versionName Versions.versionName
21 |
22 | }
23 |
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 |
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 |
37 | api project(':core')
38 | api project(':datalayer:local')
39 | api project(':datalayer:remote')
40 |
41 | implementation Dependencies.AppCompact
42 |
43 | implementation Dependencies.KotlinStdLib
44 |
45 | implementation Dependencies.Retrofit
46 | implementation Dependencies.OkLog
47 | implementation Dependencies.RetrofitTesting
48 |
49 | implementation Dependencies.Moshi
50 |
51 | api Dependencies.Room
52 | implementation Dependencies.RoomTesting
53 | kapt Dependencies.RoomKapt
54 |
55 | implementation Dependencies.RxJava
56 |
57 | implementation Dependencies.Dagger
58 | kapt Dependencies.DaggerKapt
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/datalayer/datasource/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
22 |
--------------------------------------------------------------------------------
/datalayer/datasource/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/datalayer/datasource/src/main/java/com/ahmedadel/robustandroid/datalayer/datasource/both/di/DataSourceComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.datasource.both.di
2 |
3 | import com.ahmedadel.robustandroid.datalayer.datasource.local.di.LocalModule
4 | import com.ahmedadel.robustandroid.datalayer.datasource.remote.di.RemoteModule
5 |
6 | import com.ahmedadel.robustandroid.core.di.CoreComponent
7 |
8 | import com.ahmedadel.robustandroid.datalayer.local.DatabaseManager
9 | import com.ahmedadel.robustandroid.datalayer.remote.ApiService
10 |
11 | import dagger.Component
12 |
13 | /**
14 | * Created at Tito on 3/15/19
15 | */
16 |
17 | @DataSourceScope
18 | @Component(
19 | modules = [
20 | RemoteModule::class,
21 | LocalModule::class
22 | ],
23 | dependencies = [
24 | CoreComponent::class
25 | ]
26 | )
27 | interface DataSourceComponent {
28 |
29 | fun apiService(): ApiService
30 |
31 | fun databaseManager(): DatabaseManager
32 |
33 | }
--------------------------------------------------------------------------------
/datalayer/datasource/src/main/java/com/ahmedadel/robustandroid/datalayer/datasource/both/di/DataSourceComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.datasource.both.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.core.di.CoreComponent
5 | import com.ahmedadel.robustandroid.core.di.CoreComponentWrapper
6 | import com.ahmedadel.robustandroid.datalayer.datasource.local.di.LocalModule
7 |
8 | /**
9 | * Created at Tito on 3/15/19
10 | */
11 |
12 | open class DataSourceComponentWrapper {
13 |
14 | private lateinit var component: DataSourceComponent
15 |
16 | private fun initializeComponent(coreComponent: CoreComponent) {
17 | component = DaggerDataSourceComponent.builder()
18 | .coreComponent(coreComponent)
19 | .localModule(LocalModule(coreComponent.application()))
20 | .build()
21 | }
22 |
23 | companion object {
24 |
25 | private var wrapper: DataSourceComponentWrapper? = null
26 |
27 | @Synchronized
28 | private fun getInstance(application: Application): DataSourceComponentWrapper {
29 | if (wrapper == null) {
30 | if (wrapper == null) {
31 | wrapper = DataSourceComponentWrapper()
32 | wrapper!!.initializeComponent(CoreComponentWrapper.getComponent(application))
33 | }
34 | }
35 | return wrapper!!
36 | }
37 |
38 | fun getComponent(application: Application) = getInstance(application).component
39 |
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/datalayer/datasource/src/main/java/com/ahmedadel/robustandroid/datalayer/datasource/both/di/DataSourceScope.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.datasource.both.di
2 |
3 | import javax.inject.Scope
4 |
5 | /**
6 | * Created at Tito on 3/15/19
7 | */
8 |
9 | @Scope
10 | annotation class DataSourceScope
--------------------------------------------------------------------------------
/datalayer/datasource/src/main/java/com/ahmedadel/robustandroid/datalayer/datasource/local/di/LocalComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.datasource.local.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.CoreComponent
4 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceScope
5 | import com.ahmedadel.robustandroid.datalayer.local.DatabaseManager
6 | import dagger.Component
7 |
8 | /**
9 | * Created at Tito on 3/15/19
10 | *
11 | * Dagger Component that will provide and implement the initialization of Local Module.
12 | */
13 |
14 | @DataSourceScope
15 | @Component(
16 | modules = [
17 | LocalModule::class
18 | ],
19 | dependencies = [
20 | CoreComponent::class
21 | ]
22 | )
23 | interface LocalComponent {
24 |
25 | fun databaseManager(): DatabaseManager
26 | }
--------------------------------------------------------------------------------
/datalayer/datasource/src/main/java/com/ahmedadel/robustandroid/datalayer/datasource/local/di/LocalComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.datasource.local.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.core.di.CoreComponent
5 | import com.ahmedadel.robustandroid.core.di.CoreComponentWrapper
6 |
7 | /**
8 | * Created at Tito on 3/15/19
9 | *
10 | * Wrapper class with singleton behaviour to initialize Dagger Local Component.
11 | */
12 |
13 | open class LocalComponentWrapper {
14 |
15 | private lateinit var component: LocalComponent
16 |
17 | private fun initializeComponent(coreComponent: CoreComponent) {
18 | component = DaggerLocalComponent.builder()
19 | .coreComponent(coreComponent)
20 | .localModule(LocalModule(coreComponent.application()))
21 | .build()
22 | }
23 |
24 | companion object {
25 |
26 | private var wrapper: LocalComponentWrapper? = null
27 |
28 | @Synchronized
29 | private fun getInstance(application: Application): LocalComponentWrapper {
30 | if (wrapper == null) {
31 | if (wrapper == null) {
32 | wrapper =
33 | LocalComponentWrapper()
34 | wrapper!!.initializeComponent(CoreComponentWrapper.getComponent(application))
35 | }
36 | }
37 | return wrapper!!
38 | }
39 |
40 | fun getComponent(application: Application) = getInstance(
41 | application
42 | ).component
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/datalayer/datasource/src/main/java/com/ahmedadel/robustandroid/datalayer/datasource/local/di/LocalModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.datasource.local.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceScope
5 | import com.ahmedadel.robustandroid.datalayer.local.DatabaseManager
6 | import dagger.Module
7 | import dagger.Provides
8 |
9 | /**
10 | * Created at Tito on 3/15/19
11 | *
12 | * Dagger Module that provides Local stuff like Room Database.
13 | */
14 |
15 | @Module
16 | class LocalModule(private val application: Application) {
17 |
18 | @Provides
19 | @DataSourceScope
20 | fun provideDatabaseManager(): DatabaseManager = DatabaseManager.getInstance(application)
21 |
22 | }
--------------------------------------------------------------------------------
/datalayer/datasource/src/main/java/com/ahmedadel/robustandroid/datalayer/datasource/remote/di/RemoteComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.datasource.remote.di
2 |
3 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceScope
4 | import com.ahmedadel.robustandroid.datalayer.remote.ApiService
5 | import dagger.Component
6 |
7 | /**
8 | * Created at Tito on 3/15/19
9 | *
10 | * Dagger Component that will provide and implement the initialization of Remote Module.
11 | */
12 |
13 | @DataSourceScope
14 | @Component(
15 | modules = [
16 | RemoteModule::class
17 | ]
18 | )
19 | interface RemoteComponent {
20 |
21 | fun apiService(): ApiService
22 |
23 | }
--------------------------------------------------------------------------------
/datalayer/datasource/src/main/java/com/ahmedadel/robustandroid/datalayer/datasource/remote/di/RemoteComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.datasource.remote.di
2 |
3 | /**
4 | * Created at Tito on 3/15/19
5 | *
6 | * Wrapper class with singleton behaviour to initialize Dagger Remote Component.
7 | */
8 |
9 | open class RemoteComponentWrapper {
10 |
11 | private lateinit var component: com.ahmedadel.robustandroid.datalayer.datasource.remote.di.RemoteComponent
12 |
13 | private fun initializeComponent() {
14 | component = DaggerRemoteComponent.builder()
15 | .remoteModule(RemoteModule())
16 | .build()
17 | }
18 |
19 | companion object {
20 |
21 | private var wrapper: RemoteComponentWrapper? = null
22 |
23 | @Synchronized
24 | private fun getInstance(): RemoteComponentWrapper {
25 | if (wrapper == null) {
26 | if (wrapper == null) {
27 | wrapper =
28 | RemoteComponentWrapper()
29 | wrapper!!.initializeComponent()
30 | }
31 | }
32 | return wrapper!!
33 | }
34 |
35 | fun getComponent() = getInstance().component
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/datalayer/datasource/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | datasource
3 |
4 |
--------------------------------------------------------------------------------
/datalayer/local/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/datalayer/local/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.*
2 |
3 | apply {
4 | plugin("com.android.library")
5 | plugin("kotlin-android-extensions")
6 | plugin("kotlin-android")
7 | plugin("kotlin-kapt")
8 | }
9 |
10 | android {
11 |
12 | compileSdkVersion Versions.compileSdkVersion
13 |
14 | defaultConfig {
15 |
16 | minSdkVersion Versions.minSdkVersion
17 | targetSdkVersion Versions.targetSdkVersion
18 |
19 | versionCode Versions.versionCode
20 | versionName Versions.versionName
21 |
22 | }
23 |
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 |
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 |
37 | api project(':models')
38 |
39 | implementation Dependencies.AppCompact
40 |
41 | implementation Dependencies.KotlinStdLib
42 |
43 | implementation Dependencies.RxJava
44 |
45 | implementation Dependencies.Dagger
46 | kapt Dependencies.DaggerKapt
47 |
48 | api Dependencies.Room
49 | implementation Dependencies.RoomTesting
50 | kapt Dependencies.RoomKapt
51 |
52 | implementation Dependencies.Testing
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/datalayer/local/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
22 |
--------------------------------------------------------------------------------
/datalayer/local/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/datalayer/local/src/main/java/com/ahmedadel/robustandroid/datalayer/local/DatabaseManager.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.local
2 |
3 | import android.content.Context
4 | import androidx.room.Database
5 | import androidx.room.Room
6 | import androidx.room.RoomDatabase
7 | import com.ahmedadel.robustandroid.datalayer.local.dao.movie.MovieDao
8 | import com.ahmedadel.robustandroid.datalayer.local.dao.person.PersonDao
9 | import com.ahmedadel.robustandroid.datalayer.local.dao.tv.TVDao
10 | import com.ahmedadel.robustandroid.models.local.movie.MovieLocal
11 | import com.ahmedadel.robustandroid.models.local.person.PersonLocal
12 | import com.ahmedadel.robustandroid.models.local.tv.TVLocal
13 |
14 | /**
15 | * Created at Tito on 3/15/19
16 | *
17 | * Room Database Manager for the whole project with move-app name.
18 | */
19 |
20 | @Database(entities = [MovieLocal::class, PersonLocal::class, TVLocal::class], version = 1, exportSchema = false)
21 | abstract class DatabaseManager : RoomDatabase() {
22 |
23 | abstract fun movieDao(): MovieDao
24 |
25 | abstract fun personDao(): PersonDao
26 |
27 | abstract fun tvDao(): TVDao
28 |
29 | companion object {
30 |
31 | @Synchronized
32 | fun getInstance(context: Context): DatabaseManager {
33 | return Room.databaseBuilder(
34 | context.applicationContext,
35 | DatabaseManager::class.java, "movie-app.db"
36 | ).build()
37 | }
38 |
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/datalayer/local/src/main/java/com/ahmedadel/robustandroid/datalayer/local/dao/movie/MovieDao.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.local.dao.movie
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import com.ahmedadel.robustandroid.models.local.movie.MovieLocal
8 | import io.reactivex.Single
9 |
10 | /**
11 | * Created at Tito on 3/15/19
12 | *
13 | * Dao for Movie Local.
14 | */
15 |
16 | @Dao
17 | interface MovieDao {
18 |
19 | @get:Query("SELECT * FROM movie")
20 | val getMovies: Single>
21 |
22 | @Query("SELECT * FROM movie WHERE id = :movieId")
23 | fun getMovie(movieId: Int?): Single
24 |
25 | @Insert(onConflict = OnConflictStrategy.REPLACE)
26 | fun insertMovie(movie: MovieLocal)
27 |
28 | @Query("DELETE FROM movie")
29 | fun deleteAll(): Int
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/datalayer/local/src/main/java/com/ahmedadel/robustandroid/datalayer/local/dao/person/PersonDao.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.local.dao.person
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import com.ahmedadel.robustandroid.models.local.person.PersonLocal
8 | import io.reactivex.Single
9 |
10 | /**
11 | * Created at Tito on 3/15/19
12 | *
13 | * Dao for Person Local.
14 | */
15 |
16 | @Dao
17 | interface PersonDao {
18 |
19 | @get:Query("SELECT * FROM person")
20 | val getPersons: Single>
21 |
22 | @Query("SELECT * FROM person WHERE id = :personId")
23 | fun getPerson(personId: Int?): Single
24 |
25 | @Insert(onConflict = OnConflictStrategy.REPLACE)
26 | fun insertPerson(person: PersonLocal)
27 |
28 | @Query("DELETE FROM person")
29 | fun deleteAll(): Int
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/datalayer/local/src/main/java/com/ahmedadel/robustandroid/datalayer/local/dao/tv/TVDao.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.local.dao.tv
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import com.ahmedadel.robustandroid.models.local.tv.TVLocal
8 | import io.reactivex.Single
9 |
10 | /**
11 | * Created at Tito on 3/15/19
12 | *
13 | * Dao for TV Local.
14 | */
15 |
16 | @Dao
17 | interface TVDao {
18 |
19 | @get:Query("SELECT * FROM tv")
20 | val getTVs: Single>
21 |
22 | @Query("SELECT * FROM tv WHERE id = :tvId")
23 | fun getTV(tvId: Int?): Single
24 |
25 | @Insert(onConflict = OnConflictStrategy.REPLACE)
26 | fun insertTV(tv: TVLocal)
27 |
28 | @Query("DELETE FROM tv")
29 | fun deleteAll(): Int
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/datalayer/local/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | local
3 |
4 |
--------------------------------------------------------------------------------
/datalayer/local/src/test/java/com/ahmedadel/robustandroid/datalayer/local/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.local;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/datalayer/remote/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/datalayer/remote/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.*
2 |
3 | apply {
4 | plugin("com.android.library")
5 | plugin("kotlin-android-extensions")
6 | plugin("kotlin-android")
7 | plugin("kotlin-kapt")
8 | }
9 |
10 | android {
11 |
12 | compileSdkVersion Versions.compileSdkVersion
13 |
14 | defaultConfig {
15 |
16 | minSdkVersion Versions.minSdkVersion
17 | targetSdkVersion Versions.targetSdkVersion
18 |
19 | versionCode Versions.versionCode
20 | versionName Versions.versionName
21 |
22 | }
23 |
24 | buildTypes {
25 |
26 | debug {
27 | buildConfigField("String", "BASE_API_URL", "\"${Config.STAGING_API_BASE_URL}\"")
28 | buildConfigField("String", "API_KEY", "\"${Config.API_KEY}\"")
29 | }
30 | release {
31 | minifyEnabled false
32 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
33 | buildConfigField("String", "BASE_API_URL", "\"${Config.PROD_API_BASE_URL}\"")
34 | buildConfigField("String", "API_KEY", "\"${Config.API_KEY}\"")
35 | }
36 |
37 | }
38 |
39 | }
40 |
41 | dependencies {
42 |
43 | implementation fileTree(dir: 'libs', include: ['*.jar'])
44 |
45 | api project(':models')
46 |
47 | implementation Dependencies.AppCompact
48 |
49 | implementation Dependencies.KotlinStdLib
50 |
51 | implementation Dependencies.Retrofit
52 | implementation Dependencies.OkLog
53 | implementation Dependencies.RetrofitTesting
54 |
55 | implementation Dependencies.RxJava
56 |
57 | implementation Dependencies.Dagger
58 | kapt Dependencies.DaggerKapt
59 |
60 | implementation Dependencies.Moshi
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/datalayer/remote/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
22 |
--------------------------------------------------------------------------------
/datalayer/remote/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/datalayer/remote/src/main/java/com/ahmedadel/robustandroid/datalayer/remote/ApiKeyInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.datalayer.remote
2 |
3 | import okhttp3.Interceptor
4 | import okhttp3.Response
5 |
6 | /**
7 | * Created at Tito on 3/15/19
8 | *
9 | * An Interceptor class that making some automation by sending api_key to every api request by default.
10 | */
11 |
12 | class ApiKeyInterceptor : Interceptor {
13 |
14 | override fun intercept(chain: Interceptor.Chain): Response {
15 | val original = chain.request()
16 | val originalHttpUrl = original.url()
17 |
18 | val url = originalHttpUrl.newBuilder()
19 | .addQueryParameter("api_key", BuildConfig.API_KEY)
20 | .build()
21 |
22 | val requestBuilder = original.newBuilder().url(url)
23 |
24 | val request = requestBuilder.build()
25 | return chain.proceed(request)
26 | }
27 | }
--------------------------------------------------------------------------------
/datalayer/remote/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | remote
3 |
4 |
--------------------------------------------------------------------------------
/feature/movie/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/feature/movie/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.*
2 |
3 | apply {
4 | plugin("com.android.library")
5 | plugin("kotlin-android-extensions")
6 | plugin("kotlin-android")
7 | plugin("kotlin-kapt")
8 | }
9 |
10 | android {
11 |
12 | compileSdkVersion Versions.compileSdkVersion
13 |
14 | defaultConfig {
15 |
16 | minSdkVersion Versions.minSdkVersion
17 | targetSdkVersion Versions.targetSdkVersion
18 |
19 | versionCode Versions.versionCode
20 | versionName Versions.versionName
21 |
22 | }
23 |
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 |
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 |
37 | api project(':datalayer:datasource')
38 |
39 | implementation Dependencies.AppCompact
40 |
41 | implementation Dependencies.KotlinStdLib
42 |
43 | implementation Dependencies.RxJava
44 |
45 | implementation Dependencies.Dagger
46 | kapt Dependencies.DaggerKapt
47 |
48 | implementation Dependencies.Testing
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/feature/movie/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
22 |
--------------------------------------------------------------------------------
/feature/movie/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/feature/movie/src/main/java/com/ahmedadel/robustandroid/feature/movie/di/MovieComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.movie.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.FeatureScope
4 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceComponent
5 | import com.ahmedadel.robustandroid.feature.movie.usecase.MovieUseCase
6 | import dagger.Component
7 |
8 | /**
9 | * Created at Tito on 3/15/19
10 | */
11 |
12 | @FeatureScope
13 | @Component(
14 | modules = [
15 | MovieModule::class
16 | ],
17 | dependencies = [
18 | DataSourceComponent::class
19 | ]
20 | )
21 | interface MovieComponent {
22 |
23 | fun getMovieUseCase(): MovieUseCase
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/feature/movie/src/main/java/com/ahmedadel/robustandroid/feature/movie/di/MovieComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.movie.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceComponent
5 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceComponentWrapper
6 |
7 | /**
8 | * Created at Tito on 3/15/19
9 | */
10 |
11 | open class MovieComponentWrapper {
12 |
13 | private lateinit var component: MovieComponent
14 |
15 | private fun initializeComponent(dataSourceComponent: DataSourceComponent) {
16 | component = DaggerMovieComponent.builder()
17 | .movieModule(
18 | MovieModule(
19 | dataSourceComponent.databaseManager().movieDao(),
20 | dataSourceComponent.apiService()
21 | )
22 | )
23 | .dataSourceComponent(dataSourceComponent)
24 | .build()
25 | }
26 |
27 | companion object {
28 |
29 | private var wrapper: MovieComponentWrapper? = null
30 |
31 | @Synchronized
32 | private fun getInstance(application: Application): MovieComponentWrapper {
33 | if (wrapper == null) {
34 | if (wrapper == null) {
35 | wrapper = MovieComponentWrapper()
36 | wrapper!!.initializeComponent(DataSourceComponentWrapper.getComponent(application))
37 | }
38 | }
39 | return wrapper!!
40 | }
41 |
42 | fun getComponent(application: Application) = getInstance(application).component
43 |
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/feature/movie/src/main/java/com/ahmedadel/robustandroid/feature/movie/di/MovieModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.movie.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.FeatureScope
4 | import com.ahmedadel.robustandroid.datalayer.local.dao.movie.MovieDao
5 | import com.ahmedadel.robustandroid.datalayer.remote.ApiService
6 | import com.ahmedadel.robustandroid.feature.movie.mapper.MovieMapper
7 | import com.ahmedadel.robustandroid.feature.movie.repository.MovieRepository
8 | import com.ahmedadel.robustandroid.feature.movie.usecase.MovieUseCase
9 | import dagger.Module
10 | import dagger.Provides
11 |
12 | /**
13 | * Created at Tito on 3/15/19
14 | */
15 |
16 | @Module
17 | class MovieModule(
18 | private val local: MovieDao,
19 | private val remote: ApiService
20 | ) {
21 |
22 | @Provides
23 | @FeatureScope
24 | fun providesMovieMapper() = MovieMapper()
25 |
26 | @Provides
27 | @FeatureScope
28 | fun provideMovieRepository(mapper: MovieMapper) = MovieRepository(local, remote, mapper)
29 |
30 | @Provides
31 | @FeatureScope
32 | fun provideMovieUseCase(repository: MovieRepository) = MovieUseCase(repository)
33 |
34 | }
--------------------------------------------------------------------------------
/feature/movie/src/main/java/com/ahmedadel/robustandroid/feature/movie/entity/MovieEntity.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.movie.entity
2 |
3 | import com.ahmedadel.robustandroid.models.EntityModel
4 |
5 | /**
6 | * Created at Tito on 3/15/19
7 | */
8 |
9 | data class MovieEntity(
10 |
11 | val id: Int = 0,
12 |
13 | val overview: String? = null,
14 |
15 | val originalLanguage: String? = null,
16 |
17 | val originalTitle: String? = null,
18 |
19 | val title: String? = null,
20 |
21 | val posterPath: String? = null,
22 |
23 | val releaseDate: String? = null,
24 |
25 | val popularity: Double = 0.0,
26 |
27 | val voteAverage: Double = 0.0,
28 |
29 | val isAdult: Boolean = false,
30 |
31 | val voteCount: Int = 0
32 |
33 | ) : EntityModel
--------------------------------------------------------------------------------
/feature/movie/src/main/java/com/ahmedadel/robustandroid/feature/movie/usecase/MovieUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.movie.usecase
2 |
3 | import com.ahmedadel.robustandroid.feature.movie.repository.MovieRepository
4 | import javax.inject.Inject
5 |
6 | /**
7 | * Created at Tito on 3/15/19
8 | */
9 |
10 | class MovieUseCase
11 | @Inject
12 | constructor(private val repository: MovieRepository) {
13 |
14 | fun getMovies(pageNumber: Int) = repository.getMovies(pageNumber)
15 |
16 | fun getMovie(movieId: Int) = repository.getMovie(movieId)
17 |
18 | fun getSimilarMovies(movieId: Int) = repository.getSimilarMovies(movieId)
19 |
20 | fun getRecommendationMovies(movieId: Int) = repository.getRecommendationMovies(movieId)
21 |
22 | }
--------------------------------------------------------------------------------
/feature/movie/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Movie
3 |
4 |
--------------------------------------------------------------------------------
/feature/movie/src/test/java/com/ahmedadel/robustandroid/feature/movie/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.movie;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/feature/person/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/feature/person/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.*
2 |
3 | apply {
4 | plugin("com.android.library")
5 | plugin("kotlin-android-extensions")
6 | plugin("kotlin-android")
7 | plugin("kotlin-kapt")
8 | }
9 |
10 | android {
11 |
12 | compileSdkVersion Versions.compileSdkVersion
13 |
14 | defaultConfig {
15 |
16 | minSdkVersion Versions.minSdkVersion
17 | targetSdkVersion Versions.targetSdkVersion
18 |
19 | versionCode Versions.versionCode
20 | versionName Versions.versionName
21 |
22 | }
23 |
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 |
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 |
37 | api project(':datalayer:datasource')
38 |
39 | implementation Dependencies.AppCompact
40 |
41 | implementation Dependencies.KotlinStdLib
42 |
43 | implementation Dependencies.RxJava
44 |
45 | implementation Dependencies.Dagger
46 | kapt Dependencies.DaggerKapt
47 |
48 | implementation Dependencies.Testing
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/feature/person/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
22 |
--------------------------------------------------------------------------------
/feature/person/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/feature/person/src/main/java/com/ahmedadel/robustandroid/feature/person/di/PersonComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.person.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.FeatureScope
4 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceComponent
5 | import com.ahmedadel.robustandroid.feature.person.usecase.PersonUseCase
6 | import dagger.Component
7 |
8 | /**
9 | * Created at Tito on 3/15/19
10 | */
11 |
12 | @FeatureScope
13 | @Component(
14 | modules = [
15 | PersonModule::class
16 | ],
17 | dependencies = [
18 | DataSourceComponent::class
19 | ]
20 | )
21 | interface PersonComponent {
22 |
23 | fun getPersonUseCase(): PersonUseCase
24 |
25 | }
--------------------------------------------------------------------------------
/feature/person/src/main/java/com/ahmedadel/robustandroid/feature/person/di/PersonComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.person.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceComponent
5 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceComponentWrapper
6 |
7 | /**
8 | * Created at Tito on 3/15/19
9 | */
10 |
11 | open class PersonComponentWrapper {
12 |
13 | private lateinit var component: PersonComponent
14 |
15 | private fun initializeComponent(dataSourceComponent: DataSourceComponent) {
16 | component = DaggerPersonComponent.builder()
17 | .personModule(
18 | PersonModule(
19 | dataSourceComponent.databaseManager().personDao(),
20 | dataSourceComponent.apiService()
21 | )
22 | )
23 | .dataSourceComponent(dataSourceComponent)
24 | .build()
25 | }
26 |
27 | companion object {
28 |
29 | private var wrapper: PersonComponentWrapper? = null
30 |
31 | @Synchronized
32 | private fun getInstance(application: Application): PersonComponentWrapper {
33 | if (wrapper == null) {
34 | if (wrapper == null) {
35 | wrapper = PersonComponentWrapper()
36 | wrapper!!.initializeComponent(DataSourceComponentWrapper.getComponent(application))
37 | }
38 | }
39 | return wrapper!!
40 | }
41 |
42 | fun getComponent(application: Application) = getInstance(application).component
43 |
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/feature/person/src/main/java/com/ahmedadel/robustandroid/feature/person/di/PersonModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.person.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.FeatureScope
4 | import com.ahmedadel.robustandroid.datalayer.local.dao.person.PersonDao
5 | import com.ahmedadel.robustandroid.datalayer.remote.ApiService
6 | import com.ahmedadel.robustandroid.feature.person.mapper.PersonMapper
7 | import com.ahmedadel.robustandroid.feature.person.repository.PersonRepository
8 | import com.ahmedadel.robustandroid.feature.person.usecase.PersonUseCase
9 | import dagger.Module
10 | import dagger.Provides
11 |
12 | /**
13 | * Created at Tito on 3/15/19
14 | */
15 |
16 | @Module
17 | class PersonModule(
18 | private val local: PersonDao,
19 | private val remote: ApiService
20 | ) {
21 |
22 | @Provides
23 | @FeatureScope
24 | fun providesPersonMapper() = PersonMapper()
25 |
26 | @Provides
27 | @FeatureScope
28 | fun providePersonRepository(mapper: PersonMapper) = PersonRepository(local, remote, mapper)
29 |
30 | @Provides
31 | @FeatureScope
32 | fun providePersonUseCase(repository: PersonRepository) = PersonUseCase(repository)
33 |
34 | }
--------------------------------------------------------------------------------
/feature/person/src/main/java/com/ahmedadel/robustandroid/feature/person/entity/PersonEntity.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.person.entity
2 |
3 | import com.ahmedadel.robustandroid.models.EntityModel
4 |
5 | /**
6 | * Created at Tito on 3/15/19
7 | */
8 |
9 | data class PersonEntity(
10 |
11 | val id: Int = 0,
12 |
13 | val popularity: Double = 0.0,
14 |
15 | val name: String? = null,
16 |
17 | val profilePath: String? = null,
18 |
19 | val isAdult: Boolean = false
20 |
21 | ) : EntityModel
--------------------------------------------------------------------------------
/feature/person/src/main/java/com/ahmedadel/robustandroid/feature/person/mapper/PersonMapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.person.mapper
2 |
3 | import com.ahmedadel.robustandroid.feature.person.entity.PersonEntity
4 | import com.ahmedadel.robustandroid.models.local.person.PersonLocal
5 | import com.ahmedadel.robustandroid.models.mappers.MapFromRemoteToEntity
6 | import com.ahmedadel.robustandroid.models.remote.person.PersonRemote
7 |
8 | /**
9 | * Created at Tito on 3/15/19
10 | */
11 |
12 | class PersonMapper : MapFromRemoteToEntity {
13 |
14 | override fun mapFromRemoteToEntity(model: PersonRemote): PersonEntity {
15 | with(model) {
16 | return PersonEntity(
17 | id = id,
18 | popularity = popularity,
19 | name = name,
20 | profilePath = profilePath,
21 | isAdult = isAdult
22 | )
23 | }
24 | }
25 |
26 | override fun mapFromLocalToEntity(model: PersonLocal): PersonEntity {
27 | with(model) {
28 | return PersonEntity(
29 | id = id,
30 | popularity = popularity,
31 | name = name,
32 | profilePath = profilePath,
33 | isAdult = adult
34 | )
35 | }
36 | }
37 |
38 | override fun mapFromRemoteToLocal(model: PersonRemote): PersonLocal {
39 | with(model) {
40 | return PersonLocal(
41 | id = id,
42 | popularity = popularity,
43 | name = name,
44 | profilePath = profilePath,
45 | adult = isAdult
46 | )
47 | }
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/feature/person/src/main/java/com/ahmedadel/robustandroid/feature/person/usecase/PersonUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.person.usecase
2 |
3 | import com.ahmedadel.robustandroid.feature.person.repository.PersonRepository
4 | import javax.inject.Inject
5 |
6 | /**
7 | * Created at Tito on 3/15/19
8 | */
9 |
10 | class PersonUseCase
11 | @Inject
12 | constructor(private val repository: PersonRepository) {
13 |
14 | fun getPersons(pageNumber: Int) = repository.getPersons(pageNumber)
15 |
16 | fun getPerson(personId: Int) = repository.getPerson(personId)
17 |
18 | }
--------------------------------------------------------------------------------
/feature/person/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | person
3 |
4 |
--------------------------------------------------------------------------------
/feature/person/src/test/java/com/ahmedadel/robustandroid/feature/person/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.person;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/feature/tv/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/feature/tv/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.*
2 |
3 | apply {
4 | plugin("com.android.library")
5 | plugin("kotlin-android-extensions")
6 | plugin("kotlin-android")
7 | plugin("kotlin-kapt")
8 | }
9 |
10 | android {
11 |
12 | compileSdkVersion Versions.compileSdkVersion
13 |
14 | defaultConfig {
15 |
16 | minSdkVersion Versions.minSdkVersion
17 | targetSdkVersion Versions.targetSdkVersion
18 |
19 | versionCode Versions.versionCode
20 | versionName Versions.versionName
21 |
22 | }
23 |
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 |
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 |
37 | api project(':datalayer:datasource')
38 |
39 | implementation Dependencies.AppCompact
40 |
41 | implementation Dependencies.KotlinStdLib
42 |
43 | implementation Dependencies.RxJava
44 |
45 | implementation Dependencies.Dagger
46 | kapt Dependencies.DaggerKapt
47 |
48 | implementation Dependencies.Testing
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/feature/tv/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
22 |
--------------------------------------------------------------------------------
/feature/tv/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/feature/tv/src/main/java/com/ahmedadel/robustandroid/feature/tv/di/TVComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.tv.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.FeatureScope
4 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceComponent
5 | import com.ahmedadel.robustandroid.feature.tv.usecase.TVUseCase
6 | import dagger.Component
7 |
8 | /**
9 | * Created at Tito on 3/15/19
10 | */
11 |
12 | @FeatureScope
13 | @Component(
14 | modules = [
15 | TVModule::class
16 | ],
17 | dependencies = [
18 | DataSourceComponent::class
19 | ]
20 | )
21 | interface TVComponent {
22 |
23 | fun getTVUseCase(): TVUseCase
24 |
25 | }
--------------------------------------------------------------------------------
/feature/tv/src/main/java/com/ahmedadel/robustandroid/feature/tv/di/TVComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.tv.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceComponent
5 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceComponentWrapper
6 |
7 | /**
8 | * Created at Tito on 3/17/19
9 | */
10 |
11 | open class TVComponentWrapper {
12 |
13 | private lateinit var component: TVComponent
14 |
15 | private fun initializeComponent(dataSourceComponent: DataSourceComponent) {
16 | component = DaggerTVComponent.builder()
17 | .tVModule(
18 | TVModule(
19 | dataSourceComponent.databaseManager().tvDao(),
20 | dataSourceComponent.apiService()
21 | )
22 | )
23 | .dataSourceComponent(dataSourceComponent)
24 | .build()
25 | }
26 |
27 | companion object {
28 |
29 | private var wrapper: TVComponentWrapper? = null
30 |
31 | @Synchronized
32 | private fun getInstance(application: Application): TVComponentWrapper {
33 | if (wrapper == null) {
34 | if (wrapper == null) {
35 | wrapper = TVComponentWrapper()
36 | wrapper!!.initializeComponent(DataSourceComponentWrapper.getComponent(application))
37 | }
38 | }
39 | return wrapper!!
40 | }
41 |
42 | fun getComponent(application: Application) = getInstance(application).component
43 |
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/feature/tv/src/main/java/com/ahmedadel/robustandroid/feature/tv/di/TVModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.tv.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.FeatureScope
4 | import com.ahmedadel.robustandroid.datalayer.local.dao.tv.TVDao
5 | import com.ahmedadel.robustandroid.datalayer.remote.ApiService
6 | import com.ahmedadel.robustandroid.feature.tv.mapper.TVMapper
7 | import com.ahmedadel.robustandroid.feature.tv.repository.TVRepository
8 | import com.ahmedadel.robustandroid.feature.tv.usecase.TVUseCase
9 | import dagger.Module
10 | import dagger.Provides
11 |
12 | /**
13 | * Created at Tito on 3/15/19
14 | */
15 |
16 | @Module
17 | class TVModule(
18 | private val local: TVDao,
19 | private val remote: ApiService
20 | ) {
21 |
22 | @Provides
23 | @FeatureScope
24 | fun providesTVMapper() = TVMapper()
25 |
26 | @Provides
27 | @FeatureScope
28 | fun provideTVRepository(mapper: TVMapper) = TVRepository(local, remote, mapper)
29 |
30 | @Provides
31 | @FeatureScope
32 | fun provideTVUseCase(repository: TVRepository) = TVUseCase(repository)
33 |
34 | }
--------------------------------------------------------------------------------
/feature/tv/src/main/java/com/ahmedadel/robustandroid/feature/tv/entity/TVEntity.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.tv.entity
2 |
3 | import com.ahmedadel.robustandroid.models.EntityModel
4 |
5 | /**
6 | * Created at Tito on 3/15/19
7 | */
8 |
9 | data class TVEntity(
10 |
11 | val id: Int = 0,
12 |
13 | val overview: String? = null,
14 |
15 | val originalLanguage: String? = null,
16 |
17 | val posterPath: String? = null,
18 |
19 | val voteAverage: Double = 0.0,
20 |
21 | val originalName: String? = null,
22 |
23 | val name: String? = null,
24 |
25 | val voteCount: Int = 0
26 |
27 | ) : EntityModel
--------------------------------------------------------------------------------
/feature/tv/src/main/java/com/ahmedadel/robustandroid/feature/tv/mapper/TVMapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.tv.mapper
2 |
3 | import com.ahmedadel.robustandroid.feature.tv.entity.TVEntity
4 | import com.ahmedadel.robustandroid.models.local.tv.TVLocal
5 | import com.ahmedadel.robustandroid.models.mappers.MapFromRemoteToEntity
6 | import com.ahmedadel.robustandroid.models.remote.tv.TVRemote
7 |
8 | /**
9 | * Created at Tito on 3/15/19
10 | */
11 |
12 | class TVMapper : MapFromRemoteToEntity {
13 |
14 | override fun mapFromRemoteToEntity(model: TVRemote): TVEntity {
15 | with(model) {
16 | return TVEntity(
17 | id = id,
18 | overview = overview,
19 | originalLanguage = originalLanguage,
20 | posterPath = posterPath,
21 | voteAverage = voteAverage,
22 | originalName = originalName,
23 | name = name,
24 | voteCount = voteCount
25 | )
26 | }
27 | }
28 |
29 | override fun mapFromLocalToEntity(model: TVLocal): TVEntity {
30 | with(model) {
31 | return TVEntity(
32 | id = id,
33 | overview = overview,
34 | originalLanguage = originalLanguage,
35 | posterPath = posterPath,
36 | voteAverage = voteAverage,
37 | originalName = originalName,
38 | name = name,
39 | voteCount = voteCount
40 | )
41 | }
42 | }
43 |
44 | override fun mapFromRemoteToLocal(model: TVRemote): TVLocal {
45 | with(model) {
46 | return TVLocal(
47 | id = id,
48 | firstAirDate = firstAirDate,
49 | overview = overview,
50 | originalLanguage = originalLanguage,
51 | posterPath = posterPath,
52 | backdropPath = backdropPath,
53 | popularity = popularity,
54 | voteAverage = voteAverage,
55 | originalName = originalName,
56 | name = name,
57 | voteCount = voteCount
58 | )
59 | }
60 | }
61 |
62 | }
--------------------------------------------------------------------------------
/feature/tv/src/main/java/com/ahmedadel/robustandroid/feature/tv/usecase/TVUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.tv.usecase
2 |
3 | import com.ahmedadel.robustandroid.feature.tv.repository.TVRepository
4 | import javax.inject.Inject
5 |
6 | /**
7 | * Created at Tito on 3/15/19
8 | */
9 |
10 | class TVUseCase
11 | @Inject
12 | constructor(private val repository: TVRepository) {
13 |
14 | fun getTVs(pageNumber: Int) = repository.getTVs(pageNumber)
15 |
16 | fun getTV(tvId: Int) = repository.getTV(tvId)
17 |
18 | fun getSimilarTVs(tvId: Int) = repository.getSimilarTVs(tvId)
19 |
20 | fun getRecommendationTVs(tvId: Int) = repository.getRecommendationTVs(tvId)
21 |
22 | }
--------------------------------------------------------------------------------
/feature/tv/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | tv
3 |
4 |
--------------------------------------------------------------------------------
/feature/tv/src/test/java/com/ahmedadel/robustandroid/feature/tv/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.feature.tv;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/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=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeAreEGDroid/Robust-Android-App/314fbfd40061f72f96424c462f955c7a581be799/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Mar 12 21:34:13 EET 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
7 |
--------------------------------------------------------------------------------
/models/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/models/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.*
2 |
3 | apply {
4 | plugin("com.android.library")
5 | plugin("kotlin-android-extensions")
6 | plugin("kotlin-android")
7 | plugin("kotlin-kapt")
8 | }
9 |
10 | android {
11 | compileSdkVersion Versions.compileSdkVersion
12 |
13 | defaultConfig {
14 |
15 | minSdkVersion Versions.minSdkVersion
16 | targetSdkVersion Versions.targetSdkVersion
17 |
18 | versionCode Versions.versionCode
19 | versionName Versions.versionName
20 |
21 | }
22 |
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 |
30 | }
31 |
32 | dependencies {
33 |
34 | implementation fileTree(dir: 'libs', include: ['*.jar'])
35 |
36 | implementation Dependencies.AppCompact
37 |
38 | implementation Dependencies.KotlinStdLib
39 |
40 | implementation Dependencies.Moshi
41 |
42 | api Dependencies.Room
43 | kapt Dependencies.RoomKapt
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/models/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
22 |
--------------------------------------------------------------------------------
/models/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/EntityModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models
2 |
3 | /**
4 | * Created at Tito on 3/15/19
5 | *
6 | * Parent class that will be extended from any entity models.
7 | */
8 | interface EntityModel
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/UiModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models
2 |
3 | /**
4 | * Created at Tito on 3/15/19
5 | *
6 | * Parent class that will be extended from any ui models.
7 | */
8 |
9 | interface UiModel
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/local/movie/MovieLocal.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models.local.movie
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 |
7 | /**
8 | * Created at Tito on 3/15/19
9 | *
10 | * Movie local model that will be used as a table in Room database.
11 | */
12 |
13 | @Entity(tableName = "movie")
14 | data class MovieLocal (
15 |
16 | @PrimaryKey(autoGenerate = true)
17 | val id: Int,
18 |
19 | @ColumnInfo(name = "overview")
20 | val overview: String? = null,
21 |
22 | @ColumnInfo(name = "original_language")
23 | val originalLanguage: String? = null,
24 |
25 | @ColumnInfo(name = "original_title")
26 | val originalTitle: String? = null,
27 |
28 | @ColumnInfo(name = "video")
29 | val video: Boolean = false,
30 |
31 | @ColumnInfo(name = "title")
32 | val title: String? = null,
33 |
34 | @ColumnInfo(name = "poster_path")
35 | val posterPath: String? = null,
36 |
37 | @ColumnInfo(name = "backdrop_path")
38 | val backdropPath: String? = null,
39 |
40 | @ColumnInfo(name = "release_date")
41 | val releaseDate: String? = null,
42 |
43 | @ColumnInfo(name = "popularity")
44 | val popularity: Double = 0.0,
45 |
46 | @ColumnInfo(name = "vote_average")
47 | val voteAverage: Double = 0.0,
48 |
49 | @ColumnInfo(name = "adult")
50 | val adult: Boolean = false,
51 |
52 | @ColumnInfo(name = "vote_count")
53 | val voteCount: Int = 0
54 |
55 | )
56 |
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/local/person/PersonLocal.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models.local.person
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 |
7 | /**
8 | * Created at Tito on 3/15/19
9 | *
10 | * Person local model that will be used as a table in Room database.
11 | */
12 |
13 | @Entity(tableName = "person")
14 | data class PersonLocal(
15 |
16 | @PrimaryKey
17 | val id: Int = 0,
18 |
19 | @ColumnInfo(name = "popularity")
20 | val popularity: Double = 0.0,
21 |
22 | @ColumnInfo(name = "name")
23 | val name: String? = null,
24 |
25 | @ColumnInfo(name = "profile_path")
26 | val profilePath: String? = null,
27 |
28 | @ColumnInfo(name = "adult")
29 | val adult: Boolean = false
30 |
31 | )
32 |
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/local/tv/TVLocal.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models.local.tv
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 |
7 | /**
8 | * Created at Tito on 3/15/19
9 | *
10 | * TV local model that will be used as a table in Room database.
11 | */
12 |
13 | @Entity(tableName = "tv")
14 | data class TVLocal(
15 |
16 | @PrimaryKey
17 | val id: Int = 0,
18 |
19 | @ColumnInfo(name = "first_air_date")
20 | val firstAirDate: String? = null,
21 |
22 | @ColumnInfo(name = "overview")
23 | val overview: String? = null,
24 |
25 | @ColumnInfo(name = "original_language")
26 | val originalLanguage: String? = null,
27 |
28 | @ColumnInfo(name = "poster_path")
29 | val posterPath: String? = null,
30 |
31 | @ColumnInfo(name = "backdrop_path")
32 | val backdropPath: String? = null,
33 |
34 | @ColumnInfo(name = "popularity")
35 | val popularity: Double = 0.0,
36 |
37 | @ColumnInfo(name = "vote_average")
38 | val voteAverage: Double = 0.0,
39 |
40 | @ColumnInfo(name = "original_name")
41 | val originalName: String? = null,
42 |
43 | @ColumnInfo(name = "name")
44 | val name: String? = null,
45 |
46 | @ColumnInfo(name = "vote_count")
47 | val voteCount: Int = 0
48 |
49 | )
50 |
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/mappers/MapFromEntityToUi.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models.mappers
2 |
3 | import com.ahmedadel.robustandroid.models.UiModel
4 |
5 | /**
6 | * Created at Tito on 3/15/19
7 | *
8 | * Map from entity use case to ui model.
9 | */
10 |
11 | interface MapFromEntityToUi {
12 | fun mapToUiModel(model: E): U
13 | fun mapToUiModelList(model: List): List
14 | }
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/mappers/MapFromRemoteToEntity.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models.mappers
2 |
3 | import com.ahmedadel.robustandroid.models.EntityModel
4 |
5 | /**
6 | * Created at Tito on 3/15/19
7 | *
8 | * Map from remote to entity use case model.
9 | */
10 |
11 | interface MapFromRemoteToEntity {
12 |
13 | fun mapFromRemoteToEntity(model: R): E
14 | fun mapFromLocalToEntity(model: L): E
15 | fun mapFromRemoteToLocal(model: R): L
16 |
17 | }
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/remote/movie/MovieListRemote.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models.remote.movie
2 |
3 | import com.squareup.moshi.Json
4 |
5 | data class MovieListRemote(
6 |
7 | @Json(name = "page")
8 | val page: Int = 0,
9 |
10 | @Json(name = "total_pages")
11 | val totalPages: Int = 0,
12 |
13 | @Json(name = "results")
14 | val movies: List? = null,
15 |
16 | @Json(name = "total_results")
17 | val totalResults: Int = 0
18 |
19 | )
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/remote/movie/MovieRemote.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models.remote.movie
2 |
3 | import com.squareup.moshi.Json
4 |
5 | data class MovieRemote(
6 |
7 | @Json(name = "id")
8 | val id: Int = 0,
9 |
10 | @Json(name = "overview")
11 | val overview: String? = null,
12 |
13 | @Json(name = "original_language")
14 | val originalLanguage: String? = null,
15 |
16 | @Json(name = "original_title")
17 | val originalTitle: String? = null,
18 |
19 | @Json(name = "video")
20 | val isVideo: Boolean = false,
21 |
22 | @Json(name = "title")
23 | val title: String? = null,
24 |
25 | @Json(name = "poster_path")
26 | val posterPath: String? = null,
27 |
28 | @Json(name = "backdrop_path")
29 | val backdropPath: String? = null,
30 |
31 | @Json(name = "release_date")
32 | val releaseDate: String? = null,
33 |
34 | @Json(name = "popularity")
35 | val popularity: Double = 0.0,
36 |
37 | @Json(name = "vote_average")
38 | val voteAverage: Double = 0.0,
39 |
40 | @Json(name = "adult")
41 | val isAdult: Boolean = false,
42 |
43 | @Json(name = "vote_count")
44 | val voteCount: Int = 0
45 |
46 | )
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/remote/person/PersonListRemote.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models.remote.person
2 |
3 | import com.squareup.moshi.Json
4 |
5 | data class PersonListRemote(
6 |
7 | @Json(name = "page")
8 | val page: Int = 0,
9 |
10 | @Json(name = "total_pages")
11 | val totalPages: Int = 0,
12 |
13 | @Json(name = "results")
14 | val persons: List? = null,
15 |
16 | @Json(name = "total_results")
17 | val totalResults: Int = 0
18 |
19 | )
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/remote/person/PersonRemote.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models.remote.person
2 |
3 | import com.squareup.moshi.Json
4 |
5 | data class PersonRemote(
6 |
7 | @Json(name = "id")
8 | val id: Int = 0,
9 |
10 | @Json(name = "popularity")
11 | val popularity: Double = 0.0,
12 |
13 | @Json(name = "name")
14 | val name: String? = null,
15 |
16 | @Json(name = "profile_path")
17 | val profilePath: String? = null,
18 |
19 | @Json(name = "adult")
20 | val isAdult: Boolean = false
21 |
22 | )
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/remote/tv/TVListRemote.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models.remote.tv
2 |
3 | import com.squareup.moshi.Json
4 |
5 | data class TVListRemote(
6 |
7 | @Json(name = "page")
8 | val page: Int = 0,
9 |
10 | @Json(name = "total_pages")
11 | val totalPages: Int = 0,
12 |
13 | @Json(name = "results")
14 | val tVs: List? = null,
15 |
16 | @Json(name = "total_results")
17 | val totalResults: Int = 0
18 |
19 | )
--------------------------------------------------------------------------------
/models/src/main/java/com/ahmedadel/robustandroid/models/remote/tv/TVRemote.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.models.remote.tv
2 |
3 | import com.squareup.moshi.Json
4 |
5 | data class TVRemote(
6 |
7 | @Json(name = "id")
8 | val id: Int = 0,
9 |
10 | @Json(name = "first_air_date")
11 | val firstAirDate: String? = null,
12 |
13 | @Json(name = "overview")
14 | val overview: String? = null,
15 |
16 | @Json(name = "original_language")
17 | val originalLanguage: String? = null,
18 |
19 | @Json(name = "poster_path")
20 | val posterPath: String? = null,
21 |
22 | @Json(name = "backdrop_path")
23 | val backdropPath: String? = null,
24 |
25 | @Json(name = "popularity")
26 | val popularity: Double = 0.0,
27 |
28 | @Json(name = "vote_average")
29 | val voteAverage: Double = 0.0,
30 |
31 | @Json(name = "original_name")
32 | val originalName: String? = null,
33 |
34 | @Json(name = "name")
35 | val name: String? = null,
36 |
37 | @Json(name = "vote_count")
38 | val voteCount: Int = 0
39 |
40 | )
--------------------------------------------------------------------------------
/models/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | models
3 |
4 |
--------------------------------------------------------------------------------
/presentation/mvi/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/presentation/mvi/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.*
2 |
3 | apply {
4 | plugin("com.android.library")
5 | plugin("kotlin-android-extensions")
6 | plugin("kotlin-android")
7 | plugin("kotlin-kapt")
8 | }
9 |
10 | android {
11 |
12 | compileSdkVersion Versions.compileSdkVersion
13 |
14 | defaultConfig {
15 |
16 | minSdkVersion Versions.minSdkVersion
17 | targetSdkVersion Versions.targetSdkVersion
18 |
19 | versionCode Versions.versionCode
20 | versionName Versions.versionName
21 |
22 | }
23 |
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 |
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 |
37 | api project(':feature:movie')
38 | api project(':feature:tv')
39 |
40 | implementation Dependencies.AppCompact
41 |
42 | implementation Dependencies.AndroidArchComponent
43 |
44 | implementation Dependencies.KotlinStdLib
45 |
46 | implementation Dependencies.RxJava
47 |
48 | implementation Dependencies.Dagger
49 | kapt Dependencies.DaggerKapt
50 |
51 | implementation Dependencies.Testing
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/presentation/mvi/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
22 |
--------------------------------------------------------------------------------
/presentation/mvi/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/MviAction.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi
2 |
3 | /**
4 | * Created at Tito on 3/19/19
5 | *
6 | * This interface just creates a type that we will use to tag actions classes with and does not need any functions
7 | * till now.
8 | */
9 |
10 | interface MviAction
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/MviIntent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi
2 |
3 | /**
4 | * Created at Tito on 3/19/19
5 | *
6 | * This interface just creates a type that we will use to tag intent classes with and does not need any functions
7 | * till now.
8 | */
9 |
10 | interface MviIntent
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/MviProcessor.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi
2 |
3 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProvider
4 | import io.reactivex.Scheduler
5 | import io.reactivex.subjects.PublishSubject
6 |
7 | /**
8 | * Created at Tito on 3/19/19
9 | */
10 |
11 | abstract class MviProcessor
12 | (baseSchedulerProvider: BaseSchedulerProvider) {
13 |
14 | protected val subscribeOn: Scheduler = baseSchedulerProvider.io()
15 | protected val observeOn: Scheduler = baseSchedulerProvider.ui()
16 |
17 | val actions: PublishSubject = PublishSubject.create()
18 |
19 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/MviResult.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi
2 |
3 | /**
4 | * Created at Tito on 3/19/19
5 | *
6 | * This interface just creates a type that we will use to tag result classes with and does not need any functions
7 | * till now.
8 | */
9 |
10 | interface MviResult
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/MviView.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi
2 |
3 | import io.reactivex.Observable
4 |
5 | /**
6 | * Created at Tito on 3/19/19
7 | *
8 | * The view must provide intents for the [MviViewModel] and also be able to render new state coming from
9 | * [MviViewModel].
10 | * It is typed by an [MviIntent] and [MviViewState]
11 | */
12 |
13 | interface MviView {
14 | fun bind()
15 | fun intents(): Observable
16 | fun render(state: S)
17 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/MviViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi
2 |
3 | import androidx.lifecycle.ViewModel
4 | import io.reactivex.Observable
5 | import io.reactivex.disposables.CompositeDisposable
6 |
7 | /**
8 | * Created at Tito on 3/19/19
9 | *
10 | * Process intents coming from the view and provide a stream of states for the view to observe.
11 | * It is typed by an [MviIntent] and [MviViewState]
12 | */
13 |
14 | abstract class MviViewModel :
15 | ViewModel() {
16 |
17 | val disposables: CompositeDisposable = CompositeDisposable()
18 |
19 | private val tag by lazy {
20 | javaClass.simpleName
21 | }
22 |
23 | abstract fun processIntents(intents: Observable)
24 |
25 | abstract fun states(): Observable
26 |
27 | protected abstract fun actionFromIntent(intent: I): A
28 |
29 | override fun onCleared() {
30 | disposables.clear()
31 | }
32 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/MviViewState.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi
2 |
3 | /**
4 | * Created at Tito on 3/19/19
5 | *
6 | * This interface just creates a type that we will use to tag state classes with and does not need any functions
7 | * till now.
8 | */
9 |
10 | interface MviViewState
11 |
12 | abstract class CompoundViewState<
13 | V1 : MviViewState,
14 | V2 : MviViewState,
15 | V3 : MviViewState>(
16 | open val first: V1,
17 | open val second: V2,
18 | open val third: V3
19 | ) : MviViewState
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/movie/di/MovieDetailsComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi.movie.di
2 |
3 | import androidx.lifecycle.ViewModelProvider
4 | import com.ahmedadel.robustandroid.core.di.PresentationScope
5 | import com.ahmedadel.robustandroid.core.di.ViewModelFactoryModule
6 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProviderModule
7 | import com.ahmedadel.robustandroid.feature.movie.di.MovieComponent
8 | import com.ahmedadel.robustandroid.presentation.mvi.movie.MovieDetailsViewModel
9 | import dagger.Component
10 |
11 | /**
12 | * Created at Tito on 3/19/19
13 | */
14 |
15 | @PresentationScope
16 | @Component(
17 | modules = [
18 | ViewModelFactoryModule::class,
19 | MovieDetailsViewModelModule::class,
20 | MovieDetailsModule::class,
21 | BaseSchedulerProviderModule::class
22 | ],
23 | dependencies = [
24 | MovieComponent::class
25 | ]
26 | )
27 | interface MovieDetailsComponent {
28 |
29 | fun movieDetailsViewModel(): MovieDetailsViewModel
30 |
31 | fun viewModelFactory(): ViewModelProvider.Factory
32 |
33 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/movie/di/MovieDetailsComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi.movie.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.feature.movie.di.MovieComponent
5 | import com.ahmedadel.robustandroid.feature.movie.di.MovieComponentWrapper
6 |
7 | /**
8 | * Created at Tito on 3/20/19
9 | */
10 |
11 | open class MovieDetailsComponentWrapper {
12 |
13 | private lateinit var component: MovieDetailsComponent
14 |
15 | private fun initializeComponent(movieComponent: MovieComponent) {
16 | component = DaggerMovieDetailsComponent.builder()
17 | .movieComponent(movieComponent)
18 | .build()
19 | }
20 |
21 | companion object {
22 |
23 | private var wrapper: MovieDetailsComponentWrapper? = null
24 |
25 | @Synchronized
26 | private fun getInstance(application: Application): MovieDetailsComponentWrapper {
27 | if (wrapper == null) {
28 | if (wrapper == null) {
29 | wrapper = MovieDetailsComponentWrapper()
30 | wrapper!!.initializeComponent(MovieComponentWrapper.getComponent(application))
31 | }
32 | }
33 | return wrapper!!
34 | }
35 |
36 | fun getComponent(application: Application) = getInstance(application).component
37 |
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/movie/di/MovieDetailsModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi.movie.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.PresentationScope
4 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProvider
5 | import com.ahmedadel.robustandroid.feature.movie.usecase.MovieUseCase
6 | import com.ahmedadel.robustandroid.presentation.mvi.movie.MovieDetailsProcessor
7 | import com.ahmedadel.robustandroid.presentation.mvi.movie.mapper.MovieMapper
8 | import dagger.Module
9 | import dagger.Provides
10 |
11 | /**
12 | * Created at Tito on 3/19/19
13 | */
14 |
15 | @Module
16 | class MovieDetailsModule {
17 |
18 | @Provides
19 | @PresentationScope
20 | fun providesMovieMapper() = MovieMapper()
21 |
22 | @Provides
23 | @PresentationScope
24 | fun providesMovieDetailsProcessor(
25 | baseSchedulerProvider: BaseSchedulerProvider,
26 | movieUseCase: MovieUseCase,
27 | mapper: MovieMapper
28 | ): MovieDetailsProcessor = MovieDetailsProcessor(baseSchedulerProvider, movieUseCase, mapper)
29 |
30 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/movie/di/MovieDetailsViewModelModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi.movie.di
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.ahmedadel.robustandroid.core.di.PresentationScope
5 | import com.ahmedadel.robustandroid.core.di.ViewModelKey
6 | import com.ahmedadel.robustandroid.presentation.mvi.movie.MovieDetailsViewModel
7 | import dagger.Binds
8 | import dagger.Module
9 | import dagger.multibindings.IntoMap
10 |
11 | /**
12 | * Created at Tito on 3/20/19
13 | */
14 |
15 | @Module
16 | abstract class MovieDetailsViewModelModule {
17 |
18 | @Binds
19 | @IntoMap
20 | @ViewModelKey(MovieDetailsViewModel::class)
21 | @PresentationScope
22 | internal abstract fun provideHomeViewModel(movieDetailsViewModel: MovieDetailsViewModel): ViewModel
23 |
24 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/movie/mapper/MovieMapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi.movie.mapper
2 |
3 | import com.ahmedadel.robustandroid.feature.movie.entity.MovieEntity
4 | import com.ahmedadel.robustandroid.models.mappers.MapFromEntityToUi
5 | import com.ahmedadel.robustandroid.presentation.mvi.movie.uimodel.MovieUiModel
6 |
7 | /**
8 | * Created at Tito on 3/19/19
9 | */
10 |
11 | class MovieMapper : MapFromEntityToUi {
12 |
13 | override fun mapToUiModel(model: MovieEntity): MovieUiModel {
14 | with(model) {
15 | return MovieUiModel(
16 | id = id,
17 | title = title,
18 | overview = overview,
19 | originalLanguage = originalLanguage,
20 | originalTitle = originalTitle,
21 | posterPath = posterPath,
22 | releaseDate = releaseDate,
23 | voteAverage = voteAverage,
24 | isAdult = isAdult
25 | )
26 | }
27 | }
28 |
29 | override fun mapToUiModelList(model: List): List {
30 | return model.map {
31 | with(it) {
32 | MovieUiModel(
33 | id = id,
34 | title = title,
35 | overview = overview,
36 | originalLanguage = originalLanguage,
37 | originalTitle = originalTitle,
38 | posterPath = posterPath,
39 | releaseDate = releaseDate,
40 | voteAverage = voteAverage,
41 | isAdult = isAdult
42 | )
43 | }
44 | }
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/movie/uimodel/MovieUiModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi.movie.uimodel
2 |
3 | import com.ahmedadel.robustandroid.models.UiModel
4 |
5 | /**
6 | * Created at Tito on 3/19/19
7 | */
8 |
9 | data class MovieUiModel(
10 |
11 | val id: Int = 0,
12 |
13 | val title: String? = null,
14 |
15 | val overview: String? = null,
16 |
17 | val originalLanguage: String? = null,
18 |
19 | val originalTitle: String? = null,
20 |
21 | val posterPath: String? = null,
22 |
23 | val releaseDate: String? = null,
24 |
25 | val voteAverage: Double = 0.0,
26 |
27 | val isAdult: Boolean = false
28 |
29 | ) : UiModel
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/tv/di/TVDetailsComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi.tv.di
2 |
3 | import androidx.lifecycle.ViewModelProvider
4 | import com.ahmedadel.robustandroid.core.di.PresentationScope
5 | import com.ahmedadel.robustandroid.core.di.ViewModelFactoryModule
6 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProviderModule
7 | import com.ahmedadel.robustandroid.feature.tv.di.TVComponent
8 | import com.ahmedadel.robustandroid.presentation.mvi.tv.TVDetailsViewModel
9 | import dagger.Component
10 |
11 | /**
12 | * Created at Tito on 3/20/19
13 | */
14 |
15 | @PresentationScope
16 | @Component(
17 | modules = [
18 | ViewModelFactoryModule::class,
19 | TVDetailsViewModelModule::class,
20 | TVDetailsModule::class,
21 | BaseSchedulerProviderModule::class
22 | ],
23 | dependencies = [
24 | TVComponent::class
25 | ]
26 | )
27 | interface TVDetailsComponent {
28 |
29 | fun tvDetailsViewModel(): TVDetailsViewModel
30 |
31 | fun viewModelFactory(): ViewModelProvider.Factory
32 |
33 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/tv/di/TVDetailsComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi.tv.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.feature.tv.di.TVComponent
5 | import com.ahmedadel.robustandroid.feature.tv.di.TVComponentWrapper
6 |
7 | /**
8 | * Created at Tito on 3/20/19
9 | */
10 |
11 | open class TVDetailsComponentWrapper {
12 |
13 | private lateinit var component: TVDetailsComponent
14 |
15 | private fun initializeComponent(tvComponent: TVComponent) {
16 | component = DaggerTVDetailsComponent.builder()
17 | .tVComponent(tvComponent)
18 | .build()
19 | }
20 |
21 | companion object {
22 |
23 | private var wrapper: TVDetailsComponentWrapper? = null
24 |
25 | @Synchronized
26 | private fun getInstance(application: Application): TVDetailsComponentWrapper {
27 | if (wrapper == null) {
28 | if (wrapper == null) {
29 | wrapper = TVDetailsComponentWrapper()
30 | wrapper!!.initializeComponent(TVComponentWrapper.getComponent(application))
31 | }
32 | }
33 | return wrapper!!
34 | }
35 |
36 | fun getComponent(application: Application) = getInstance(application).component
37 |
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/tv/di/TVDetailsModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi.tv.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.PresentationScope
4 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProvider
5 | import com.ahmedadel.robustandroid.feature.tv.usecase.TVUseCase
6 | import com.ahmedadel.robustandroid.presentation.mvi.tv.TVDetailsProcessor
7 | import com.ahmedadel.robustandroid.presentation.mvi.tv.mapper.TVMapper
8 | import dagger.Module
9 | import dagger.Provides
10 |
11 | /**
12 | * Created at Tito on 3/20/19
13 | */
14 |
15 | @Module
16 | class TVDetailsModule {
17 |
18 | @Provides
19 | @PresentationScope
20 | fun providesTVMapper() = TVMapper()
21 |
22 | @Provides
23 | @PresentationScope
24 | fun providesTVDetailsProcessor(
25 | baseSchedulerProvider: BaseSchedulerProvider,
26 | tvUseCase: TVUseCase,
27 | mapper: TVMapper
28 | ): TVDetailsProcessor = TVDetailsProcessor(baseSchedulerProvider, tvUseCase, mapper)
29 |
30 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/tv/di/TVDetailsViewModelModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi.tv.di
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.ahmedadel.robustandroid.core.di.PresentationScope
5 | import com.ahmedadel.robustandroid.core.di.ViewModelKey
6 | import com.ahmedadel.robustandroid.presentation.mvi.tv.TVDetailsViewModel
7 | import dagger.Binds
8 | import dagger.Module
9 | import dagger.multibindings.IntoMap
10 |
11 | /**
12 | * Created at Tito on 3/20/19
13 | */
14 |
15 | @Module
16 | abstract class TVDetailsViewModelModule {
17 |
18 | @Binds
19 | @IntoMap
20 | @ViewModelKey(TVDetailsViewModel::class)
21 | @PresentationScope
22 | internal abstract fun provideHomeViewModel(tvDetailsViewModel: TVDetailsViewModel): ViewModel
23 |
24 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/tv/mapper/TVMapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi.tv.mapper
2 |
3 | import com.ahmedadel.robustandroid.feature.tv.entity.TVEntity
4 | import com.ahmedadel.robustandroid.models.mappers.MapFromEntityToUi
5 | import com.ahmedadel.robustandroid.presentation.mvi.tv.uimodel.TVUiModel
6 |
7 | /**
8 | * Created at Tito on 3/20/19
9 | */
10 |
11 | class TVMapper : MapFromEntityToUi {
12 |
13 | override fun mapToUiModel(model: TVEntity): TVUiModel {
14 | with(model) {
15 | return TVUiModel(
16 | id,
17 | name,
18 | overview,
19 | originalName,
20 | originalLanguage,
21 | posterPath,
22 | voteCount
23 | )
24 | }
25 | }
26 |
27 | override fun mapToUiModelList(model: List): List {
28 | return model.map {
29 | with(it) {
30 | TVUiModel(
31 | id,
32 | name,
33 | overview,
34 | originalName,
35 | originalLanguage,
36 | posterPath,
37 | voteCount
38 | )
39 | }
40 | }
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/presentation/mvi/src/main/java/com/ahmedadel/robustandroid/presentation/mvi/tv/uimodel/TVUiModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi.tv.uimodel
2 |
3 | import com.ahmedadel.robustandroid.models.UiModel
4 |
5 | /**
6 | * Created at Tito on 3/16/19
7 | */
8 |
9 | data class TVUiModel (
10 |
11 | val id: Int = 0,
12 |
13 | val name: String? = null,
14 |
15 | val overview: String? = null,
16 |
17 | val originalName: String? = null,
18 |
19 | val originalLanguage: String? = null,
20 |
21 | val posterPath: String? = null,
22 |
23 | val voteCount: Int = 0
24 |
25 | ) : UiModel
--------------------------------------------------------------------------------
/presentation/mvi/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | mvi
3 |
4 |
--------------------------------------------------------------------------------
/presentation/mvi/src/test/java/com/ahmedadel/robustandroid/presentation/mvi/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvi;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/presentation/mvp/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/presentation/mvp/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.*
2 |
3 | apply {
4 | plugin("com.android.library")
5 | plugin("kotlin-android-extensions")
6 | plugin("kotlin-android")
7 | plugin("kotlin-kapt")
8 | }
9 |
10 | android {
11 |
12 | compileSdkVersion Versions.compileSdkVersion
13 |
14 | defaultConfig {
15 |
16 | minSdkVersion Versions.minSdkVersion
17 | targetSdkVersion Versions.targetSdkVersion
18 |
19 | versionCode Versions.versionCode
20 | versionName Versions.versionName
21 |
22 | }
23 |
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 |
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 |
37 | api project(':feature:movie')
38 | api project(':feature:person')
39 | api project(':feature:tv')
40 |
41 | implementation Dependencies.AppCompact
42 |
43 | implementation Dependencies.AndroidArchComponent
44 |
45 | implementation Dependencies.KotlinStdLib
46 |
47 | implementation Dependencies.RxJava
48 |
49 | implementation Dependencies.Dagger
50 | kapt Dependencies.DaggerKapt
51 |
52 | implementation Dependencies.Testing
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/presentation/mvp/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
22 |
--------------------------------------------------------------------------------
/presentation/mvp/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/BasePresenter.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp
2 |
3 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProvider
4 | import io.reactivex.Flowable
5 | import io.reactivex.Scheduler
6 | import io.reactivex.disposables.CompositeDisposable
7 | import io.reactivex.disposables.Disposable
8 | import io.reactivex.functions.Consumer
9 | import org.reactivestreams.Subscription
10 | import java.lang.ref.WeakReference
11 |
12 | /**
13 | * Created at Tito on 3/17/19
14 | */
15 |
16 | abstract class BasePresenter
17 | constructor(
18 | baseSchedulerProvider: BaseSchedulerProvider
19 | ) {
20 |
21 | private var view: WeakReference? = null
22 |
23 | private val subscribeOn: Scheduler = baseSchedulerProvider.io()
24 | private val observeOn: Scheduler = baseSchedulerProvider.ui()
25 | private val disposables: CompositeDisposable = CompositeDisposable()
26 |
27 | fun setView(view: V) {
28 | this.view = WeakReference(view)
29 | }
30 |
31 | protected fun getView(): V? = view?.get()
32 |
33 | protected fun execute(
34 | loadingConsumer: Consumer,
35 | successConsumer: Consumer,
36 | throwableConsumer: Consumer,
37 | useCase: Flowable
38 | ) {
39 | val observable = useCase
40 | .doOnSubscribe(loadingConsumer)
41 | .subscribeOn(subscribeOn)
42 | .observeOn(observeOn)
43 | addDisposable(observable.subscribe(successConsumer, throwableConsumer))
44 | }
45 |
46 | private fun dispose() {
47 | if (!disposables.isDisposed) {
48 | disposables.dispose()
49 | }
50 | }
51 |
52 | private fun addDisposable(disposable: Disposable) {
53 | disposables.add(disposable)
54 | }
55 |
56 | fun clear() {
57 | dispose()
58 | }
59 |
60 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/BaseView.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp
2 |
3 | interface BaseView {
4 |
5 | fun showErrorMessage(message: String?)
6 |
7 | fun showLoading(isLoading: Boolean, isFirstPage: Boolean)
8 |
9 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/movielist/MovieListContract.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.movielist
2 |
3 | import com.ahmedadel.robustandroid.feature.movie.entity.MovieEntity
4 | import com.ahmedadel.robustandroid.presentation.mvp.BaseView
5 | import com.ahmedadel.robustandroid.presentation.mvp.movielist.uimodel.MovieUiModel
6 | import io.reactivex.Flowable
7 |
8 | /**
9 | * Created at Tito on 3/17/19
10 | */
11 |
12 | interface MovieListContract {
13 |
14 | interface View : BaseView {
15 |
16 | fun showMovies(movies: List)
17 |
18 | }
19 |
20 | interface Presenter {
21 |
22 | fun callMovies(pageNumber: Int)
23 |
24 | }
25 |
26 | interface Model {
27 |
28 | fun getMovies(pageNumber: Int): Flowable?>
29 |
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/movielist/MovieListModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.movielist
2 |
3 | import com.ahmedadel.robustandroid.feature.movie.usecase.MovieUseCase
4 | import javax.inject.Inject
5 |
6 | /**
7 | * Created at Tito on 3/17/19
8 | */
9 |
10 | class MovieListModel
11 | @Inject
12 | constructor(private val movieUseCase: MovieUseCase) : MovieListContract.Model {
13 |
14 | override fun getMovies(pageNumber: Int) = movieUseCase.getMovies(pageNumber)
15 |
16 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/movielist/MovieListPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.movielist
2 |
3 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProvider
4 | import com.ahmedadel.robustandroid.presentation.mvp.BasePresenter
5 | import com.ahmedadel.robustandroid.presentation.mvp.movielist.mapper.MovieMapper
6 | import io.reactivex.functions.Consumer
7 | import javax.inject.Inject
8 |
9 | /**
10 | * Created at Tito on 3/17/19
11 | */
12 |
13 | class MovieListPresenter
14 | @Inject
15 | constructor(
16 | baseSchedulerProvider: BaseSchedulerProvider,
17 | private val model: MovieListContract.Model,
18 | private val mapper: MovieMapper
19 | ) : BasePresenter(baseSchedulerProvider),
20 | MovieListContract.Presenter {
21 |
22 | override fun callMovies(pageNumber: Int) {
23 |
24 | execute(
25 | loadingConsumer = Consumer {
26 | getView()?.showLoading(true,pageNumber == 1)
27 | },
28 | successConsumer = Consumer { movieItemList ->
29 | getView()?.showLoading(false,pageNumber == 1)
30 | movieItemList?.let {
31 | getView()?.showMovies(mapper.mapToUiModelList(it))
32 | }
33 | },
34 | throwableConsumer = Consumer { throwable ->
35 | getView()?.showErrorMessage(throwable.message)
36 | },
37 | useCase = model.getMovies(pageNumber)
38 | )
39 |
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/movielist/di/MovieListComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.movielist.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.PresentationScope
4 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProviderModule
5 | import com.ahmedadel.robustandroid.feature.movie.di.MovieComponent
6 | import com.ahmedadel.robustandroid.presentation.mvp.movielist.MovieListContract
7 | import com.ahmedadel.robustandroid.presentation.mvp.movielist.MovieListPresenter
8 | import dagger.Component
9 |
10 | /**
11 | * Created at Tito on 3/17/19
12 | */
13 |
14 | @PresentationScope
15 | @Component(
16 | modules = [
17 | MovieListModule::class,
18 | BaseSchedulerProviderModule::class
19 | ],
20 | dependencies = [
21 | MovieComponent::class
22 | ]
23 | )
24 | interface MovieListComponent {
25 |
26 | fun movieListPresenter(): MovieListPresenter
27 |
28 | fun movieListModel() : MovieListContract.Model
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/movielist/di/MovieListComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.movielist.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.feature.movie.di.MovieComponent
5 | import com.ahmedadel.robustandroid.feature.movie.di.MovieComponentWrapper
6 |
7 | /**
8 | * Created at Tito on 3/17/19
9 | */
10 |
11 | open class MovieListComponentWrapper {
12 |
13 | private lateinit var component: MovieListComponent
14 |
15 | private fun initializeComponent(movieComponent: MovieComponent) {
16 | component = DaggerMovieListComponent.builder()
17 | .movieComponent(movieComponent)
18 | .build()
19 | }
20 |
21 | companion object {
22 |
23 | private var wrapper: MovieListComponentWrapper? = null
24 |
25 | @Synchronized
26 | private fun getInstance(application: Application): MovieListComponentWrapper {
27 | if (wrapper == null) {
28 | if (wrapper == null) {
29 | wrapper = MovieListComponentWrapper()
30 | wrapper!!.initializeComponent(MovieComponentWrapper.getComponent(application))
31 | }
32 | }
33 | return wrapper!!
34 | }
35 |
36 | fun getComponent(application: Application) = getInstance(application).component
37 |
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/movielist/di/MovieListModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.movielist.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.PresentationScope
4 | import com.ahmedadel.robustandroid.feature.movie.usecase.MovieUseCase
5 | import com.ahmedadel.robustandroid.presentation.mvp.movielist.MovieListContract
6 | import com.ahmedadel.robustandroid.presentation.mvp.movielist.MovieListModel
7 | import com.ahmedadel.robustandroid.presentation.mvp.movielist.mapper.MovieMapper
8 | import dagger.Module
9 | import dagger.Provides
10 |
11 | /**
12 | * Created at Tito on 3/17/19
13 | */
14 |
15 | @Module
16 | class MovieListModule {
17 |
18 | @Provides
19 | @PresentationScope
20 | fun providesMovieMapper() = MovieMapper()
21 |
22 | @Provides
23 | @PresentationScope
24 | fun providesMovieListModel(movieUseCase: MovieUseCase): MovieListContract.Model = MovieListModel(movieUseCase)
25 |
26 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/movielist/mapper/MovieMapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.movielist.mapper
2 |
3 | import com.ahmedadel.robustandroid.feature.movie.entity.MovieEntity
4 | import com.ahmedadel.robustandroid.models.mappers.MapFromEntityToUi
5 | import com.ahmedadel.robustandroid.presentation.mvp.movielist.uimodel.MovieUiModel
6 |
7 | /**
8 | * Created at Tito on 3/17/19
9 | */
10 |
11 | class MovieMapper : MapFromEntityToUi {
12 |
13 | override fun mapToUiModel(model: MovieEntity): MovieUiModel {
14 | with(model) {
15 | return MovieUiModel(
16 | id = id,
17 | title = title,
18 | overview = overview,
19 | originalLanguage = originalLanguage,
20 | originalTitle = originalTitle,
21 | posterPath = posterPath,
22 | releaseDate = releaseDate,
23 | voteAverage = voteAverage,
24 | isAdult = isAdult
25 | )
26 | }
27 | }
28 |
29 | override fun mapToUiModelList(model: List): List {
30 | return model.map {
31 | with(it) {
32 | MovieUiModel(
33 | id = id,
34 | title = title,
35 | overview = overview,
36 | originalLanguage = originalLanguage,
37 | originalTitle = originalTitle,
38 | posterPath = posterPath,
39 | releaseDate = releaseDate,
40 | voteAverage = voteAverage,
41 | isAdult = isAdult
42 | )
43 | }
44 | }
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/movielist/uimodel/MovieUiModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.movielist.uimodel
2 |
3 | import com.ahmedadel.robustandroid.models.UiModel
4 |
5 | /**
6 | * Created at Tito on 3/17/19
7 | */
8 |
9 | data class MovieUiModel(
10 |
11 | val id: Int = 0,
12 |
13 | val title: String? = null,
14 |
15 | val overview: String? = null,
16 |
17 | val originalLanguage: String? = null,
18 |
19 | val originalTitle: String? = null,
20 |
21 | val posterPath: String? = null,
22 |
23 | val releaseDate: String? = null,
24 |
25 | val voteAverage: Double = 0.0,
26 |
27 | val isAdult: Boolean = false
28 |
29 | ) : UiModel
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/personlist/PersonListContract.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.personlist
2 |
3 | import com.ahmedadel.robustandroid.feature.person.entity.PersonEntity
4 | import com.ahmedadel.robustandroid.presentation.mvp.BaseView
5 | import com.ahmedadel.robustandroid.presentation.mvp.personlist.uimodel.PersonUiModel
6 | import io.reactivex.Flowable
7 |
8 | /**
9 | * Created at Tito on 3/17/19
10 | */
11 |
12 | interface PersonListContract {
13 |
14 | interface View : BaseView {
15 |
16 | fun showPersons(persons: List)
17 |
18 | }
19 |
20 | interface Presenter {
21 |
22 | fun callPersons(pageNumber: Int)
23 |
24 | }
25 |
26 | interface Model {
27 |
28 | fun getPersons(pageNumber: Int): Flowable?>
29 |
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/personlist/PersonListModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.personlist
2 |
3 | import com.ahmedadel.robustandroid.feature.person.usecase.PersonUseCase
4 | import javax.inject.Inject
5 |
6 | /**
7 | * Created at Tito on 3/17/19
8 | */
9 |
10 | class PersonListModel
11 | @Inject
12 | constructor(private val personUseCase: PersonUseCase) : PersonListContract.Model {
13 |
14 | override fun getPersons(pageNumber: Int) = personUseCase.getPersons(pageNumber)
15 |
16 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/personlist/PersonListPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.personlist
2 |
3 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProvider
4 | import com.ahmedadel.robustandroid.presentation.mvp.BasePresenter
5 | import com.ahmedadel.robustandroid.presentation.mvp.personlist.mapper.PersonMapper
6 | import io.reactivex.functions.Consumer
7 | import javax.inject.Inject
8 |
9 | /**
10 | * Created at Tito on 3/17/19
11 | */
12 |
13 | class PersonListPresenter
14 | @Inject
15 | constructor(
16 | baseSchedulerProvider: BaseSchedulerProvider,
17 | private val model: PersonListContract.Model,
18 | private val mapper: PersonMapper
19 | ) : BasePresenter(baseSchedulerProvider),
20 | PersonListContract.Presenter {
21 |
22 | override fun callPersons(pageNumber: Int) {
23 |
24 | execute(
25 | loadingConsumer = Consumer {
26 | getView()?.showLoading(true, pageNumber == 1)
27 | },
28 | successConsumer = Consumer { personItemList ->
29 | getView()?.showLoading(false, pageNumber == 1)
30 | personItemList?.let {
31 | getView()?.showPersons(mapper.mapToUiModelList(it))
32 | }
33 | },
34 | throwableConsumer = Consumer { throwable ->
35 | getView()?.showErrorMessage(throwable.message)
36 | },
37 | useCase = model.getPersons(pageNumber)
38 | )
39 |
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/personlist/di/PersonListComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.personlist.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.PresentationScope
4 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProviderModule
5 | import com.ahmedadel.robustandroid.feature.person.di.PersonComponent
6 | import com.ahmedadel.robustandroid.presentation.mvp.personlist.PersonListContract
7 | import com.ahmedadel.robustandroid.presentation.mvp.personlist.PersonListPresenter
8 | import dagger.Component
9 |
10 | /**
11 | * Created at Tito on 3/17/19
12 | */
13 |
14 | @PresentationScope
15 | @Component(
16 | modules = [
17 | PersonListModule::class,
18 | BaseSchedulerProviderModule::class
19 | ],
20 | dependencies = [
21 | PersonComponent::class
22 | ]
23 | )
24 | interface PersonListComponent {
25 |
26 | fun personListPresenter(): PersonListPresenter
27 |
28 | fun personListModel(): PersonListContract.Model
29 |
30 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/personlist/di/PersonListComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.personlist.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.feature.person.di.PersonComponent
5 | import com.ahmedadel.robustandroid.feature.person.di.PersonComponentWrapper
6 |
7 | /**
8 | * Created at Tito on 3/17/19
9 | */
10 |
11 | open class PersonListComponentWrapper {
12 |
13 | private lateinit var component: PersonListComponent
14 |
15 | private fun initializeComponent(personComponent: PersonComponent) {
16 | component = DaggerPersonListComponent.builder()
17 | .personComponent(personComponent)
18 | .build()
19 | }
20 |
21 | companion object {
22 |
23 | private var wrapper: PersonListComponentWrapper? = null
24 |
25 | @Synchronized
26 | private fun getInstance(application: Application): PersonListComponentWrapper {
27 | if (wrapper == null) {
28 | if (wrapper == null) {
29 | wrapper = PersonListComponentWrapper()
30 | wrapper!!.initializeComponent(PersonComponentWrapper.getComponent(application))
31 | }
32 | }
33 | return wrapper!!
34 | }
35 |
36 | fun getComponent(application: Application) = getInstance(application).component
37 |
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/personlist/di/PersonListModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.personlist.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.PresentationScope
4 | import com.ahmedadel.robustandroid.feature.person.usecase.PersonUseCase
5 | import com.ahmedadel.robustandroid.presentation.mvp.personlist.PersonListContract
6 | import com.ahmedadel.robustandroid.presentation.mvp.personlist.PersonListModel
7 | import com.ahmedadel.robustandroid.presentation.mvp.personlist.mapper.PersonMapper
8 | import dagger.Module
9 | import dagger.Provides
10 |
11 | /**
12 | * Created at Tito on 3/17/19
13 | */
14 |
15 | @Module
16 | class PersonListModule {
17 |
18 | @Provides
19 | @PresentationScope
20 | fun providesPersonMapper() = PersonMapper()
21 |
22 | @Provides
23 | @PresentationScope
24 | fun providesPersonListModel(personUseCase: PersonUseCase): PersonListContract.Model = PersonListModel(personUseCase)
25 |
26 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/personlist/mapper/PersonMapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.personlist.mapper
2 |
3 | import com.ahmedadel.robustandroid.feature.person.entity.PersonEntity
4 | import com.ahmedadel.robustandroid.models.mappers.MapFromEntityToUi
5 | import com.ahmedadel.robustandroid.presentation.mvp.personlist.uimodel.PersonUiModel
6 |
7 | /**
8 | * Created at Tito on 3/16/19
9 | */
10 |
11 | class PersonMapper : MapFromEntityToUi {
12 |
13 | override fun mapToUiModel(model: PersonEntity): PersonUiModel {
14 | with(model) {
15 | return PersonUiModel(
16 | id = id,
17 | popularity = popularity,
18 | name = name,
19 | profilePath = profilePath,
20 | isAdult = isAdult
21 | )
22 | }
23 | }
24 |
25 | override fun mapToUiModelList(model: List): List {
26 | return model.map {
27 | with(it) {
28 | PersonUiModel(
29 | id = id,
30 | popularity = popularity,
31 | name = name,
32 | profilePath = profilePath,
33 | isAdult = isAdult
34 | )
35 | }
36 | }
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/personlist/uimodel/PersonUiModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.personlist.uimodel
2 |
3 | import com.ahmedadel.robustandroid.models.UiModel
4 |
5 | /**
6 | * Created at Tito on 3/17/19
7 | */
8 |
9 | data class PersonUiModel(
10 |
11 | val id: Int = 0,
12 |
13 | val popularity: Double = 0.0,
14 |
15 | val name: String? = null,
16 |
17 | val profilePath: String? = null,
18 |
19 | val isAdult: Boolean = false
20 |
21 | ) : UiModel
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/tvlist/TVListContract.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.tvlist
2 |
3 | import com.ahmedadel.robustandroid.feature.tv.entity.TVEntity
4 | import com.ahmedadel.robustandroid.presentation.mvp.BaseView
5 | import com.ahmedadel.robustandroid.presentation.mvp.tvlist.uimodel.TVUiModel
6 | import io.reactivex.Flowable
7 |
8 | /**
9 | * Created at Tito on 3/17/19
10 | */
11 |
12 | interface TVListContract {
13 |
14 | interface View : BaseView {
15 |
16 | fun showTVs(tvs: List)
17 |
18 | }
19 |
20 | interface Presenter {
21 |
22 | fun callTVs(pageNumber: Int)
23 |
24 | }
25 |
26 | interface Model {
27 |
28 | fun getTVs(pageNumber: Int): Flowable?>
29 |
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/tvlist/TVListModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.tvlist
2 |
3 | import com.ahmedadel.robustandroid.feature.tv.usecase.TVUseCase
4 | import javax.inject.Inject
5 |
6 | /**
7 | * Created at Tito on 3/17/19
8 | */
9 |
10 | class TVListModel
11 | @Inject
12 | constructor(private val tvUseCase: TVUseCase) : TVListContract.Model {
13 |
14 | override fun getTVs(pageNumber: Int) = tvUseCase.getTVs(pageNumber)
15 |
16 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/tvlist/TVListPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.tvlist
2 |
3 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProvider
4 | import com.ahmedadel.robustandroid.presentation.mvp.BasePresenter
5 | import com.ahmedadel.robustandroid.presentation.mvp.tvlist.mapper.TVMapper
6 | import io.reactivex.functions.Consumer
7 | import javax.inject.Inject
8 |
9 | /**
10 | * Created at Tito on 3/17/19
11 | */
12 |
13 | class TVListPresenter
14 | @Inject
15 | constructor(
16 | baseSchedulerProvider: BaseSchedulerProvider,
17 | private val model: TVListContract.Model,
18 | private val mapper: TVMapper
19 | ) : BasePresenter(baseSchedulerProvider),
20 | TVListContract.Presenter {
21 |
22 | override fun callTVs(pageNumber: Int) {
23 |
24 | execute(
25 | loadingConsumer = Consumer {
26 | getView()?.showLoading(true, pageNumber == 1)
27 | },
28 | successConsumer = Consumer { tvItemList ->
29 | getView()?.showLoading(false, pageNumber == 1)
30 | tvItemList?.let {
31 | getView()?.showTVs(mapper.mapToUiModelList(it))
32 | }
33 | },
34 | throwableConsumer = Consumer { throwable ->
35 | getView()?.showErrorMessage(throwable.message)
36 | },
37 | useCase = model.getTVs(pageNumber)
38 | )
39 |
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/tvlist/di/TVListComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.tvlist.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.PresentationScope
4 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProviderModule
5 | import com.ahmedadel.robustandroid.feature.tv.di.TVComponent
6 | import com.ahmedadel.robustandroid.presentation.mvp.tvlist.TVListContract
7 | import com.ahmedadel.robustandroid.presentation.mvp.tvlist.TVListPresenter
8 | import dagger.Component
9 |
10 | /**
11 | * Created at Tito on 3/17/19
12 | */
13 |
14 | @PresentationScope
15 | @Component(
16 | modules = [
17 | TVListModule::class,
18 | BaseSchedulerProviderModule::class
19 | ],
20 | dependencies = [
21 | TVComponent::class
22 | ]
23 | )
24 | interface TVListComponent {
25 |
26 | fun tvListPresenter(): TVListPresenter
27 |
28 | fun personListModel(): TVListContract.Model
29 |
30 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/tvlist/di/TVListComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.tvlist.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.feature.tv.di.TVComponent
5 | import com.ahmedadel.robustandroid.feature.tv.di.TVComponentWrapper
6 |
7 | /**
8 | * Created at Tito on 3/17/19
9 | */
10 |
11 | open class TVListComponentWrapper {
12 |
13 | private lateinit var component: TVListComponent
14 |
15 | private fun initializeComponent(tvComponent: TVComponent) {
16 | component = DaggerTVListComponent.builder()
17 | .tVComponent(tvComponent)
18 | .build()
19 | }
20 |
21 | companion object {
22 |
23 | private var wrapper: TVListComponentWrapper? = null
24 |
25 | @Synchronized
26 | private fun getInstance(application: Application): TVListComponentWrapper {
27 | if (wrapper == null) {
28 | if (wrapper == null) {
29 | wrapper = TVListComponentWrapper()
30 | wrapper!!.initializeComponent(TVComponentWrapper.getComponent(application))
31 | }
32 | }
33 | return wrapper!!
34 | }
35 |
36 | fun getComponent(application: Application) = getInstance(application).component
37 |
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/tvlist/di/TVListModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.tvlist.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.PresentationScope
4 | import com.ahmedadel.robustandroid.feature.tv.usecase.TVUseCase
5 | import com.ahmedadel.robustandroid.presentation.mvp.tvlist.TVListContract
6 | import com.ahmedadel.robustandroid.presentation.mvp.tvlist.TVListModel
7 | import com.ahmedadel.robustandroid.presentation.mvp.tvlist.mapper.TVMapper
8 | import dagger.Module
9 | import dagger.Provides
10 |
11 | /**
12 | * Created at Tito on 3/17/19
13 | */
14 |
15 | @Module
16 | class TVListModule {
17 |
18 | @Provides
19 | @PresentationScope
20 | fun providesTVMapper() = TVMapper()
21 |
22 | @Provides
23 | @PresentationScope
24 | fun providesTVListModel(tvUseCase: TVUseCase): TVListContract.Model = TVListModel(tvUseCase)
25 |
26 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/tvlist/mapper/TVMapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.tvlist.mapper
2 |
3 | import com.ahmedadel.robustandroid.feature.tv.entity.TVEntity
4 | import com.ahmedadel.robustandroid.models.mappers.MapFromEntityToUi
5 | import com.ahmedadel.robustandroid.presentation.mvp.tvlist.uimodel.TVUiModel
6 |
7 | /**
8 | * Created at Tito on 3/16/19
9 | */
10 |
11 | class TVMapper : MapFromEntityToUi {
12 |
13 | override fun mapToUiModel(model: TVEntity): TVUiModel {
14 | with(model) {
15 | return TVUiModel(
16 | id,
17 | name,
18 | overview,
19 | originalName,
20 | originalLanguage,
21 | posterPath,
22 | voteCount
23 | )
24 | }
25 | }
26 |
27 | override fun mapToUiModelList(model: List): List {
28 | return model.map {
29 | with(it) {
30 | TVUiModel(
31 | id,
32 | name,
33 | overview,
34 | originalName,
35 | originalLanguage,
36 | posterPath,
37 | voteCount
38 | )
39 | }
40 | }
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/presentation/mvp/src/main/java/com/ahmedadel/robustandroid/presentation/mvp/tvlist/uimodel/TVUiModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp.tvlist.uimodel
2 |
3 | import com.ahmedadel.robustandroid.models.UiModel
4 |
5 | /**
6 | * Created at Tito on 3/16/19
7 | */
8 |
9 | data class TVUiModel (
10 |
11 | val id: Int = 0,
12 |
13 | val name: String? = null,
14 |
15 | val overview: String? = null,
16 |
17 | val originalName: String? = null,
18 |
19 | val originalLanguage: String? = null,
20 |
21 | val posterPath: String? = null,
22 |
23 | val voteCount: Int = 0
24 |
25 | ) : UiModel
--------------------------------------------------------------------------------
/presentation/mvp/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | mvp
3 |
4 |
--------------------------------------------------------------------------------
/presentation/mvp/src/test/java/com/ahmedadel/robustandroid/presentation/mvp/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvp;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/presentation/mvvm/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/presentation/mvvm/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.*
2 |
3 | apply {
4 | plugin("com.android.library")
5 | plugin("kotlin-android-extensions")
6 | plugin("kotlin-android")
7 | plugin("kotlin-kapt")
8 | }
9 |
10 | android {
11 |
12 | compileSdkVersion Versions.compileSdkVersion
13 |
14 | defaultConfig {
15 |
16 | minSdkVersion Versions.minSdkVersion
17 | targetSdkVersion Versions.targetSdkVersion
18 |
19 | versionCode Versions.versionCode
20 | versionName Versions.versionName
21 |
22 | }
23 |
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 |
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 |
37 | api project(':feature:movie')
38 | api project(':feature:person')
39 | api project(':feature:tv')
40 |
41 | implementation Dependencies.AppCompact
42 |
43 | implementation Dependencies.AndroidArchComponent
44 |
45 | implementation Dependencies.KotlinStdLib
46 |
47 | implementation Dependencies.RxJava
48 |
49 | implementation Dependencies.Dagger
50 | kapt Dependencies.DaggerKapt
51 |
52 | implementation Dependencies.Testing
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/presentation/mvvm/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
22 |
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/java/com/ahmedadel/robustandroid/presentation/mvvm/BaseViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProvider
5 | import io.reactivex.Flowable
6 | import io.reactivex.Scheduler
7 | import io.reactivex.disposables.CompositeDisposable
8 | import io.reactivex.disposables.Disposable
9 | import io.reactivex.functions.Consumer
10 | import org.reactivestreams.Subscription
11 |
12 | /**
13 | * Created at Tito on 3/16/19
14 | *
15 | * A base view model class that deals with RX threading, dispose them when the view model is cleared and also gives
16 | * the ability to start more than observable in the same time by adding them in the disposables object.
17 | */
18 |
19 | open class BaseViewModel
20 | constructor(
21 | baseSchedulerProvider: BaseSchedulerProvider
22 | ) : ViewModel() {
23 |
24 | private val subscribeOn: Scheduler = baseSchedulerProvider.io()
25 | private val observeOn: Scheduler = baseSchedulerProvider.ui()
26 | private val disposables: CompositeDisposable = CompositeDisposable()
27 |
28 | protected fun execute(
29 | loadingConsumer: Consumer,
30 | successConsumer: Consumer,
31 | throwableConsumer: Consumer,
32 | useCase: Flowable
33 | ) {
34 | val observable = useCase
35 | .doOnSubscribe(loadingConsumer)
36 | .subscribeOn(subscribeOn)
37 | .observeOn(observeOn)
38 | addDisposable(observable.subscribe(successConsumer, throwableConsumer))
39 | }
40 |
41 | private fun dispose() {
42 | if (!disposables.isDisposed) {
43 | disposables.dispose()
44 | }
45 | }
46 |
47 | private fun addDisposable(disposable: Disposable) {
48 | disposables.add(disposable)
49 | }
50 |
51 | override fun onCleared() {
52 | super.onCleared()
53 | dispose()
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/java/com/ahmedadel/robustandroid/presentation/mvvm/ViewState.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm
2 |
3 | /**
4 | * Created at ahmedadel on 2/25/19
5 | *
6 | *
7 | * A generic class that contains data and status about loading this data.
8 | */
9 |
10 | class ViewState private constructor(
11 | val status: Status,
12 | val data: T?,
13 | val message: String?
14 | ) {
15 |
16 | enum class Status {
17 | SUCCESS, ERROR, LOADING
18 | }
19 |
20 | companion object {
21 |
22 | fun success(data: T): ViewState {
23 | return ViewState(
24 | Status.SUCCESS,
25 | data,
26 | null
27 | )
28 | }
29 |
30 | fun error(msg: String?): ViewState {
31 | return ViewState(
32 | Status.ERROR,
33 | null,
34 | msg
35 | )
36 | }
37 |
38 | fun loading(): ViewState {
39 | return ViewState(
40 | Status.LOADING,
41 | null,
42 | null
43 | )
44 | }
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/java/com/ahmedadel/robustandroid/presentation/mvvm/home/di/HomeMappersModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm.home.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.PresentationScope
4 | import com.ahmedadel.robustandroid.presentation.mvvm.home.mapper.MovieMapper
5 | import com.ahmedadel.robustandroid.presentation.mvvm.home.mapper.PersonMapper
6 | import com.ahmedadel.robustandroid.presentation.mvvm.home.mapper.TVMapper
7 | import dagger.Module
8 | import dagger.Provides
9 |
10 | /**
11 | * Created at Tito on 3/16/19
12 | */
13 |
14 | @Module
15 | class HomeMappersModule {
16 |
17 | @Provides
18 | @PresentationScope
19 | fun providesMovieMapper() = MovieMapper()
20 |
21 | @Provides
22 | @PresentationScope
23 | fun providesPersonMapper() = PersonMapper()
24 |
25 | @Provides
26 | @PresentationScope
27 | fun providesTVMapper() = TVMapper()
28 |
29 | }
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/java/com/ahmedadel/robustandroid/presentation/mvvm/home/di/HomeUseCasesComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm.home.di
2 |
3 | import com.ahmedadel.robustandroid.core.di.FeatureScope
4 | import com.ahmedadel.robustandroid.datalayer.datasource.both.di.DataSourceComponent
5 | import com.ahmedadel.robustandroid.feature.movie.di.MovieModule
6 | import com.ahmedadel.robustandroid.feature.movie.usecase.MovieUseCase
7 | import com.ahmedadel.robustandroid.feature.person.di.PersonModule
8 | import com.ahmedadel.robustandroid.feature.person.usecase.PersonUseCase
9 | import com.ahmedadel.robustandroid.feature.tv.di.TVModule
10 | import com.ahmedadel.robustandroid.feature.tv.usecase.TVUseCase
11 | import dagger.Component
12 |
13 | /**
14 | * Created at Tito on 3/16/19
15 | */
16 |
17 | @FeatureScope
18 | @Component(
19 | modules = [
20 | MovieModule::class,
21 | PersonModule::class,
22 | TVModule::class
23 | ],
24 | dependencies = [
25 | DataSourceComponent::class
26 | ]
27 | )
28 | interface HomeUseCasesComponent {
29 |
30 | fun movieUseCase(): MovieUseCase
31 |
32 | fun personUseCase(): PersonUseCase
33 |
34 | fun tvUseCase(): TVUseCase
35 |
36 | }
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/java/com/ahmedadel/robustandroid/presentation/mvvm/home/di/HomeViewModelComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm.home.di
2 |
3 | import androidx.lifecycle.ViewModelProvider
4 | import com.ahmedadel.robustandroid.core.di.PresentationScope
5 | import com.ahmedadel.robustandroid.core.di.scheduler.BaseSchedulerProviderModule
6 | import com.ahmedadel.robustandroid.core.di.ViewModelFactoryModule
7 | import com.ahmedadel.robustandroid.presentation.mvvm.home.HomeViewModel
8 | import dagger.Component
9 |
10 | /**
11 | * Created at Tito on 3/16/19
12 | */
13 |
14 | @PresentationScope
15 | @Component(
16 | modules = [
17 | ViewModelFactoryModule::class,
18 | HomeMappersModule::class,
19 | HomeViewModelModule::class,
20 | BaseSchedulerProviderModule::class
21 | ],
22 | dependencies = [
23 | HomeUseCasesComponent::class
24 | ]
25 | )
26 | interface HomeViewModelComponent {
27 |
28 | fun homeViewModel(): HomeViewModel
29 |
30 | fun viewModelFactory(): ViewModelProvider.Factory
31 |
32 | }
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/java/com/ahmedadel/robustandroid/presentation/mvvm/home/di/HomeViewModelModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm.home.di
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.ahmedadel.robustandroid.core.di.PresentationScope
5 | import com.ahmedadel.robustandroid.core.di.ViewModelKey
6 | import com.ahmedadel.robustandroid.presentation.mvvm.home.HomeViewModel
7 | import dagger.Binds
8 | import dagger.Module
9 | import dagger.multibindings.IntoMap
10 |
11 | /**
12 | * Created at Tito on 3/16/19
13 | */
14 |
15 | @Module
16 | abstract class HomeViewModelModule {
17 |
18 | @Binds
19 | @IntoMap
20 | @ViewModelKey(HomeViewModel::class)
21 | @PresentationScope
22 | internal abstract fun provideHomeViewModel(homeViewModel: HomeViewModel): ViewModel
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/java/com/ahmedadel/robustandroid/presentation/mvvm/home/mapper/MovieMapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm.home.mapper
2 |
3 | import com.ahmedadel.robustandroid.feature.movie.entity.MovieEntity
4 | import com.ahmedadel.robustandroid.models.mappers.MapFromEntityToUi
5 | import com.ahmedadel.robustandroid.presentation.mvvm.home.uimodel.MovieUiModel
6 |
7 | /**
8 | * Created at Tito on 3/16/19
9 | */
10 |
11 | class MovieMapper : MapFromEntityToUi {
12 |
13 | override fun mapToUiModel(model: MovieEntity): MovieUiModel {
14 | with(model) {
15 | return MovieUiModel(
16 | id = id,
17 | title = title,
18 | overview = overview,
19 | originalLanguage = originalLanguage,
20 | originalTitle = originalTitle,
21 | posterPath = posterPath,
22 | releaseDate = releaseDate,
23 | voteAverage = voteAverage,
24 | isAdult = isAdult
25 | )
26 | }
27 | }
28 |
29 | override fun mapToUiModelList(model: List): List {
30 | return model.map {
31 | with(it) {
32 | MovieUiModel(
33 | id = id,
34 | title = title,
35 | overview = overview,
36 | originalLanguage = originalLanguage,
37 | originalTitle = originalTitle,
38 | posterPath = posterPath,
39 | releaseDate = releaseDate,
40 | voteAverage = voteAverage,
41 | isAdult = isAdult
42 | )
43 | }
44 | }
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/java/com/ahmedadel/robustandroid/presentation/mvvm/home/mapper/PersonMapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm.home.mapper
2 |
3 | import com.ahmedadel.robustandroid.feature.person.entity.PersonEntity
4 | import com.ahmedadel.robustandroid.models.mappers.MapFromEntityToUi
5 | import com.ahmedadel.robustandroid.presentation.mvvm.home.uimodel.PersonUiModel
6 |
7 | /**
8 | * Created at Tito on 3/16/19
9 | */
10 |
11 | class PersonMapper : MapFromEntityToUi {
12 |
13 | override fun mapToUiModel(model: PersonEntity): PersonUiModel {
14 | with(model) {
15 | return PersonUiModel(
16 | id = id,
17 | popularity = popularity,
18 | name = name,
19 | profilePath = profilePath,
20 | isAdult = isAdult
21 | )
22 | }
23 | }
24 |
25 | override fun mapToUiModelList(model: List): List {
26 | return model.map {
27 | with(it) {
28 | PersonUiModel(
29 | id = id,
30 | popularity = popularity,
31 | name = name,
32 | profilePath = profilePath,
33 | isAdult = isAdult
34 | )
35 | }
36 | }
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/java/com/ahmedadel/robustandroid/presentation/mvvm/home/mapper/TVMapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm.home.mapper
2 |
3 | import com.ahmedadel.robustandroid.feature.tv.entity.TVEntity
4 | import com.ahmedadel.robustandroid.models.mappers.MapFromEntityToUi
5 | import com.ahmedadel.robustandroid.presentation.mvvm.home.uimodel.TVUiModel
6 |
7 | /**
8 | * Created at Tito on 3/16/19
9 | */
10 |
11 | class TVMapper : MapFromEntityToUi {
12 |
13 | override fun mapToUiModel(model: TVEntity): TVUiModel {
14 | with(model) {
15 | return TVUiModel(
16 | id,
17 | name,
18 | overview,
19 | originalName,
20 | originalLanguage,
21 | posterPath,
22 | voteCount
23 | )
24 | }
25 | }
26 |
27 | override fun mapToUiModelList(model: List): List {
28 | return model.map {
29 | with(it) {
30 | TVUiModel(
31 | id,
32 | name,
33 | overview,
34 | originalName,
35 | originalLanguage,
36 | posterPath,
37 | voteCount
38 | )
39 | }
40 | }
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/java/com/ahmedadel/robustandroid/presentation/mvvm/home/uimodel/MovieUiModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm.home.uimodel
2 |
3 | import com.ahmedadel.robustandroid.models.UiModel
4 |
5 | /**
6 | * Created at Tito on 3/16/19
7 | */
8 |
9 | data class MovieUiModel(
10 |
11 | val id: Int = 0,
12 |
13 | val title: String? = null,
14 |
15 | val overview: String? = null,
16 |
17 | val originalLanguage: String? = null,
18 |
19 | val originalTitle: String? = null,
20 |
21 | val posterPath: String? = null,
22 |
23 | val releaseDate: String? = null,
24 |
25 | val voteAverage: Double = 0.0,
26 |
27 | val isAdult: Boolean = false
28 |
29 | ) : UiModel
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/java/com/ahmedadel/robustandroid/presentation/mvvm/home/uimodel/PersonUiModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm.home.uimodel
2 |
3 | import com.ahmedadel.robustandroid.models.UiModel
4 |
5 | /**
6 | * Created at Tito on 3/16/19
7 | */
8 |
9 | data class PersonUiModel(
10 |
11 | val id: Int = 0,
12 |
13 | val popularity: Double = 0.0,
14 |
15 | val name: String? = null,
16 |
17 | val profilePath: String? = null,
18 |
19 | val isAdult: Boolean = false
20 |
21 | ) : UiModel
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/java/com/ahmedadel/robustandroid/presentation/mvvm/home/uimodel/TVUiModel.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm.home.uimodel
2 |
3 | import com.ahmedadel.robustandroid.models.UiModel
4 |
5 | /**
6 | * Created at Tito on 3/16/19
7 | */
8 |
9 | data class TVUiModel (
10 |
11 | val id: Int = 0,
12 |
13 | val name: String? = null,
14 |
15 | val overview: String? = null,
16 |
17 | val originalName: String? = null,
18 |
19 | val originalLanguage: String? = null,
20 |
21 | val posterPath: String? = null,
22 |
23 | val voteCount: Int = 0
24 |
25 | ) : UiModel
26 |
--------------------------------------------------------------------------------
/presentation/mvvm/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | mvvm
3 |
4 |
--------------------------------------------------------------------------------
/presentation/mvvm/src/test/java/com/ahmedadel/robustandroid/presentation/mvvm/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.presentation.mvvm;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':ui', ':core', ':models',
2 | ':datalayer:remote', ':datalayer:local', ':datalayer:datasource',
3 | ':feature:movie', ':feature:person', ':feature:tv',
4 | ':presentation:mvvm', ':presentation:mvp', ':presentation:mvi'
5 |
--------------------------------------------------------------------------------
/ui/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/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
22 |
--------------------------------------------------------------------------------
/ui/src/androidTest/java/com/ahmedadel/robustandroid/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid
2 |
3 | import androidx.test.InstrumentationRegistry
4 | import androidx.test.runner.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.getTargetContext()
22 | assertEquals("com.ahmedadel.robustandroid", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ui/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 |
6 | /**
7 | * Created at Tito on 3/16/19
8 | *
9 | * Base Activity that contains some shared methods allover the activities of the app.
10 | */
11 |
12 | abstract class BaseActivity : AppCompatActivity() {
13 |
14 | abstract val screenName: String
15 |
16 | override fun onCreate(savedInstanceState: Bundle?) {
17 | super.onCreate(savedInstanceState)
18 | setupActivityComponent()
19 | }
20 |
21 | fun buildNavigateUpArrow() {
22 | if (supportActionBar != null) {
23 | supportActionBar!!.setDisplayHomeAsUpEnabled(true)
24 | supportActionBar!!.setDisplayShowHomeEnabled(true)
25 | }
26 | }
27 |
28 | protected abstract fun setupActivityComponent()
29 |
30 | override fun onSupportNavigateUp(): Boolean {
31 | onBackPressed()
32 | return true
33 | }
34 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid
2 |
3 | import android.animation.Animator
4 | import android.content.Intent
5 | import androidx.appcompat.app.AppCompatActivity
6 | import android.os.Bundle
7 | import com.ahmedadel.robustandroid.home.HomeActivity
8 | import kotlinx.android.synthetic.main.activity_main.*
9 |
10 | class MainActivity : AppCompatActivity() {
11 |
12 | override fun onCreate(savedInstanceState: Bundle?) {
13 | super.onCreate(savedInstanceState)
14 | setContentView(R.layout.activity_main)
15 |
16 | tmdb_icon_iv.animate()
17 | .setStartDelay(500)
18 | .setDuration(1500)
19 | .scaleX(20F)
20 | .scaleY(20F)
21 | .alpha(0F)
22 | .setListener(object : Animator.AnimatorListener {
23 |
24 | override fun onAnimationRepeat(animation: Animator?) {
25 |
26 | }
27 |
28 | override fun onAnimationEnd(animation: Animator?) {
29 | startActivity(Intent(this@MainActivity, HomeActivity::class.java))
30 | finish()
31 | }
32 |
33 | override fun onAnimationCancel(animation: Animator?) {
34 |
35 | }
36 |
37 | override fun onAnimationStart(animation: Animator?) {
38 |
39 | }
40 | })
41 |
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/di/ActivityScope.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.di
2 |
3 | import javax.inject.Scope
4 |
5 | /**
6 | * Created at Tito on 3/16/19
7 | *
8 | * Custom scope for each activity.
9 | */
10 |
11 | @Scope
12 | annotation class ActivityScope
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/home/adapter/HomePersonAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.home.adapter
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.DiffUtil
7 | import androidx.recyclerview.widget.RecyclerView
8 | import com.ahmedadel.robustandroid.BuildConfig
9 | import com.ahmedadel.robustandroid.R
10 | import com.ahmedadel.robustandroid.presentation.mvvm.home.uimodel.PersonUiModel
11 | import com.bumptech.glide.Glide
12 | import kotlinx.android.synthetic.main.person_list_item.view.*
13 |
14 | class HomePersonAdapter : RecyclerView.Adapter() {
15 |
16 | private val persons: MutableList = ArrayList()
17 |
18 | override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
19 | return ViewHolder(
20 | LayoutInflater.from(viewGroup.context)
21 | .inflate(R.layout.person_list_item, viewGroup, false)
22 | )
23 | }
24 |
25 | override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
26 | viewHolder.bind(persons[position])
27 | }
28 |
29 | override fun getItemCount() = persons.size
30 |
31 | fun submitList(newPersons: List?) {
32 | newPersons?.let {
33 | persons.addAll(it)
34 | val uniquePersonList = persons.distinctBy { person ->
35 | person.id
36 | }
37 | persons.clear()
38 | persons.addAll(uniquePersonList)
39 | notifyDataSetChanged()
40 | }
41 | }
42 |
43 | @Suppress("unused")
44 | fun clear() {
45 | persons.clear()
46 | notifyDataSetChanged()
47 | }
48 |
49 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
50 |
51 | fun bind(person: PersonUiModel) {
52 | itemView.person_title.text = person.name
53 | Glide.with(itemView.context)
54 | .load(BuildConfig.IMAGE_URL + person.profilePath)
55 | .into(itemView.person_image)
56 | }
57 |
58 | }
59 |
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/home/adapter/HomeTVAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.home.adapter
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.ahmedadel.robustandroid.BuildConfig
8 | import com.ahmedadel.robustandroid.R
9 | import com.ahmedadel.robustandroid.presentation.mvvm.home.uimodel.TVUiModel
10 | import com.bumptech.glide.Glide
11 | import kotlinx.android.synthetic.main.tv_list_item.view.*
12 |
13 | class HomeTVAdapter : RecyclerView.Adapter() {
14 |
15 | private val tvs: MutableList = ArrayList()
16 |
17 | override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
18 | return ViewHolder(
19 | LayoutInflater.from(viewGroup.context)
20 | .inflate(R.layout.tv_list_item, viewGroup, false)
21 | )
22 | }
23 |
24 | override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
25 | viewHolder.bind(tvs[position])
26 | }
27 |
28 | override fun getItemCount() = tvs.size
29 |
30 | fun submitList(newTVs: List?) {
31 | newTVs?.let {
32 | tvs.addAll(it)
33 | val uniqueTVList = tvs.distinctBy { tv ->
34 | tv.id
35 | }
36 | tvs.clear()
37 | tvs.addAll(uniqueTVList)
38 | notifyDataSetChanged()
39 | }
40 | }
41 |
42 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
43 |
44 | fun bind(tv: TVUiModel) {
45 | itemView.tv_title.text = tv.name
46 | Glide.with(itemView.context)
47 | .load(BuildConfig.IMAGE_URL + tv.posterPath)
48 | .into(itemView.tv_image)
49 | }
50 |
51 | }
52 |
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/home/di/HomeActivityComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.home.di
2 |
3 | import com.ahmedadel.robustandroid.di.ActivityScope
4 | import com.ahmedadel.robustandroid.home.HomeActivity
5 | import com.ahmedadel.robustandroid.presentation.mvvm.home.di.HomeViewModelComponent
6 | import com.ahmedadel.robustandroid.presentation.mvvm.home.di.HomeViewModelModule
7 | import dagger.Component
8 |
9 | /**
10 | * Created at Tito on 3/16/19
11 | */
12 |
13 | @ActivityScope
14 | @Component(
15 | modules = [
16 | HomeActivityModule::class
17 | ],
18 | dependencies = [
19 | HomeViewModelComponent::class
20 | ]
21 | )
22 | interface HomeActivityComponent {
23 |
24 | fun inject(homeActivity: HomeActivity)
25 |
26 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/home/di/HomeActivityComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.home.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.home.HomeActivity
5 | import com.ahmedadel.robustandroid.presentation.mvvm.home.di.HomeViewModelComponent
6 | import com.ahmedadel.robustandroid.presentation.mvvm.home.di.HomeViewModelComponentWrapper
7 |
8 | /**
9 | * Created at Tito on 3/16/19
10 | */
11 |
12 | class HomeActivityComponentWrapper {
13 |
14 | private lateinit var component: HomeActivityComponent
15 |
16 | private fun initializeComponent(homeViewModelComponent: HomeViewModelComponent) {
17 | component = DaggerHomeActivityComponent.builder()
18 | .homeViewModelComponent(homeViewModelComponent)
19 | .build()
20 | }
21 |
22 | companion object {
23 |
24 | private var wrapper: HomeActivityComponentWrapper? = null
25 |
26 | @Synchronized
27 | private fun getInstance(application: Application): HomeActivityComponentWrapper {
28 | if (wrapper == null) {
29 | if (wrapper == null) {
30 | wrapper = HomeActivityComponentWrapper()
31 | wrapper!!.initializeComponent(HomeViewModelComponentWrapper.getComponent(application))
32 | }
33 | }
34 | return wrapper!!
35 | }
36 |
37 | fun buildComponent(homeActivity: HomeActivity) {
38 | getInstance(homeActivity.application).component.inject(homeActivity)
39 | }
40 |
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/home/di/HomeActivityModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.home.di
2 |
3 | import com.ahmedadel.robustandroid.di.ActivityScope
4 | import com.ahmedadel.robustandroid.home.adapter.HomeMovieAdapter
5 | import com.ahmedadel.robustandroid.home.adapter.HomePersonAdapter
6 | import com.ahmedadel.robustandroid.home.adapter.HomeTVAdapter
7 | import dagger.Module
8 | import dagger.Provides
9 |
10 | /**
11 | * Created at Tito on 3/16/19
12 | */
13 |
14 | @Module
15 | class HomeActivityModule {
16 |
17 | @Provides
18 | @ActivityScope
19 | fun provideMovieAdapter(): HomeMovieAdapter = HomeMovieAdapter()
20 |
21 | @Provides
22 | @ActivityScope
23 | fun providePersonAdapter(): HomePersonAdapter = HomePersonAdapter()
24 |
25 | @Provides
26 | @ActivityScope
27 | fun provideTVAdapter(): HomeTVAdapter = HomeTVAdapter()
28 |
29 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/moviedetails/di/MovieDetailsActivityComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.moviedetails.di
2 |
3 | import com.ahmedadel.robustandroid.di.ActivityScope
4 | import com.ahmedadel.robustandroid.moviedetails.MovieDetailsActivity
5 | import com.ahmedadel.robustandroid.presentation.mvi.movie.di.MovieDetailsComponent
6 | import dagger.Component
7 |
8 | /**
9 | * Created at Tito on 3/20/19
10 | */
11 |
12 | @ActivityScope
13 | @Component(
14 | modules = [
15 | MovieDetailsActivityModule::class
16 | ],
17 | dependencies = [
18 | MovieDetailsComponent::class
19 | ]
20 | )
21 | interface MovieDetailsActivityComponent {
22 |
23 | fun inject(movieDetailsActivity: MovieDetailsActivity)
24 |
25 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/moviedetails/di/MovieDetailsActivityComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.moviedetails.di
2 |
3 | import com.ahmedadel.robustandroid.feature.movie.di.MovieComponentWrapper
4 | import com.ahmedadel.robustandroid.moviedetails.MovieDetailsActivity
5 | import com.ahmedadel.robustandroid.presentation.mvi.movie.di.DaggerMovieDetailsComponent
6 |
7 | /**
8 | * Created at Tito on 3/20/19
9 | */
10 |
11 | object MovieDetailsActivityComponentWrapper {
12 |
13 | fun buildComponent(movieDetailsActivity: MovieDetailsActivity) {
14 | DaggerMovieDetailsActivityComponent
15 | .builder()
16 | .movieDetailsComponent(
17 | DaggerMovieDetailsComponent
18 | .builder()
19 | .movieComponent(MovieComponentWrapper.getComponent(movieDetailsActivity.application))
20 | .build()
21 | )
22 | .build()
23 | .inject(movieDetailsActivity)
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/moviedetails/di/MovieDetailsActivityModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.moviedetails.di
2 |
3 | import com.ahmedadel.robustandroid.di.ActivityScope
4 | import com.ahmedadel.robustandroid.moviedetails.adapter.RecommendationMoviesAdapter
5 | import com.ahmedadel.robustandroid.moviedetails.adapter.SimilarMoviesAdapter
6 | import dagger.Module
7 | import dagger.Provides
8 |
9 | /**
10 | * Created at Tito on 3/21/19
11 | */
12 |
13 | @Module
14 | class MovieDetailsActivityModule {
15 |
16 | @Provides
17 | @ActivityScope
18 | fun provideSimilarMoviesAdapter(): SimilarMoviesAdapter = SimilarMoviesAdapter()
19 |
20 | @Provides
21 | @ActivityScope
22 | fun provideRecommendationMoviesAdapter(): RecommendationMoviesAdapter = RecommendationMoviesAdapter()
23 |
24 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/movielist/di/MovieListActivityComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.movielist.di
2 |
3 | import com.ahmedadel.robustandroid.di.ActivityScope
4 | import com.ahmedadel.robustandroid.movielist.MovieListActivity
5 | import com.ahmedadel.robustandroid.presentation.mvp.movielist.di.MovieListComponent
6 | import dagger.Component
7 |
8 | /**
9 | * Created at Tito on 3/17/19
10 | */
11 |
12 | @ActivityScope
13 | @Component(
14 | modules = [
15 | MovieListActivityModule::class
16 | ],
17 | dependencies = [
18 | MovieListComponent::class
19 | ]
20 | )
21 | interface MovieListActivityComponent {
22 |
23 | fun inject(movieListActivity: MovieListActivity)
24 |
25 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/movielist/di/MovieListActivityComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.movielist.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.movielist.MovieListActivity
5 | import com.ahmedadel.robustandroid.presentation.mvp.movielist.di.MovieListComponent
6 | import com.ahmedadel.robustandroid.presentation.mvp.movielist.di.MovieListComponentWrapper
7 |
8 | /**
9 | * Created at Tito on 3/17/19
10 | */
11 |
12 | class MovieListActivityComponentWrapper {
13 |
14 | private lateinit var component: MovieListActivityComponent
15 |
16 | private fun initializeComponent(movieListComponent: MovieListComponent) {
17 | component = DaggerMovieListActivityComponent.builder()
18 | .movieListComponent(movieListComponent)
19 | .build()
20 | }
21 |
22 | companion object {
23 |
24 | private var wrapper: MovieListActivityComponentWrapper? = null
25 |
26 | @Synchronized
27 | private fun getInstance(application: Application): MovieListActivityComponentWrapper {
28 | if (wrapper == null) {
29 | if (wrapper == null) {
30 | wrapper = MovieListActivityComponentWrapper()
31 | wrapper!!.initializeComponent(MovieListComponentWrapper.getComponent(application))
32 | }
33 | }
34 | return wrapper!!
35 | }
36 |
37 | fun buildComponent(movieListActivity: MovieListActivity) {
38 | getInstance(movieListActivity.application).component.inject(movieListActivity)
39 | }
40 |
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/movielist/di/MovieListActivityModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.movielist.di
2 |
3 | import com.ahmedadel.robustandroid.di.ActivityScope
4 | import com.ahmedadel.robustandroid.movielist.adapter.MovieListAdapter
5 | import dagger.Module
6 | import dagger.Provides
7 |
8 | /**
9 | * Created at Tito on 3/17/19
10 | */
11 |
12 | @Module
13 | class MovieListActivityModule {
14 |
15 | @Provides
16 | @ActivityScope
17 | fun provideMovieListAdapter(): MovieListAdapter = MovieListAdapter()
18 |
19 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/personlist/adapter/PersonListAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.personlist.adapter
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.ahmedadel.robustandroid.BuildConfig
8 | import com.ahmedadel.robustandroid.R
9 | import com.ahmedadel.robustandroid.presentation.mvp.personlist.uimodel.PersonUiModel
10 | import com.bumptech.glide.Glide
11 | import kotlinx.android.synthetic.main.activity_person_list_item.view.*
12 |
13 | /**
14 | * Created at Tito on 3/18/19
15 | */
16 |
17 | class PersonListAdapter : RecyclerView.Adapter() {
18 |
19 | private val persons: MutableList = ArrayList()
20 |
21 | override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
22 | return ViewHolder(
23 | LayoutInflater.from(viewGroup.context)
24 | .inflate(R.layout.activity_person_list_item, viewGroup, false)
25 | )
26 | }
27 |
28 | override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
29 | viewHolder.bind(persons[position])
30 | }
31 |
32 | override fun getItemCount() = persons.size
33 |
34 | fun submitList(newPersons: List) {
35 | persons.addAll(newPersons)
36 | val uniquePersonList = persons.distinctBy { movie ->
37 | movie.id
38 | }
39 | persons.clear()
40 | persons.addAll(uniquePersonList)
41 | notifyDataSetChanged()
42 | }
43 |
44 | fun clear() {
45 | persons.clear()
46 | notifyDataSetChanged()
47 | }
48 |
49 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
50 |
51 | fun bind(person: PersonUiModel) {
52 | itemView.person_title.text = person.name
53 | Glide.with(itemView.context)
54 | .load(BuildConfig.IMAGE_URL + person.profilePath)
55 | .into(itemView.person_image)
56 | }
57 |
58 | }
59 |
60 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/personlist/di/PersonListActivityComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.personlist.di
2 |
3 | import com.ahmedadel.robustandroid.di.ActivityScope
4 | import com.ahmedadel.robustandroid.personlist.PersonListActivity
5 | import com.ahmedadel.robustandroid.presentation.mvp.personlist.di.PersonListComponent
6 | import dagger.Component
7 |
8 | /**
9 | * Created at Tito on 3/18/19
10 | */
11 |
12 | @ActivityScope
13 | @Component(
14 | modules = [
15 | PersonListActivityModule::class
16 | ],
17 | dependencies = [
18 | PersonListComponent::class
19 | ]
20 | )
21 | interface PersonListActivityComponent {
22 |
23 | fun inject(personListActivity: PersonListActivity)
24 |
25 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/personlist/di/PersonListActivityComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.personlist.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.personlist.PersonListActivity
5 | import com.ahmedadel.robustandroid.presentation.mvp.personlist.di.PersonListComponent
6 | import com.ahmedadel.robustandroid.presentation.mvp.personlist.di.PersonListComponentWrapper
7 |
8 | /**
9 | * Created at Tito on 3/18/19
10 | */
11 |
12 | class PersonListActivityComponentWrapper {
13 |
14 | private lateinit var component: PersonListActivityComponent
15 |
16 | private fun initializeComponent(personListComponent: PersonListComponent) {
17 | component = DaggerPersonListActivityComponent.builder()
18 | .personListComponent(personListComponent)
19 | .build()
20 | }
21 |
22 | companion object {
23 |
24 | private var wrapper: PersonListActivityComponentWrapper? = null
25 |
26 | @Synchronized
27 | private fun getInstance(application: Application): PersonListActivityComponentWrapper {
28 | if (wrapper == null) {
29 | if (wrapper == null) {
30 | wrapper = PersonListActivityComponentWrapper()
31 | wrapper!!.initializeComponent(PersonListComponentWrapper.getComponent(application))
32 | }
33 | }
34 | return wrapper!!
35 | }
36 |
37 | fun buildComponent(personListActivity: PersonListActivity) {
38 | getInstance(personListActivity.application).component.inject(personListActivity)
39 | }
40 |
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/personlist/di/PersonListActivityModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.personlist.di
2 |
3 | import com.ahmedadel.robustandroid.di.ActivityScope
4 | import com.ahmedadel.robustandroid.personlist.adapter.PersonListAdapter
5 | import dagger.Module
6 | import dagger.Provides
7 |
8 | /**
9 | * Created at Tito on 3/18/19
10 | */
11 |
12 | @Module
13 | class PersonListActivityModule {
14 |
15 | @Provides
16 | @ActivityScope
17 | fun providePersonListAdapter(): PersonListAdapter = PersonListAdapter()
18 |
19 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/tvdetails/di/TVDetailsActivityComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.tvdetails.di
2 |
3 | import com.ahmedadel.robustandroid.di.ActivityScope
4 | import com.ahmedadel.robustandroid.presentation.mvi.tv.di.TVDetailsComponent
5 | import com.ahmedadel.robustandroid.tvdetails.TVDetailsActivity
6 | import dagger.Component
7 |
8 | /**
9 | * Created at Tito on 3/20/19
10 | */
11 |
12 | @ActivityScope
13 | @Component(
14 | dependencies = [
15 | TVDetailsComponent::class
16 | ]
17 | )
18 | interface TVDetailsActivityComponent {
19 |
20 | fun inject(tvDetailsActivity: TVDetailsActivity)
21 |
22 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/tvdetails/di/TVDetailsActivityComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.tvdetails.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.presentation.mvi.tv.di.TVDetailsComponent
5 | import com.ahmedadel.robustandroid.presentation.mvi.tv.di.TVDetailsComponentWrapper
6 | import com.ahmedadel.robustandroid.tvdetails.TVDetailsActivity
7 |
8 | /**
9 | * Created at Tito on 3/20/19
10 | */
11 |
12 | class TVDetailsActivityComponentWrapper {
13 |
14 | private lateinit var component: TVDetailsActivityComponent
15 |
16 | private fun initializeComponent(tvDetailsComponent: TVDetailsComponent) {
17 | component = DaggerTVDetailsActivityComponent.builder()
18 | .tVDetailsComponent(tvDetailsComponent)
19 | .build()
20 | }
21 |
22 | companion object {
23 |
24 | private var wrapper: TVDetailsActivityComponentWrapper? = null
25 |
26 | @Synchronized
27 | private fun getInstance(application: Application): TVDetailsActivityComponentWrapper {
28 | if (wrapper == null) {
29 | if (wrapper == null) {
30 | wrapper = TVDetailsActivityComponentWrapper()
31 | wrapper!!.initializeComponent(TVDetailsComponentWrapper.getComponent(application))
32 | }
33 | }
34 | return wrapper!!
35 | }
36 |
37 | fun buildComponent(tvDetailsActivity: TVDetailsActivity) {
38 | getInstance(tvDetailsActivity.application).component.inject(tvDetailsActivity)
39 | }
40 |
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/tvlist/di/TVListActivityComponent.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.tvlist.di
2 |
3 | import com.ahmedadel.robustandroid.di.ActivityScope
4 | import com.ahmedadel.robustandroid.presentation.mvp.tvlist.di.TVListComponent
5 | import com.ahmedadel.robustandroid.tvlist.TVListActivity
6 | import dagger.Component
7 |
8 | /**
9 | * Created at Tito on 3/18/19
10 | */
11 | @ActivityScope
12 | @Component(
13 | modules = [
14 | TVListActivityModule::class
15 | ],
16 | dependencies = [
17 | TVListComponent::class
18 | ]
19 | )
20 | interface TVListActivityComponent {
21 |
22 | fun inject(tvListActivity: TVListActivity)
23 |
24 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/tvlist/di/TVListActivityComponentWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.tvlist.di
2 |
3 | import android.app.Application
4 | import com.ahmedadel.robustandroid.presentation.mvp.tvlist.di.TVListComponent
5 | import com.ahmedadel.robustandroid.presentation.mvp.tvlist.di.TVListComponentWrapper
6 | import com.ahmedadel.robustandroid.tvlist.TVListActivity
7 |
8 | /**
9 | * Created at Tito on 3/18/19
10 | */
11 |
12 | class TVListActivityComponentWrapper {
13 |
14 | private lateinit var component: TVListActivityComponent
15 |
16 | private fun initializeComponent(tvListComponent: TVListComponent) {
17 | component = DaggerTVListActivityComponent.builder()
18 | .tVListComponent(tvListComponent)
19 | .build()
20 | }
21 |
22 | companion object {
23 |
24 | private var wrapper: TVListActivityComponentWrapper? = null
25 |
26 | @Synchronized
27 | private fun getInstance(application: Application): TVListActivityComponentWrapper {
28 | if (wrapper == null) {
29 | if (wrapper == null) {
30 | wrapper = TVListActivityComponentWrapper()
31 | wrapper!!.initializeComponent(TVListComponentWrapper.getComponent(application))
32 | }
33 | }
34 | return wrapper!!
35 | }
36 |
37 | fun buildComponent(tvListActivity: TVListActivity) {
38 | getInstance(tvListActivity.application).component.inject(tvListActivity)
39 | }
40 |
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/ui/src/main/java/com/ahmedadel/robustandroid/tvlist/di/TVListActivityModule.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid.tvlist.di
2 |
3 | import com.ahmedadel.robustandroid.di.ActivityScope
4 | import com.ahmedadel.robustandroid.tvlist.adapter.TVListAdapter
5 | import dagger.Module
6 | import dagger.Provides
7 |
8 | /**
9 | * Created at Tito on 3/18/19
10 | */
11 |
12 | @Module
13 | class TVListActivityModule {
14 |
15 | @Provides
16 | @ActivityScope
17 | fun provideTVListAdapter(): TVListAdapter = TVListAdapter()
18 |
19 | }
--------------------------------------------------------------------------------
/ui/src/main/res-screens/home/layout/movie_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
18 |
19 |
27 |
28 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ui/src/main/res-screens/home/layout/person_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
18 |
19 |
27 |
28 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ui/src/main/res-screens/home/layout/tv_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
18 |
19 |
27 |
28 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ui/src/main/res-screens/person-list/layout/activity_person_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
20 |
21 |
30 |
31 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/ui/src/main/res-screens/tv-details/layout/activity_tv_details.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/ui/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/ui/src/main/res/drawable/tmdb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeAreEGDroid/Robust-Android-App/314fbfd40061f72f96424c462f955c7a581be799/ui/src/main/res/drawable/tmdb.png
--------------------------------------------------------------------------------
/ui/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/ui/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ui/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ui/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeAreEGDroid/Robust-Android-App/314fbfd40061f72f96424c462f955c7a581be799/ui/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ui/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeAreEGDroid/Robust-Android-App/314fbfd40061f72f96424c462f955c7a581be799/ui/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/ui/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeAreEGDroid/Robust-Android-App/314fbfd40061f72f96424c462f955c7a581be799/ui/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ui/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeAreEGDroid/Robust-Android-App/314fbfd40061f72f96424c462f955c7a581be799/ui/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/ui/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeAreEGDroid/Robust-Android-App/314fbfd40061f72f96424c462f955c7a581be799/ui/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ui/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeAreEGDroid/Robust-Android-App/314fbfd40061f72f96424c462f955c7a581be799/ui/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/ui/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeAreEGDroid/Robust-Android-App/314fbfd40061f72f96424c462f955c7a581be799/ui/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ui/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeAreEGDroid/Robust-Android-App/314fbfd40061f72f96424c462f955c7a581be799/ui/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/ui/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeAreEGDroid/Robust-Android-App/314fbfd40061f72f96424c462f955c7a581be799/ui/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ui/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeAreEGDroid/Robust-Android-App/314fbfd40061f72f96424c462f955c7a581be799/ui/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/ui/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 | #e0f2f1
8 | #b2dfdb
9 | #80cbc4
10 | #4db6ac
11 | #26a69a
12 | #009688
13 | #00897b
14 | #00796b
15 | #00695c
16 | #004d40
17 | #a7ffeb
18 | #64ffda
19 | #1de9b6
20 | #00bfa5
21 | #80999999
22 |
23 |
24 |
--------------------------------------------------------------------------------
/ui/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RobustAndroidApp
3 |
4 | more
5 | Movies
6 | Persons
7 | Tvs
8 | No results 😓
9 | Home
10 | Movie List
11 | Person List
12 | TV List
13 | Recommendation
14 | Similar
15 | Language : %s
16 | Release Date : %s
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ui/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
16 |
17 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ui/src/main/res/xml/backup_descriptor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ui/src/test/java/com/ahmedadel/robustandroid/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.ahmedadel.robustandroid
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 | }
18 |
--------------------------------------------------------------------------------