├── .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 | 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 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | --------------------------------------------------------------------------------