├── .gitignore
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── gradle.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── isfaaghyth
│ │ └── app
│ │ └── jetmovie
│ │ ├── ExampleInstrumentedTest.kt
│ │ └── MainActivityTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── isfaaghyth
│ │ │ └── app
│ │ │ └── jetmovie
│ │ │ ├── MainActivity.kt
│ │ │ └── deeplink
│ │ │ ├── AppDeepLinkModule.kt
│ │ │ └── DeepLinkRouterActivity.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── 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
│ └── test
│ └── java
│ └── isfaaghyth
│ └── app
│ └── jetmovie
│ └── ExampleUnitTest.kt
├── build.gradle
├── buildSrc
├── build.gradle.kts
├── build
│ ├── classes
│ │ └── kotlin
│ │ │ └── main
│ │ │ ├── Android.class
│ │ │ ├── ApplicationId.class
│ │ │ ├── Coroutines.class
│ │ │ ├── Dagger.class
│ │ │ ├── Dependencies.class
│ │ │ ├── Glide.class
│ │ │ ├── Jetpack.class
│ │ │ ├── META-INF
│ │ │ └── buildSrc.kotlin_module
│ │ │ ├── Misc.class
│ │ │ ├── Modules.class
│ │ │ ├── Releases.class
│ │ │ ├── Retrofit.class
│ │ │ ├── Testing.class
│ │ │ └── Version.class
│ ├── kotlin
│ │ ├── buildSrcjar-classes.txt
│ │ └── compileKotlin
│ │ │ ├── build-history.bin
│ │ │ ├── caches-jvm
│ │ │ ├── inputs
│ │ │ │ ├── source-to-output.tab
│ │ │ │ ├── source-to-output.tab.keystream
│ │ │ │ ├── source-to-output.tab.keystream.len
│ │ │ │ ├── source-to-output.tab.len
│ │ │ │ ├── source-to-output.tab.values.at
│ │ │ │ ├── source-to-output.tab_i
│ │ │ │ └── source-to-output.tab_i.len
│ │ │ ├── jvm
│ │ │ │ └── kotlin
│ │ │ │ │ ├── class-fq-name-to-source.tab
│ │ │ │ │ ├── class-fq-name-to-source.tab.keystream
│ │ │ │ │ ├── class-fq-name-to-source.tab.keystream.len
│ │ │ │ │ ├── class-fq-name-to-source.tab.len
│ │ │ │ │ ├── class-fq-name-to-source.tab.values.at
│ │ │ │ │ ├── class-fq-name-to-source.tab_i
│ │ │ │ │ ├── class-fq-name-to-source.tab_i.len
│ │ │ │ │ ├── constants.tab
│ │ │ │ │ ├── constants.tab.keystream
│ │ │ │ │ ├── constants.tab.keystream.len
│ │ │ │ │ ├── constants.tab.len
│ │ │ │ │ ├── constants.tab.values.at
│ │ │ │ │ ├── constants.tab_i
│ │ │ │ │ ├── constants.tab_i.len
│ │ │ │ │ ├── internal-name-to-source.tab
│ │ │ │ │ ├── internal-name-to-source.tab.keystream
│ │ │ │ │ ├── internal-name-to-source.tab.keystream.len
│ │ │ │ │ ├── internal-name-to-source.tab.len
│ │ │ │ │ ├── internal-name-to-source.tab.values.at
│ │ │ │ │ ├── internal-name-to-source.tab_i
│ │ │ │ │ ├── internal-name-to-source.tab_i.len
│ │ │ │ │ ├── proto.tab
│ │ │ │ │ ├── proto.tab.keystream
│ │ │ │ │ ├── proto.tab.keystream.len
│ │ │ │ │ ├── proto.tab.len
│ │ │ │ │ ├── proto.tab.values.at
│ │ │ │ │ ├── proto.tab_i
│ │ │ │ │ ├── proto.tab_i.len
│ │ │ │ │ ├── source-to-classes.tab
│ │ │ │ │ ├── source-to-classes.tab.keystream
│ │ │ │ │ ├── source-to-classes.tab.keystream.len
│ │ │ │ │ ├── source-to-classes.tab.len
│ │ │ │ │ ├── source-to-classes.tab.values.at
│ │ │ │ │ ├── source-to-classes.tab_i
│ │ │ │ │ └── source-to-classes.tab_i.len
│ │ │ └── lookups
│ │ │ │ ├── counters.tab
│ │ │ │ ├── file-to-id.tab
│ │ │ │ ├── file-to-id.tab.keystream
│ │ │ │ ├── file-to-id.tab.keystream.len
│ │ │ │ ├── file-to-id.tab.len
│ │ │ │ ├── file-to-id.tab.values.at
│ │ │ │ ├── file-to-id.tab_i
│ │ │ │ ├── file-to-id.tab_i.len
│ │ │ │ ├── id-to-file.tab
│ │ │ │ ├── id-to-file.tab.keystream
│ │ │ │ ├── id-to-file.tab.keystream.len
│ │ │ │ ├── id-to-file.tab.len
│ │ │ │ ├── id-to-file.tab.values.at
│ │ │ │ ├── id-to-file.tab_i
│ │ │ │ ├── id-to-file.tab_i.len
│ │ │ │ ├── lookups.tab
│ │ │ │ ├── lookups.tab.keystream
│ │ │ │ ├── lookups.tab.keystream.len
│ │ │ │ ├── lookups.tab.len
│ │ │ │ ├── lookups.tab.values.at
│ │ │ │ ├── lookups.tab_i
│ │ │ │ └── lookups.tab_i.len
│ │ │ ├── data-container-format-version.txt
│ │ │ ├── format-version.txt
│ │ │ ├── gradle-format-version.txt
│ │ │ └── last-build.bin
│ ├── libs
│ │ └── buildSrc.jar
│ ├── source-roots
│ │ └── buildSrc
│ │ │ └── source-roots.txt
│ └── tmp
│ │ └── jar
│ │ └── MANIFEST.MF
└── src
│ └── main
│ └── java
│ ├── Dependencies.kt
│ └── Modules.kt
├── common.gradle
├── data
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── isfaaghyth
│ │ │ └── app
│ │ │ └── data
│ │ │ ├── di
│ │ │ ├── DataModule.kt
│ │ │ └── DataScope.kt
│ │ │ ├── entity
│ │ │ ├── Movie.kt
│ │ │ └── TVShow.kt
│ │ │ ├── repository
│ │ │ ├── movie
│ │ │ │ ├── MovieRepository.kt
│ │ │ │ └── MovieRepositoryImpl.kt
│ │ │ ├── movie_detail
│ │ │ │ ├── MovieDetailRepository.kt
│ │ │ │ └── MovieDetailRepositoryImpl.kt
│ │ │ └── tvshow
│ │ │ │ ├── TVShowRepository.kt
│ │ │ │ └── TVShowRepositoryImpl.kt
│ │ │ └── service
│ │ │ └── NetworkServices.kt
│ └── res
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── isfaaghyth
│ └── app
│ └── data
│ └── repository
│ ├── MockAPIResponse.kt
│ ├── NetworkMovieTest.kt
│ ├── movie
│ └── MovieRepositoryTest.kt
│ ├── movie_detail
│ └── MovieDetailRepositoryTest.kt
│ └── tvshow
│ └── TVShowRepositoryTest.kt
├── features
├── movie_details
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── isfaaghyth
│ │ │ └── app
│ │ │ └── movie_details
│ │ │ └── ExampleInstrumentedTest.java
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── isfaaghyth
│ │ │ │ └── app
│ │ │ │ └── movie_details
│ │ │ │ ├── di
│ │ │ │ ├── MovieDetailComponent.kt
│ │ │ │ ├── MovieDetailDeepLinkModule.kt
│ │ │ │ └── MovieDetailScope.kt
│ │ │ │ ├── domain
│ │ │ │ └── MovieDetailUseCase.kt
│ │ │ │ └── ui
│ │ │ │ ├── MovieDetailActivity.kt
│ │ │ │ ├── MovieDetailModule.kt
│ │ │ │ ├── MovieDetailViewModel.kt
│ │ │ │ └── MovieDetailViewModelModule.kt
│ │ └── res
│ │ │ ├── layout
│ │ │ └── activity_movie_detail.xml
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── isfaaghyth
│ │ └── app
│ │ └── movie_details
│ │ ├── ExampleUnitTest.java
│ │ ├── domain
│ │ └── MovieDetailUseCaseTest.kt
│ │ └── ui
│ │ └── MovieDetailViewModelTest.kt
├── movies
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── isfaaghyth
│ │ │ └── app
│ │ │ └── movies
│ │ │ └── ExampleInstrumentedTest.java
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── isfaaghyth
│ │ │ │ └── app
│ │ │ │ └── movies
│ │ │ │ ├── di
│ │ │ │ ├── MovieComponent.kt
│ │ │ │ └── MovieScope.kt
│ │ │ │ ├── domain
│ │ │ │ └── MovieUseCase.kt
│ │ │ │ └── ui
│ │ │ │ ├── MovieAdapter.kt
│ │ │ │ ├── MovieFragment.kt
│ │ │ │ ├── MovieModule.kt
│ │ │ │ ├── MovieViewModel.kt
│ │ │ │ └── MovieViewModelModule.kt
│ │ └── res
│ │ │ ├── layout
│ │ │ ├── fragment_movie.xml
│ │ │ └── item_movie.xml
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── isfaaghyth
│ │ └── app
│ │ └── movies
│ │ ├── ExampleUnitTest.java
│ │ ├── domain
│ │ └── MovieUseCaseTest.kt
│ │ └── ui
│ │ └── MovieViewModelTest.kt
└── tvshows
│ ├── .gitignore
│ ├── build.gradle
│ ├── build
│ └── generated
│ │ └── source
│ │ └── buildConfig
│ │ └── debug
│ │ └── isfaaghyth
│ │ └── app
│ │ └── tvshows
│ │ └── BuildConfig.java
│ ├── proguard-rules.pro
│ └── src
│ ├── androidTest
│ └── java
│ │ └── isfaaghyth
│ │ └── app
│ │ └── tvshows
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── isfaaghyth
│ │ │ └── app
│ │ │ └── tvshows
│ │ │ ├── di
│ │ │ ├── TVShowComponent.kt
│ │ │ └── TVShowScope.kt
│ │ │ ├── domain
│ │ │ └── TVShowUseCase.kt
│ │ │ └── ui
│ │ │ ├── TVShowAdapter.kt
│ │ │ ├── TVShowFragment.kt
│ │ │ ├── TVShowModule.kt
│ │ │ ├── TVShowViewModel.kt
│ │ │ └── TVShowViewModelModule.kt
│ └── res
│ │ ├── layout
│ │ ├── fragment_tvshow.xml
│ │ └── item_tvshow.xml
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── isfaaghyth
│ └── app
│ └── tvshows
│ ├── ExampleUnitTest.java
│ ├── domain
│ └── TVShowUseCaseTest.kt
│ └── ui
│ └── TVShowViewModelTest.kt
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── libraries
├── abstraction
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── isfaaghyth
│ │ │ └── app
│ │ │ └── abstraction
│ │ │ └── ExampleInstrumentedTest.java
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── ic_placeholder-web.png
│ │ ├── ic_rating-web.png
│ │ ├── ic_voter-web.png
│ │ ├── java
│ │ │ └── isfaaghyth
│ │ │ │ └── app
│ │ │ │ └── abstraction
│ │ │ │ ├── annotation
│ │ │ │ └── Testable.kt
│ │ │ │ ├── base
│ │ │ │ ├── BaseActivity.kt
│ │ │ │ ├── BaseView.kt
│ │ │ │ └── BaseViewModel.kt
│ │ │ │ ├── helper
│ │ │ │ └── EspressoIdlingResource.kt
│ │ │ │ ├── ui
│ │ │ │ └── ViewPagerAdapter.kt
│ │ │ │ └── util
│ │ │ │ ├── Consts.kt
│ │ │ │ ├── ext
│ │ │ │ ├── CoroutinesUtil.kt
│ │ │ │ ├── ImageExt.kt
│ │ │ │ └── UtilExt.kt
│ │ │ │ ├── state
│ │ │ │ ├── LoaderState.kt
│ │ │ │ └── ResultState.kt
│ │ │ │ ├── thread
│ │ │ │ ├── ApplicationSchedulerProvider.kt
│ │ │ │ ├── SchedulerProvider.kt
│ │ │ │ └── TestSchedulerProvider.kt
│ │ │ │ ├── view
│ │ │ │ └── KeyboardUtils.kt
│ │ │ │ └── viewmodel
│ │ │ │ ├── ViewModelFactory.kt
│ │ │ │ └── ViewModelKey.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── bg_gradient.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_placeholder.png
│ │ │ ├── ic_rating.png
│ │ │ └── ic_voter.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_placeholder.png
│ │ │ ├── ic_rating.png
│ │ │ └── ic_voter.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_placeholder.png
│ │ │ ├── ic_rating.png
│ │ │ └── ic_voter.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_placeholder.png
│ │ │ ├── ic_rating.png
│ │ │ └── ic_voter.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_placeholder.png
│ │ │ ├── ic_rating.png
│ │ │ └── ic_voter.png
│ │ │ └── values
│ │ │ ├── dimens.xml
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── isfaaghyth
│ │ └── app
│ │ └── abstraction
│ │ └── ExampleUnitTest.java
└── network
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ ├── androidTest
│ └── java
│ │ └── isfaaghyth
│ │ └── app
│ │ └── network
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── isfaaghyth
│ │ │ └── app
│ │ │ └── network
│ │ │ ├── Network.kt
│ │ │ └── NetworkInterceptor.kt
│ └── res
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── isfaaghyth
│ └── app
│ └── network
│ └── ExampleUnitTest.java
└── settings.gradle
/.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/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
26 |
27 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'kotlin-kapt'
5 |
6 | android {
7 | compileSdkVersion Releases.compileSdkVersion
8 | buildToolsVersion Releases.buildToolsVersion
9 |
10 | defaultConfig {
11 | applicationId ApplicationId.id
12 | minSdkVersion Releases.minSdkVersion
13 | targetSdkVersion Releases.targetSdkVersion
14 | versionCode Releases.versionCode
15 | versionName Releases.versionName
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 |
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation Dependencies.kotlin
35 |
36 | //android
37 | implementation Android.appCompat
38 | implementation Android.ktx
39 | implementation Android.constraintLayout
40 | implementation Android.design
41 |
42 | //deeplink
43 | implementation Misc.deeplink
44 | kapt Misc.deeplinkProcessor
45 |
46 | //dependency injection
47 | implementation Dagger.dagger
48 | implementation Dagger.android
49 | kapt Dagger.compiler
50 | kapt Dagger.processor
51 |
52 | //testing
53 | testImplementation Testing.jUnit
54 | testImplementation Testing.mockito
55 | testImplementation Testing.mockKtRunner
56 | androidTestImplementation Testing.coreTesting
57 | androidTestImplementation Testing.testRunner
58 | androidTestImplementation Testing.espresso
59 | androidTestImplementation Testing.espressoContrib
60 | androidTestImplementation Testing.runner
61 | androidTestImplementation Testing.rules
62 | androidTestImplementation Testing.core
63 | androidTestImplementation Testing.espressoIdleResources
64 | androidTestImplementation Testing.extJunit
65 | androidTestImplementation Testing.extTruth
66 |
67 | /* Feature Module should add below */
68 | implementation project(Modules.abstraction)
69 | implementation project(Modules.movieDetail)
70 | implementation project(Modules.tvshows)
71 | implementation project(Modules.movies)
72 | }
73 |
74 | repositories {
75 | google()
76 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/isfaaghyth/app/jetmovie/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.jetmovie
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("isfaaghyth.app.jetmovie", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/isfaaghyth/app/jetmovie/MainActivityTest.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.jetmovie
2 |
3 | import androidx.recyclerview.widget.RecyclerView
4 | import androidx.test.espresso.Espresso.onView
5 | import androidx.test.espresso.Espresso.pressBack
6 | import androidx.test.espresso.IdlingRegistry
7 | import androidx.test.espresso.action.ViewActions.click
8 | import androidx.test.espresso.action.ViewActions.swipeLeft
9 | import androidx.test.espresso.assertion.ViewAssertions.matches
10 | import androidx.test.espresso.contrib.RecyclerViewActions
11 | import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
12 | import androidx.test.espresso.matcher.ViewMatchers.withId
13 | import androidx.test.ext.junit.runners.AndroidJUnit4
14 | import androidx.test.filters.LargeTest
15 | import androidx.test.rule.ActivityTestRule
16 | import isfaaghyth.app.abstraction.helper.FetchingIdlingResource
17 | import org.junit.After
18 | import org.junit.Before
19 | import org.junit.Rule
20 | import org.junit.Test
21 | import org.junit.runner.RunWith
22 |
23 | @LargeTest
24 | @RunWith(AndroidJUnit4::class)
25 | class MainActivityTest {
26 |
27 | @get:Rule
28 | val activityRule = ActivityTestRule(
29 | MainActivity::class.java,
30 | false,
31 | false)
32 |
33 | private val itemPosition = 5
34 |
35 | @Before fun setUp() {
36 | activityRule.launchActivity(null)
37 | IdlingRegistry.getInstance().register(FetchingIdlingResource.get())
38 | }
39 |
40 | @Test fun testMovieList() {
41 | //check visibility of listMovies
42 | onView(withId(R.id.lstMovies)).check(matches(isDisplayed()))
43 |
44 | //scroll movie list into 5
45 | onView(withId(R.id.lstMovies)).perform(RecyclerViewActions.scrollToPosition(itemPosition))
46 |
47 | //goto movieDetailActivity
48 | onView(withId(R.id.lstMovies)).perform(RecyclerViewActions.actionOnItemAtPosition(itemPosition, click()))
49 |
50 | //check visibility of image of banner
51 | onView(withId(R.id.imgBanner)).check(matches(isDisplayed()))
52 | }
53 |
54 | @Test fun testTvShowList() {
55 | onView(withId(R.id.viewpagerMain)).perform(swipeLeft())
56 |
57 | //check visibility of listMovies
58 | onView(withId(R.id.lstTvShows)).check(matches(isDisplayed()))
59 |
60 | //scroll movie list into 5
61 | onView(withId(R.id.lstTvShows)).perform(RecyclerViewActions.scrollToPosition(itemPosition))
62 |
63 | //goto movieDetailActivity
64 | onView(withId(R.id.lstTvShows)).perform(RecyclerViewActions.actionOnItemAtPosition(itemPosition, click()))
65 |
66 | //check visibility of image of banner
67 | onView(withId(R.id.imgBanner)).check(matches(isDisplayed()))
68 | }
69 |
70 | @After fun tearDown() {
71 | IdlingRegistry.getInstance().unregister(FetchingIdlingResource.get())
72 | }
73 |
74 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/java/isfaaghyth/app/jetmovie/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.jetmovie
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import androidx.fragment.app.Fragment
6 | import isfaaghyth.app.abstraction.ui.ViewPagerAdapter
7 | import isfaaghyth.app.movies.ui.MovieFragment
8 | import isfaaghyth.app.tvshows.ui.TVShowFragment
9 | import kotlinx.android.synthetic.main.activity_main.*
10 |
11 | class MainActivity : AppCompatActivity() {
12 |
13 | override fun onCreate(savedInstanceState: Bundle?) {
14 | super.onCreate(savedInstanceState)
15 | setContentView(R.layout.activity_main)
16 |
17 | viewPagerSetup()
18 | }
19 |
20 | private fun viewPagerSetup() {
21 | val adapter = ViewPagerAdapter(supportFragmentManager)
22 | adapter.addFragment(MovieFragment(), "Movie")
23 | adapter.addFragment(TVShowFragment(), "TV")
24 | viewpagerMain?.adapter = adapter
25 | tabLayoutMain?.setupWithViewPager(viewpagerMain)
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/isfaaghyth/app/jetmovie/deeplink/AppDeepLinkModule.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.jetmovie.deeplink
2 |
3 | import com.airbnb.deeplinkdispatch.DeepLinkModule
4 |
5 | @DeepLinkModule class AppDeepLinkModule
--------------------------------------------------------------------------------
/app/src/main/java/isfaaghyth/app/jetmovie/deeplink/DeepLinkRouterActivity.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.jetmovie.deeplink
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.airbnb.deeplinkdispatch.DeepLinkHandler
6 | import isfaaghyth.app.movie_details.di.MovieDetailDeepLinkModule
7 | import isfaaghyth.app.movie_details.di.MovieDetailDeepLinkModuleLoader
8 |
9 | @DeepLinkHandler(
10 | AppDeepLinkModule::class,
11 | MovieDetailDeepLinkModule::class
12 | )
13 | class DeepLinkRouterActivity: AppCompatActivity() {
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 | val deepLinkDelegate = DeepLinkDelegate(
17 | AppDeepLinkModuleLoader(),
18 | MovieDetailDeepLinkModuleLoader())
19 | deepLinkDelegate.dispatchFrom(this)
20 | finish()
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
21 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #30000000
4 | #99000000
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Jet Movie
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/isfaaghyth/app/jetmovie/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.jetmovie
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 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.20'
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.3.1'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 | classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 | mavenCentral()
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.gradle.kotlin.dsl.`kotlin-dsl`
2 |
3 | plugins {
4 | `kotlin-dsl`
5 | }
6 |
7 | // Required since Gradle 4.10+.
8 | repositories {
9 | jcenter()
10 | }
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/Android.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/Android.class
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/ApplicationId.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/ApplicationId.class
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/Coroutines.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/Coroutines.class
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/Dagger.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/Dagger.class
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/Dependencies.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/Dependencies.class
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/Glide.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/Glide.class
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/Jetpack.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/Jetpack.class
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/META-INF/buildSrc.kotlin_module:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/Misc.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/Misc.class
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/Modules.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/Modules.class
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/Releases.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/Releases.class
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/Retrofit.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/Retrofit.class
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/Testing.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/Testing.class
--------------------------------------------------------------------------------
/buildSrc/build/classes/kotlin/main/Version.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/classes/kotlin/main/Version.class
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/buildSrcjar-classes.txt:
--------------------------------------------------------------------------------
1 | /Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/Android.class:/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/ApplicationId.class:/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/Coroutines.class:/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/Dagger.class:/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/Dependencies.class:/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/Glide.class:/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/Jetpack.class:/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/Misc.class:/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/Modules.class:/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/Releases.class:/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/Retrofit.class:/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/Testing.class:/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/build/classes/kotlin/main/Version.class
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/build-history.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/build-history.bin
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.keystream:
--------------------------------------------------------------------------------
1 | S/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/src/main/java/Dependencies.kt
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.keystream.len:
--------------------------------------------------------------------------------
1 | T
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.values.at:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.values.at
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab_i:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab_i
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab_i.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab_i.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream:
--------------------------------------------------------------------------------
1 |
ApplicationIdReleasesVersionAndroidJetpackDependenciesRetrofit
2 | CoroutinesGlideDaggerTestingMisc
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len:
--------------------------------------------------------------------------------
1 | j
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len:
--------------------------------------------------------------------------------
1 | �
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.keystream:
--------------------------------------------------------------------------------
1 | Version
ApplicationIdReleases
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.keystream.len:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.values.at:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.values.at
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream:
--------------------------------------------------------------------------------
1 |
ApplicationIdReleasesVersionAndroidJetpackDependenciesRetrofit
2 | CoroutinesGlideDaggerTestingMisc
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len:
--------------------------------------------------------------------------------
1 | j
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len:
--------------------------------------------------------------------------------
1 | �
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.keystream:
--------------------------------------------------------------------------------
1 |
ApplicationIdReleasesVersionAndroidJetpackDependenciesRetrofit
2 | CoroutinesGlideDaggerTestingMisc.kotlin_module
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.keystream.len:
--------------------------------------------------------------------------------
1 | y
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.len:
--------------------------------------------------------------------------------
1 | �
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.values.at:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.values.at
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab_i:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab_i
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab_i.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab_i.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream:
--------------------------------------------------------------------------------
1 | S/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/src/main/java/Dependencies.kt
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len:
--------------------------------------------------------------------------------
1 | T
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at:
--------------------------------------------------------------------------------
1 | / Header Record For PersistentHashMapValueStoragey
ApplicationIdReleasesVersionAndroidJetpackDependenciesRetrofit
2 | CoroutinesGlideDaggerTestingMisc.kotlin_moduley
ApplicationIdReleasesVersionAndroidJetpackDependenciesRetrofit
3 | CoroutinesGlideDaggerTestingMisc.kotlin_moduley
ApplicationIdReleasesVersionAndroidJetpackDependenciesRetrofit
4 | CoroutinesGlideDaggerTestingMisc.kotlin_moduley
ApplicationIdReleasesVersionAndroidJetpackDependenciesRetrofit
5 | CoroutinesGlideDaggerTestingMisc.kotlin_moduley
ApplicationIdReleasesVersionAndroidJetpackDependenciesRetrofit
6 | CoroutinesGlideDaggerTestingMisc.kotlin_module
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab_i:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab_i
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/counters.tab:
--------------------------------------------------------------------------------
1 | 5
2 | 4
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.keystream:
--------------------------------------------------------------------------------
1 | S/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/src/main/java/Dependencies.kt
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.keystream.len:
--------------------------------------------------------------------------------
1 | U
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.values.at:
--------------------------------------------------------------------------------
1 | / Header Record For PersistentHashMapValueStorage
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab.keystream:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab.keystream.len:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab.values.at:
--------------------------------------------------------------------------------
1 | / Header Record For PersistentHashMapValueStorageU S/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/src/main/java/Dependencies.ktU S/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/src/main/java/Dependencies.ktU S/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/src/main/java/Dependencies.ktU S/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/src/main/java/Dependencies.ktU S/Users/nakama/AndroidStudioProjects/JetMovie/buildSrc/src/main/java/Dependencies.kt
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab_i:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab_i
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab_i.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab_i.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream.len:
--------------------------------------------------------------------------------
1 | �
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.len:
--------------------------------------------------------------------------------
1 | X
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.values.at:
--------------------------------------------------------------------------------
1 | / Header Record For PersistentHashMapValueStorage
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab_i:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab_i
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab_i.len:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab_i.len
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/data-container-format-version.txt:
--------------------------------------------------------------------------------
1 | 3011001
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/format-version.txt:
--------------------------------------------------------------------------------
1 | 9011001
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/gradle-format-version.txt:
--------------------------------------------------------------------------------
1 | 4011001
--------------------------------------------------------------------------------
/buildSrc/build/kotlin/compileKotlin/last-build.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/kotlin/compileKotlin/last-build.bin
--------------------------------------------------------------------------------
/buildSrc/build/libs/buildSrc.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/buildSrc/build/libs/buildSrc.jar
--------------------------------------------------------------------------------
/buildSrc/build/source-roots/buildSrc/source-roots.txt:
--------------------------------------------------------------------------------
1 | src/main/resources
2 | src/main/java
3 | src/main/groovy
4 | src/main/kotlin
5 | src/test/resources
6 | src/test/java
7 | src/test/groovy
8 | src/test/kotlin
9 |
--------------------------------------------------------------------------------
/buildSrc/build/tmp/jar/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 |
3 |
--------------------------------------------------------------------------------
/buildSrc/src/main/java/Modules.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by isfaaghyth on 31/07/19.
3 | * github: @isfaaghyth
4 | */
5 |
6 | object Modules {
7 | //app level
8 | val app = ":app"
9 |
10 | //feature level
11 | val movies = ":features:movies"
12 | val tvshows = ":features:tvshows"
13 | val movieDetail = ":features:movie_details"
14 |
15 | //libraries level
16 | val network = ":libraries:network"
17 | val abstraction = ":libraries:abstraction"
18 |
19 | //data level
20 | val data = ":data"
21 | }
--------------------------------------------------------------------------------
/common.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'kotlin-kapt'
5 | apply plugin: 'kotlin-allopen'
6 |
7 | allOpen {
8 | def openClass = ".annotation.OpenClass"
9 | annotation ApplicationId.abstraction + openClass
10 | }
11 |
12 | android {
13 | compileSdkVersion Releases.compileSdkVersion
14 |
15 | defaultConfig {
16 | minSdkVersion Releases.minSdkVersion
17 | targetSdkVersion Releases.targetSdkVersion
18 | versionCode Releases.versionCode
19 | versionName Releases.versionName
20 |
21 | buildConfigField("String", "API_KEY", movieApiKey)
22 | buildConfigField("String", "MOVIE_URL", movieApiUrl)
23 | buildConfigField("String", "IMAGE_URL", movieImageUrl)
24 | }
25 |
26 | buildTypes {
27 | debug {
28 |
29 | }
30 | release {
31 | minifyEnabled false
32 | }
33 | }
34 |
35 | compileOptions {
36 | sourceCompatibility JavaVersion.VERSION_1_8
37 | targetCompatibility JavaVersion.VERSION_1_8
38 | }
39 | }
40 |
41 | dependencies {
42 | implementation Dependencies.kotlin
43 |
44 | //di
45 | implementation Dagger.dagger
46 | implementation Dagger.android
47 | kapt Dagger.compiler
48 | kapt Dagger.processor
49 |
50 | //deeplink
51 | implementation Misc.deeplink
52 | kapt Misc.deeplinkProcessor
53 |
54 | //testing
55 | implementation Testing.espressoIdleResources
56 |
57 | testImplementation Testing.jUnit
58 | testImplementation Testing.mockito
59 | testImplementation Testing.mockKtRunner
60 | testImplementation Testing.androidX
61 | testImplementation Coroutines.core
62 | testImplementation Coroutines.android
63 | testImplementation Coroutines.test
64 | testImplementation Retrofit.mockWebServer
65 | androidTestImplementation Testing.testRunner
66 | androidTestImplementation Testing.espresso
67 | androidTestImplementation Testing.espressoContrib
68 | androidTestImplementation Testing.runner
69 | androidTestImplementation Testing.rules
70 | androidTestImplementation Testing.core
71 | androidTestImplementation Testing.espressoIdleResources
72 | androidTestImplementation Testing.extJunit
73 | androidTestImplementation Testing.extTruth
74 | }
--------------------------------------------------------------------------------
/data/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/data/build.gradle:
--------------------------------------------------------------------------------
1 | apply from: "$rootDir/common.gradle"
2 |
3 | dependencies {
4 | //utilities
5 | implementation Android.gson
6 |
7 | //network
8 | implementation Retrofit.retrofit
9 |
10 | //coroutines
11 | implementation Coroutines.core
12 |
13 | //modules
14 | implementation project(Modules.network)
15 | }
--------------------------------------------------------------------------------
/data/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/data/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/data/src/main/java/isfaaghyth/app/data/di/DataModule.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.di
2 |
3 | import dagger.Module
4 | import dagger.Provides
5 | import isfaaghyth.app.data.service.NetworkServices
6 | import isfaaghyth.app.network.Network.retrofitClient
7 |
8 | @Module class DataModule {
9 |
10 | @Provides @DataScope
11 | fun provideServices(): NetworkServices {
12 | return retrofitClient().create(NetworkServices::class.java)
13 | }
14 |
15 | }
--------------------------------------------------------------------------------
/data/src/main/java/isfaaghyth/app/data/di/DataScope.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.di
2 |
3 | import javax.inject.Qualifier
4 |
5 | @Qualifier annotation class DataScope
--------------------------------------------------------------------------------
/data/src/main/java/isfaaghyth/app/data/entity/Movie.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.entity
2 |
3 | import android.os.Parcel
4 | import android.os.Parcelable
5 | import com.google.gson.annotations.Expose
6 | import com.google.gson.annotations.SerializedName
7 | import isfaaghyth.app.data.BuildConfig
8 |
9 | data class Movies(
10 | @Expose @SerializedName("results") val resultsIntent: List
11 | )
12 |
13 | data class Movie(
14 | @Expose @SerializedName("id") val id: String,
15 | @Expose @SerializedName("movie_id") val movieId: String,
16 | @Expose @SerializedName("original_title") val title: String,
17 | @Expose @SerializedName("poster_path") val posterPath: String,
18 | @Expose @SerializedName("overview") val overview: String,
19 | @Expose @SerializedName("backdrop_path") val backdropPath: String,
20 | @Expose @SerializedName("vote_count") val voteCount: Int,
21 | @Expose @SerializedName("vote_average") val voteAverage: Float,
22 | @Expose @SerializedName("release_date") val releaseDate: String
23 | ): Parcelable {
24 |
25 | fun bannerUrl() = "${BuildConfig.IMAGE_URL}$backdropPath"
26 |
27 | fun posterUrl() = "${BuildConfig.IMAGE_URL}$posterPath"
28 |
29 | constructor(parcel: Parcel) : this(
30 | parcel.readString()?: "",
31 | parcel.readString()?: "",
32 | parcel.readString()?: "",
33 | parcel.readString()?: "",
34 | parcel.readString()?: "",
35 | parcel.readString()?: "",
36 | parcel.readInt(),
37 | parcel.readFloat(),
38 | parcel.readString()?: ""
39 | )
40 |
41 | override fun writeToParcel(parcel: Parcel, flags: Int) {
42 | parcel.writeString(id)
43 | parcel.writeString(movieId)
44 | parcel.writeString(title)
45 | parcel.writeString(posterPath)
46 | parcel.writeString(overview)
47 | parcel.writeString(backdropPath)
48 | parcel.writeInt(voteCount)
49 | parcel.writeFloat(voteAverage)
50 | parcel.writeString(releaseDate)
51 | }
52 |
53 | override fun describeContents(): Int {
54 | return 0
55 | }
56 |
57 | companion object CREATOR : Parcelable.Creator {
58 | override fun createFromParcel(parcel: Parcel): Movie {
59 | return Movie(parcel)
60 | }
61 |
62 | override fun newArray(size: Int): Array {
63 | return arrayOfNulls(size)
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/data/src/main/java/isfaaghyth/app/data/entity/TVShow.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.entity
2 |
3 | import android.os.Parcel
4 | import android.os.Parcelable
5 | import com.google.gson.annotations.Expose
6 | import com.google.gson.annotations.SerializedName
7 | import isfaaghyth.app.data.BuildConfig
8 |
9 | data class TVShows(
10 | @Expose @SerializedName("results") val resultsIntent: List
11 | )
12 |
13 | data class TVShow(
14 | @Expose @SerializedName("id") val id: String,
15 | @Expose @SerializedName("movie_id") val movieId: String,
16 | @Expose @SerializedName("original_name") val title: String,
17 | @Expose @SerializedName("poster_path") val posterPath: String,
18 | @Expose @SerializedName("overview") val overview: String,
19 | @Expose @SerializedName("backdrop_path") val backdropPath: String,
20 | @Expose @SerializedName("vote_count") val voteCount: Int,
21 | @Expose @SerializedName("vote_average") val voteAverage: Float,
22 | @Expose @SerializedName("first_air_date") val releaseDate: String
23 | ): Parcelable {
24 |
25 | fun bannerUrl() = "${BuildConfig.IMAGE_URL}$backdropPath"
26 |
27 | fun posterUrl() = "${BuildConfig.IMAGE_URL}$posterPath"
28 |
29 | constructor(parcel: Parcel) : this(
30 | parcel.readString()?: "",
31 | parcel.readString()?: "",
32 | parcel.readString()?: "",
33 | parcel.readString()?: "",
34 | parcel.readString()?: "",
35 | parcel.readString()?: "",
36 | parcel.readInt(),
37 | parcel.readFloat(),
38 | parcel.readString()?: ""
39 | )
40 |
41 | override fun writeToParcel(parcel: Parcel, flags: Int) {
42 | parcel.writeString(id)
43 | parcel.writeString(movieId)
44 | parcel.writeString(title)
45 | parcel.writeString(posterPath)
46 | parcel.writeString(overview)
47 | parcel.writeString(backdropPath)
48 | parcel.writeInt(voteCount)
49 | parcel.writeFloat(voteAverage)
50 | parcel.writeString(releaseDate)
51 | }
52 |
53 | override fun describeContents(): Int {
54 | return 0
55 | }
56 |
57 | companion object CREATOR : Parcelable.Creator {
58 | override fun createFromParcel(parcel: Parcel): TVShow {
59 | return TVShow(parcel)
60 | }
61 |
62 | override fun newArray(size: Int): Array {
63 | return arrayOfNulls(size)
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/data/src/main/java/isfaaghyth/app/data/repository/movie/MovieRepository.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.repository.movie
2 |
3 | import isfaaghyth.app.data.entity.Movies
4 | import kotlinx.coroutines.Deferred
5 | import retrofit2.Response
6 |
7 | interface MovieRepository {
8 | suspend fun getPopularMovie(): Response
9 | }
--------------------------------------------------------------------------------
/data/src/main/java/isfaaghyth/app/data/repository/movie/MovieRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.repository.movie
2 |
3 | import isfaaghyth.app.data.entity.Movies
4 | import isfaaghyth.app.data.service.NetworkServices
5 | import retrofit2.Response
6 | import javax.inject.Inject
7 |
8 | class MovieRepositoryImpl @Inject constructor(
9 | private val service: NetworkServices
10 | ): MovieRepository {
11 |
12 | override suspend fun getPopularMovie(): Response {
13 | return service.getPopularMovie()
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/data/src/main/java/isfaaghyth/app/data/repository/movie_detail/MovieDetailRepository.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.repository.movie_detail
2 |
3 | import isfaaghyth.app.data.entity.Movie
4 | import isfaaghyth.app.data.entity.TVShow
5 | import kotlinx.coroutines.Deferred
6 | import retrofit2.Response
7 |
8 | interface MovieDetailRepository {
9 | suspend fun getMovieDetail(movieId: String): Response
10 | suspend fun getTVShowDetail(movieId: String): Response
11 | }
--------------------------------------------------------------------------------
/data/src/main/java/isfaaghyth/app/data/repository/movie_detail/MovieDetailRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.repository.movie_detail
2 |
3 | import isfaaghyth.app.data.entity.Movie
4 | import isfaaghyth.app.data.entity.TVShow
5 | import isfaaghyth.app.data.service.NetworkServices
6 | import retrofit2.Response
7 | import javax.inject.Inject
8 |
9 | class MovieDetailRepositoryImpl @Inject constructor(
10 | private val service: NetworkServices
11 | ): MovieDetailRepository {
12 |
13 | override suspend fun getMovieDetail(movieId: String): Response {
14 | return service.getMovieDetail(movieId)
15 | }
16 |
17 | override suspend fun getTVShowDetail(movieId: String): Response {
18 | return service.getTvDetail(movieId)
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/data/src/main/java/isfaaghyth/app/data/repository/tvshow/TVShowRepository.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.repository.tvshow
2 |
3 | import isfaaghyth.app.data.entity.TVShows
4 | import kotlinx.coroutines.Deferred
5 | import retrofit2.Response
6 |
7 | interface TVShowRepository {
8 | suspend fun getPopularTVShow(): Response
9 | }
--------------------------------------------------------------------------------
/data/src/main/java/isfaaghyth/app/data/repository/tvshow/TVShowRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.repository.tvshow
2 |
3 | import isfaaghyth.app.data.entity.TVShows
4 | import isfaaghyth.app.data.service.NetworkServices
5 | import retrofit2.Response
6 | import javax.inject.Inject
7 |
8 | class TVShowRepositoryImpl @Inject constructor(
9 | private val service: NetworkServices
10 | ): TVShowRepository {
11 |
12 | override suspend fun getPopularTVShow(): Response {
13 | return service.getPopularTVShow()
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/data/src/main/java/isfaaghyth/app/data/service/NetworkServices.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.service
2 |
3 | import isfaaghyth.app.data.entity.Movie
4 | import isfaaghyth.app.data.entity.Movies
5 | import isfaaghyth.app.data.entity.TVShow
6 | import isfaaghyth.app.data.entity.TVShows
7 | import retrofit2.Response
8 | import retrofit2.http.GET
9 | import retrofit2.http.Path
10 | import retrofit2.http.Query
11 |
12 | interface NetworkServices {
13 |
14 | @GET("movie/popular")
15 | suspend fun getPopularMovie(): Response
16 |
17 | @GET("tv/popular")
18 | suspend fun getPopularTVShow(): Response
19 |
20 | @GET("movie/{movie_id}")
21 | suspend fun getMovieDetail(
22 | @Path("movie_id") movieId: String
23 | ): Response
24 |
25 | @GET("tv/{movie_id}")
26 | suspend fun getTvDetail(
27 | @Path("movie_id") movieId: String
28 | ): Response
29 |
30 | }
--------------------------------------------------------------------------------
/data/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/src/test/java/isfaaghyth/app/data/repository/NetworkMovieTest.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.repository
2 |
3 | import okhttp3.mockwebserver.MockResponse
4 | import okhttp3.mockwebserver.MockWebServer
5 | import org.junit.After
6 | import org.junit.Assert.assertEquals
7 | import org.junit.Before
8 | import org.junit.Test
9 |
10 | /**
11 | * Experimental purpose
12 | */
13 | class NetworkMovieTest {
14 |
15 | private lateinit var mockWebServer: MockWebServer
16 |
17 | private lateinit var success: MockResponse
18 | private lateinit var error: MockResponse
19 |
20 | @Before fun setUp() {
21 | mockWebServer = MockWebServer()
22 | mockWebServer.url("/")
23 |
24 | success = MockResponse()
25 | .setBody(successJson)
26 | .setResponseCode(200)
27 |
28 | error = MockResponse()
29 | .setBody(errorJson)
30 | .setResponseCode(500)
31 | }
32 |
33 | @Test fun `test success mock web server`() {
34 | mockWebServer.enqueue(success)
35 | assertEquals("HTTP/1.1 200 OK", success.status)
36 | }
37 |
38 | @Test fun `test error mock web server`() {
39 | mockWebServer.enqueue(error)
40 | assertEquals("HTTP/1.1 500 Server Error", error.status)
41 | }
42 |
43 | @After fun tearDown() {
44 | mockWebServer.shutdown()
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/data/src/test/java/isfaaghyth/app/data/repository/movie/MovieRepositoryTest.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.repository.movie
2 |
3 | import isfaaghyth.app.data.entity.Movie
4 | import isfaaghyth.app.data.entity.Movies
5 | import isfaaghyth.app.data.service.NetworkServices
6 | import kotlinx.coroutines.runBlocking
7 | import okhttp3.MediaType
8 | import okhttp3.ResponseBody
9 | import org.junit.After
10 | import org.junit.Assert.assertEquals
11 | import org.junit.Before
12 | import org.junit.Test
13 | import org.mockito.Mockito.*
14 | import retrofit2.Response
15 |
16 | class MovieRepositoryTest {
17 |
18 | private var services = mock(NetworkServices::class.java)
19 | private lateinit var repository: MovieRepository
20 |
21 | private val movies = listOf(
22 | Movie(
23 | "id",
24 | "movieId",
25 | "title",
26 | "posterPath",
27 | "overview",
28 | "backdrop",
29 | 0,
30 | 0f,
31 | "relateDate"
32 | )
33 | )
34 |
35 | @Before fun setUp() {
36 | repository = MovieRepositoryImpl(services)
37 | }
38 |
39 | @Test fun `should get popular movie success`() = runBlocking {
40 | `when`(services.getPopularMovie()).thenReturn(
41 | Response.success(Movies(movies))
42 | )
43 | val repo = repository.getPopularMovie()
44 |
45 | assertEquals(repo.body(), Movies(movies))
46 | }
47 |
48 | @Test fun `should get null and error`() = runBlocking {
49 | `when`(services.getPopularMovie()).thenReturn(
50 | Response.error(401, ResponseBody.create(MediaType.parse("application/json"), ""))
51 | )
52 | val repo = repository.getPopularMovie()
53 |
54 | assertEquals(repo.body(), null)
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/data/src/test/java/isfaaghyth/app/data/repository/movie_detail/MovieDetailRepositoryTest.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.repository.movie_detail
2 |
3 | import isfaaghyth.app.data.entity.Movie
4 | import isfaaghyth.app.data.entity.TVShow
5 | import isfaaghyth.app.data.service.NetworkServices
6 | import kotlinx.coroutines.runBlocking
7 | import okhttp3.MediaType
8 | import okhttp3.ResponseBody
9 | import org.junit.Assert.assertEquals
10 | import org.junit.Before
11 | import org.junit.Test
12 | import org.mockito.Mockito
13 | import org.mockito.Mockito.mock
14 | import retrofit2.Response
15 |
16 | class MovieDetailRepositoryTest {
17 |
18 | private var services = mock(NetworkServices::class.java)
19 | private lateinit var repository: MovieDetailRepository
20 |
21 | private val movieId = "1"
22 |
23 | private val tvshow = TVShow(
24 | "id",
25 | "movieId",
26 | "title",
27 | "posterPath",
28 | "overview",
29 | "backdrop",
30 | 0,
31 | 0f,
32 | "relateDate"
33 | )
34 |
35 | private val movie = Movie(
36 | "id",
37 | "movieId",
38 | "title",
39 | "posterPath",
40 | "overview",
41 | "backdrop",
42 | 0,
43 | 0f,
44 | "relateDate"
45 | )
46 |
47 | @Before fun setUp() {
48 | repository = MovieDetailRepositoryImpl(services)
49 | }
50 |
51 | @Test fun `should get popular tv show detail success`() = runBlocking {
52 | Mockito.`when`(services.getTvDetail(movieId))
53 | .thenReturn(
54 | Response.success(tvshow)
55 | )
56 | val repo = repository.getTVShowDetail(movieId)
57 | assert(repo.body() === tvshow)
58 | }
59 |
60 | @Test fun `should get popular movie detail success`() = runBlocking {
61 | Mockito.`when`(services.getMovieDetail(movieId))
62 | .thenReturn(
63 | Response.success(movie)
64 | )
65 | val repo = repository.getMovieDetail(movieId)
66 | assert(repo.body() === movie)
67 | }
68 |
69 | @Test fun `should get null when getting tv show detail and error`() = runBlocking {
70 | Mockito.`when`(services.getTvDetail(movieId))
71 | .thenReturn(
72 | Response.error(401, ResponseBody.create(MediaType.parse("application/json"), ""))
73 | )
74 | val repo = repository.getTVShowDetail(movieId)
75 | assert(repo.body() === null)
76 | }
77 |
78 | @Test fun `should get null when getting movie detail and error`() = runBlocking {
79 | Mockito.`when`(services.getMovieDetail(movieId))
80 | .thenReturn(
81 | Response.error(401, ResponseBody.create(MediaType.parse("application/json"), ""))
82 | )
83 | val repo = repository.getMovieDetail(movieId)
84 | assert(repo.body() === null)
85 | }
86 |
87 | }
--------------------------------------------------------------------------------
/data/src/test/java/isfaaghyth/app/data/repository/tvshow/TVShowRepositoryTest.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.data.repository.tvshow
2 |
3 | import isfaaghyth.app.data.entity.TVShows
4 | import isfaaghyth.app.data.entity.TVShow
5 | import isfaaghyth.app.data.service.NetworkServices
6 | import kotlinx.coroutines.runBlocking
7 | import okhttp3.MediaType
8 | import okhttp3.ResponseBody
9 | import org.junit.After
10 | import org.junit.Assert
11 | import org.junit.Before
12 | import org.junit.Test
13 | import org.mockito.Mockito
14 | import retrofit2.Response
15 |
16 | class TVShowRepositoryTest {
17 |
18 | private var services = Mockito.mock(NetworkServices::class.java)
19 | private lateinit var repository: TVShowRepository
20 |
21 | private val tvshows = listOf(
22 | TVShow(
23 | "id",
24 | "movieId",
25 | "title",
26 | "posterPath",
27 | "overview",
28 | "backdrop",
29 | 0,
30 | 0f,
31 | "relateDate"
32 | )
33 | )
34 |
35 | @Before fun setUp() {
36 | repository = TVShowRepositoryImpl(services)
37 | }
38 |
39 | @Test fun `should get popular tv show success`() = runBlocking {
40 | Mockito.`when`(services.getPopularTVShow()).thenReturn(
41 | Response.success(TVShows(tvshows))
42 | )
43 | val repo = repository.getPopularTVShow()
44 |
45 | Assert.assertEquals(repo.body(), TVShows(tvshows))
46 | }
47 |
48 | @Test fun `should get null and error`() = runBlocking {
49 | Mockito.`when`(services.getPopularTVShow()).thenReturn(
50 | Response.error(401, ResponseBody.create(MediaType.parse("application/json"), ""))
51 | )
52 | val repo = repository.getPopularTVShow()
53 |
54 | Assert.assertEquals(repo.body(), null)
55 | }
56 | }
--------------------------------------------------------------------------------
/features/movie_details/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/features/movie_details/build.gradle:
--------------------------------------------------------------------------------
1 | apply from: "$rootDir/common.gradle"
2 |
3 | dependencies {
4 | //android support
5 | implementation Android.appCompat
6 | implementation Android.ktx
7 | implementation Android.constraintLayout
8 | implementation Android.cardView
9 |
10 | //network
11 | implementation Retrofit.retrofit
12 | implementation Retrofit.gsonConverter
13 | implementation Retrofit.okHttpLogging
14 | implementation Retrofit.coroutinesAdapter
15 |
16 | //jetpack
17 | implementation Jetpack.recyclerView
18 | implementation Jetpack.lifecycle
19 | kapt Jetpack.lifecycleCompiler
20 |
21 | //coroutines
22 | implementation Coroutines.core
23 | implementation Coroutines.android
24 |
25 | //modules
26 | implementation project(Modules.abstraction)
27 | implementation project(Modules.data)
28 | }
--------------------------------------------------------------------------------
/features/movie_details/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 |
--------------------------------------------------------------------------------
/features/movie_details/src/androidTest/java/isfaaghyth/app/movie_details/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movie_details;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("isfaaghyth.app.movie_details.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/features/movie_details/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/features/movie_details/src/main/java/isfaaghyth/app/movie_details/di/MovieDetailComponent.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movie_details.di
2 |
3 | import dagger.Component
4 | import isfaaghyth.app.movie_details.ui.MovieDetailActivity
5 | import isfaaghyth.app.movie_details.ui.MovieDetailModule
6 | import isfaaghyth.app.movie_details.ui.MovieDetailViewModelModule
7 |
8 | @MovieDetailScope
9 | @Component(modules = [
10 | MovieDetailModule::class,
11 | MovieDetailViewModelModule::class
12 | ])
13 | interface MovieDetailComponent {
14 | fun inject(activity: MovieDetailActivity)
15 | }
--------------------------------------------------------------------------------
/features/movie_details/src/main/java/isfaaghyth/app/movie_details/di/MovieDetailDeepLinkModule.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movie_details.di
2 |
3 | import com.airbnb.deeplinkdispatch.DeepLinkModule
4 |
5 | @DeepLinkModule
6 | class MovieDetailDeepLinkModule
--------------------------------------------------------------------------------
/features/movie_details/src/main/java/isfaaghyth/app/movie_details/di/MovieDetailScope.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movie_details.di
2 |
3 | import javax.inject.Scope
4 |
5 | @Scope annotation class MovieDetailScope
--------------------------------------------------------------------------------
/features/movie_details/src/main/java/isfaaghyth/app/movie_details/domain/MovieDetailUseCase.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movie_details.domain
2 |
3 | import isfaaghyth.app.abstraction.util.state.ResultState
4 | import isfaaghyth.app.abstraction.util.UNSUCCESSFUL_MESSAGE
5 | import isfaaghyth.app.abstraction.util.ext.fetchState
6 | import isfaaghyth.app.data.entity.Movie
7 | import isfaaghyth.app.data.entity.TVShow
8 | import isfaaghyth.app.data.repository.movie_detail.MovieDetailRepository
9 | import retrofit2.Response
10 | import javax.inject.Inject
11 |
12 | class MovieDetailUseCase @Inject constructor(val repository: MovieDetailRepository) {
13 |
14 | suspend fun getMovieDetail(movieId: String): ResultState {
15 | return fetchState {
16 | wrapperDetail {
17 | repository.getMovieDetail(movieId)
18 | }
19 | }
20 | }
21 |
22 | suspend fun getTvDetail(movieId: String): ResultState {
23 | return fetchState {
24 | wrapperDetail {
25 | repository.getTVShowDetail(movieId)
26 | }
27 | }
28 | }
29 |
30 | private suspend fun wrapperDetail(call: suspend () -> Response): ResultState {
31 | return if (call.invoke().isSuccessful) {
32 | ResultState.Success(call.invoke().body()!!)
33 | } else {
34 | ResultState.Error(UNSUCCESSFUL_MESSAGE)
35 | }
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/features/movie_details/src/main/java/isfaaghyth/app/movie_details/ui/MovieDetailActivity.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movie_details.ui
2 |
3 | import androidx.lifecycle.Observer
4 | import androidx.lifecycle.ViewModelProvider
5 | import androidx.lifecycle.ViewModelProviders
6 | import com.airbnb.deeplinkdispatch.DeepLink
7 | import isfaaghyth.app.abstraction.base.BaseActivity
8 | import isfaaghyth.app.abstraction.util.AppLink.MovieDetail.MOVIE_DETAIL
9 | import isfaaghyth.app.abstraction.util.AppLink.MovieDetail.PARAM_MOVIE_ID
10 | import isfaaghyth.app.abstraction.util.AppLink.MovieDetail.PARAM_TYPE
11 | import isfaaghyth.app.abstraction.util.ext.hide
12 | import isfaaghyth.app.abstraction.util.ext.load
13 | import isfaaghyth.app.abstraction.util.ext.show
14 | import isfaaghyth.app.abstraction.util.ext.toast
15 | import isfaaghyth.app.abstraction.util.state.LoaderState
16 | import isfaaghyth.app.movie_details.R
17 | import isfaaghyth.app.movie_details.di.DaggerMovieDetailComponent
18 | import kotlinx.android.synthetic.main.activity_movie_detail.*
19 | import javax.inject.Inject
20 |
21 | @DeepLink(MOVIE_DETAIL)
22 | class MovieDetailActivity: BaseActivity() {
23 |
24 | override fun contentView(): Int = R.layout.activity_movie_detail
25 |
26 | @Inject lateinit var viewModelFactory: ViewModelProvider.Factory
27 | private lateinit var viewModel: MovieDetailViewModel
28 |
29 | override fun initView() {
30 | viewModel = ViewModelProviders
31 | .of(this, viewModelFactory)
32 | .get(MovieDetailViewModel::class.java)
33 |
34 | initObserver()
35 | }
36 |
37 | private fun initObserver() {
38 | if (intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false)) {
39 | val parameters = intent.extras!!
40 | val movieId = parameters.getString(PARAM_MOVIE_ID)?: ""
41 |
42 | when(parameters.getString(PARAM_TYPE)) {
43 | TYPE_MOVIE -> viewModel.getMovieDetail(movieId)
44 | TYPE_TV -> viewModel.getTVShowDetail(movieId)
45 | else -> finish()
46 | }
47 |
48 | viewModel.state.observe(this, Observer {
49 | when (it) {
50 | is LoaderState.ShowLoading -> {
51 | rootView.hide()
52 | progressBar.show()
53 | }
54 | is LoaderState.HideLoading -> {
55 | rootView.show()
56 | progressBar.hide()
57 | }
58 | }
59 | })
60 |
61 | viewModel.movieDetail.observe(this, Observer { movie ->
62 | showDetail(
63 | movie.bannerUrl(),
64 | movie.posterUrl(),
65 | movie.title,
66 | movie.overview,
67 | movie.voteCount.toString(),
68 | movie.voteAverage.toString()
69 | )
70 | })
71 |
72 | viewModel.tvDetail.observe(this, Observer { tv ->
73 | showDetail(
74 | tv.bannerUrl(),
75 | tv.posterUrl(),
76 | tv.title,
77 | tv.overview,
78 | tv.voteCount.toString(),
79 | tv.voteAverage.toString()
80 | )
81 | })
82 |
83 | viewModel.error.observe(this, Observer { toast(it) })
84 | }
85 | }
86 |
87 | private fun showDetail(banner: String, poster: String,
88 | title: String, overview: String,
89 | voteCount: String, voteAverage: String) {
90 | imgBanner.load(banner)
91 | imgPoster.load(poster)
92 | txtMovieName.text = title
93 | txtContent.text = overview
94 | txtRating.text = voteCount
95 | txtVote.text = voteAverage
96 | }
97 |
98 | override fun initInjector() {
99 | DaggerMovieDetailComponent.builder()
100 | .movieDetailModule(MovieDetailModule())
101 | .build()
102 | .inject(this)
103 | }
104 |
105 | companion object {
106 | const val TYPE_MOVIE = "movie"
107 | const val TYPE_TV = "tv"
108 | }
109 |
110 | }
--------------------------------------------------------------------------------
/features/movie_details/src/main/java/isfaaghyth/app/movie_details/ui/MovieDetailModule.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movie_details.ui
2 |
3 | import dagger.Module
4 | import dagger.Provides
5 | import isfaaghyth.app.abstraction.util.thread.ApplicationSchedulerProvider
6 | import isfaaghyth.app.abstraction.util.thread.SchedulerProvider
7 | import isfaaghyth.app.data.di.DataModule
8 | import isfaaghyth.app.data.di.DataScope
9 | import isfaaghyth.app.data.repository.movie_detail.MovieDetailRepository
10 | import isfaaghyth.app.data.repository.movie_detail.MovieDetailRepositoryImpl
11 | import isfaaghyth.app.data.service.NetworkServices
12 | import isfaaghyth.app.movie_details.di.MovieDetailScope
13 | import isfaaghyth.app.movie_details.domain.MovieDetailUseCase
14 |
15 | @Module(includes = [DataModule::class])
16 | class MovieDetailModule {
17 |
18 | @MovieDetailScope @Provides
19 | fun provideRepository(@DataScope service: NetworkServices): MovieDetailRepository {
20 | return MovieDetailRepositoryImpl(service)
21 | }
22 |
23 | @MovieDetailScope @Provides
24 | fun provideUseCase(repository: MovieDetailRepository): MovieDetailUseCase {
25 | return MovieDetailUseCase(repository)
26 | }
27 |
28 | @MovieDetailScope @Provides
29 | fun provideSchedulerProvider(): SchedulerProvider = ApplicationSchedulerProvider()
30 |
31 | }
--------------------------------------------------------------------------------
/features/movie_details/src/main/java/isfaaghyth/app/movie_details/ui/MovieDetailViewModel.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movie_details.ui
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import isfaaghyth.app.abstraction.base.BaseViewModel
6 | import isfaaghyth.app.abstraction.helper.FetchingIdlingResource
7 | import isfaaghyth.app.abstraction.util.state.LoaderState
8 | import isfaaghyth.app.abstraction.util.state.ResultState
9 | import isfaaghyth.app.abstraction.util.thread.SchedulerProvider
10 | import isfaaghyth.app.data.entity.Movie
11 | import isfaaghyth.app.data.entity.TVShow
12 | import isfaaghyth.app.movie_details.domain.MovieDetailUseCase
13 | import kotlinx.coroutines.Dispatchers
14 | import kotlinx.coroutines.launch
15 | import kotlinx.coroutines.withContext
16 | import javax.inject.Inject
17 |
18 | interface MovieDetailContract {
19 | fun getMovieDetail(movieId: String)
20 | fun getTVShowDetail(movieId: String)
21 | }
22 |
23 | class MovieDetailViewModel @Inject constructor(
24 | private val useCase: MovieDetailUseCase,
25 | dispatcher: SchedulerProvider
26 | ): BaseViewModel(dispatcher), MovieDetailContract {
27 |
28 | private val _movieDetail = MutableLiveData()
29 | val movieDetail: LiveData
30 | get() = _movieDetail
31 |
32 | private val _tvDetail = MutableLiveData()
33 | val tvDetail: LiveData
34 | get() = _tvDetail
35 |
36 | private val _error = MutableLiveData()
37 | val error: LiveData
38 | get() = _error
39 |
40 | private val _state = MutableLiveData()
41 | val state: LiveData
42 | get() = _state
43 |
44 | override fun getMovieDetail(movieId: String) {
45 | FetchingIdlingResource.begin()
46 | _state.value = LoaderState.ShowLoading
47 | launch {
48 | val result = useCase.getMovieDetail(movieId)
49 | withContext(Dispatchers.Main) {
50 | FetchingIdlingResource.complete()
51 | _state.value = LoaderState.HideLoading
52 | when (result) {
53 | is ResultState.Success -> _movieDetail.postValue(result.data)
54 | is ResultState.Error -> _error.postValue(result.error)
55 | }
56 | }
57 | }
58 | }
59 |
60 | override fun getTVShowDetail(movieId: String) {
61 | FetchingIdlingResource.begin()
62 | _state.value = LoaderState.ShowLoading
63 | launch {
64 | val result = useCase.getTvDetail(movieId)
65 | withContext(Dispatchers.Main) {
66 | FetchingIdlingResource.complete()
67 | _state.value = LoaderState.HideLoading
68 | when (result) {
69 | is ResultState.Success -> _tvDetail.postValue(result.data)
70 | is ResultState.Error -> _error.postValue(result.error)
71 | }
72 | }
73 | }
74 | }
75 |
76 | }
--------------------------------------------------------------------------------
/features/movie_details/src/main/java/isfaaghyth/app/movie_details/ui/MovieDetailViewModelModule.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movie_details.ui
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import dagger.Binds
6 | import dagger.Module
7 | import dagger.multibindings.IntoMap
8 | import isfaaghyth.app.abstraction.util.viewmodel.ViewModelFactory
9 | import isfaaghyth.app.abstraction.util.viewmodel.ViewModelKey
10 | import isfaaghyth.app.movie_details.di.MovieDetailScope
11 |
12 | @Module abstract class MovieDetailViewModelModule {
13 |
14 | @MovieDetailScope
15 | @Binds
16 | internal abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory
17 |
18 | @MovieDetailScope
19 | @Binds
20 | @IntoMap
21 | @ViewModelKey(MovieDetailViewModel::class)
22 | internal abstract fun bindMovieViewModel(viewModel: MovieDetailViewModel): ViewModel
23 |
24 | }
--------------------------------------------------------------------------------
/features/movie_details/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/features/movie_details/src/test/java/isfaaghyth/app/movie_details/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movie_details;
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 | }
--------------------------------------------------------------------------------
/features/movie_details/src/test/java/isfaaghyth/app/movie_details/domain/MovieDetailUseCaseTest.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movie_details.domain
2 |
3 | import isfaaghyth.app.abstraction.util.state.ResultState
4 | import isfaaghyth.app.data.entity.Movie
5 | import isfaaghyth.app.data.entity.TVShow
6 | import isfaaghyth.app.data.repository.movie_detail.MovieDetailRepository
7 | import kotlinx.coroutines.ExperimentalCoroutinesApi
8 | import kotlinx.coroutines.runBlocking
9 | import org.junit.Before
10 | import org.junit.Test
11 | import org.mockito.Mockito.`when`
12 | import org.mockito.Mockito.mock
13 | import retrofit2.Response
14 |
15 | @ExperimentalCoroutinesApi
16 | class MovieDetailUseCaseTest {
17 |
18 | private var repository = mock(MovieDetailRepository::class.java)
19 | private lateinit var useCase: MovieDetailUseCase
20 |
21 | @Before fun setUp() {
22 | useCase = MovieDetailUseCase(repository)
23 | }
24 |
25 | @Test fun `should show movie detail response`() {
26 | val movieId = "60735"
27 | val returnValue = ResultState.Success(movie)
28 | val request = runBlocking {
29 | `when`(repository.getMovieDetail(movieId)).thenReturn(
30 | Response.success(movie)
31 | )
32 | useCase.getMovieDetail(movieId)
33 | }
34 | assert(returnValue == request)
35 | }
36 |
37 | @Test fun `should show tv detail response`() {
38 | val movieId = "60735"
39 | val returnValue = ResultState.Success(tvshow)
40 | val request = runBlocking {
41 | `when`(repository.getTVShowDetail(movieId)).thenReturn(
42 | Response.success(tvshow)
43 | )
44 | useCase.getTvDetail(movieId)
45 | }
46 | assert(returnValue == request)
47 | }
48 |
49 | companion object {
50 | private val movie = Movie(
51 | "id",
52 | "movieId",
53 | "title",
54 | "posterPath",
55 | "overview",
56 | "backdrop",
57 | 0,
58 | 0f,
59 | "relateDate"
60 | )
61 |
62 | private val tvshow = TVShow(
63 | "id",
64 | "movieId",
65 | "title",
66 | "posterPath",
67 | "overview",
68 | "backdrop",
69 | 0,
70 | 0f,
71 | "relateDate"
72 | )
73 | }
74 |
75 | }
--------------------------------------------------------------------------------
/features/movies/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/features/movies/build.gradle:
--------------------------------------------------------------------------------
1 | apply from: "$rootDir/common.gradle"
2 |
3 | dependencies {
4 | //android support
5 | implementation Android.appCompat
6 | implementation Android.ktx
7 | implementation Android.constraintLayout
8 |
9 | //network
10 | implementation Retrofit.retrofit
11 | implementation Retrofit.gsonConverter
12 | implementation Retrofit.okHttpLogging
13 | implementation Retrofit.coroutinesAdapter
14 |
15 | //coroutines
16 | implementation Coroutines.core
17 | implementation Coroutines.android
18 |
19 | //jetpack
20 | implementation Jetpack.recyclerView
21 | implementation Jetpack.lifecycle
22 | kapt Jetpack.lifecycleCompiler
23 |
24 | //modules
25 | implementation project(Modules.abstraction)
26 | implementation project(Modules.network)
27 | implementation project(Modules.data)
28 |
29 | //movie_detail features (easily purpose, just pass it with parcelable)
30 | implementation project(Modules.movieDetail)
31 | }
--------------------------------------------------------------------------------
/features/movies/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 |
--------------------------------------------------------------------------------
/features/movies/src/androidTest/java/isfaaghyth/app/movies/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movies;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("isfaaghyth.app.movies.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/features/movies/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/features/movies/src/main/java/isfaaghyth/app/movies/di/MovieComponent.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movies.di
2 |
3 | import dagger.Component
4 | import isfaaghyth.app.movies.ui.MovieFragment
5 | import isfaaghyth.app.movies.ui.MovieModule
6 | import isfaaghyth.app.movies.ui.MovieViewModelModule
7 |
8 | @MovieScope
9 | @Component(modules = [
10 | MovieModule::class,
11 | MovieViewModelModule::class
12 | ])
13 | interface MovieComponent {
14 | fun inject(fragment: MovieFragment)
15 | }
--------------------------------------------------------------------------------
/features/movies/src/main/java/isfaaghyth/app/movies/di/MovieScope.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movies.di
2 |
3 | import javax.inject.Scope
4 |
5 | @Scope annotation class MovieScope
--------------------------------------------------------------------------------
/features/movies/src/main/java/isfaaghyth/app/movies/domain/MovieUseCase.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movies.domain
2 |
3 | import isfaaghyth.app.abstraction.util.state.ResultState
4 | import isfaaghyth.app.abstraction.util.UNSUCCESSFUL_MESSAGE
5 | import isfaaghyth.app.abstraction.util.ext.fetchState
6 | import isfaaghyth.app.data.entity.Movies
7 | import isfaaghyth.app.data.repository.movie.MovieRepository
8 | import javax.inject.Inject
9 |
10 | class MovieUseCase @Inject constructor(private val repository: MovieRepository) {
11 |
12 | suspend fun getPopularMovie(): ResultState {
13 | return fetchState {
14 | val response = repository.getPopularMovie()
15 | if (response.isSuccessful) {
16 | ResultState.Success(response.body()!!)
17 | } else {
18 | ResultState.Error(UNSUCCESSFUL_MESSAGE)
19 | }
20 | }
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/features/movies/src/main/java/isfaaghyth/app/movies/ui/MovieAdapter.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movies.ui
2 |
3 | import android.content.Intent
4 | import android.net.Uri
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.recyclerview.widget.RecyclerView
9 | import isfaaghyth.app.abstraction.util.ext.load
10 | import isfaaghyth.app.data.entity.Movie
11 | import isfaaghyth.app.movies.R
12 | import kotlinx.android.synthetic.main.item_movie.view.*
13 |
14 | class MovieAdapter(private val movies: List): RecyclerView.Adapter() {
15 |
16 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
17 | return Holder.inflate(parent)
18 | }
19 |
20 | override fun getItemCount(): Int = movies.size
21 |
22 | override fun onBindViewHolder(holder: Holder, position: Int) {
23 | holder.bind(movies[position])
24 | }
25 |
26 | class Holder(private val view: View): RecyclerView.ViewHolder(view) {
27 | private val title = view.txt_movie_name
28 | private val cardItem = view.card_movie
29 | private val poster = view.img_poster
30 | private val year = view.txt_year
31 |
32 | companion object {
33 | fun inflate(parent: ViewGroup): Holder {
34 | return Holder(
35 | LayoutInflater.from(parent.context).inflate(
36 | R.layout.item_movie,
37 | parent,
38 | false)
39 | )
40 | }
41 | }
42 |
43 | fun bind(movie: Movie) {
44 | title.text = movie.title
45 | year.text = movie.releaseDate
46 | poster.load(movie.bannerUrl())
47 | cardItem.setOnClickListener {
48 | val context = view.context
49 | context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("jetmovie://detail/movie/${movie.id}")))
50 | }
51 | }
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/features/movies/src/main/java/isfaaghyth/app/movies/ui/MovieFragment.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movies.ui
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import androidx.lifecycle.Observer
9 | import androidx.lifecycle.ViewModelProvider
10 | import androidx.lifecycle.ViewModelProviders
11 | import isfaaghyth.app.abstraction.util.ext.toast
12 | import isfaaghyth.app.abstraction.util.state.LoaderState
13 | import isfaaghyth.app.data.entity.Movie
14 | import isfaaghyth.app.movies.R
15 | import isfaaghyth.app.movies.di.DaggerMovieComponent
16 | import kotlinx.android.synthetic.main.fragment_movie.*
17 | import javax.inject.Inject
18 |
19 | class MovieFragment: Fragment() {
20 |
21 | private fun contentView(): Int = R.layout.fragment_movie
22 |
23 | @Inject lateinit var viewModelFactory: ViewModelProvider.Factory
24 | private lateinit var viewModel: MovieViewModel
25 |
26 | private var movieData = arrayListOf()
27 | private val adapter: MovieAdapter by lazy {
28 | MovieAdapter(movieData)
29 | }
30 |
31 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
32 | return inflater.inflate(contentView(), container, false)
33 | }
34 |
35 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
36 | super.onViewCreated(view, savedInstanceState)
37 | initInjector()
38 | initView()
39 | }
40 |
41 | private fun initView() {
42 | viewModel = ViewModelProviders
43 | .of(this, viewModelFactory)
44 | .get(MovieViewModel::class.java)
45 |
46 | lstMovies.adapter = adapter
47 |
48 | initObserver()
49 | }
50 |
51 | private fun initObserver() {
52 | viewModel.state.observe(this, Observer { state ->
53 | when (state) {
54 | is LoaderState.ShowLoading -> toast("loading")
55 | is LoaderState.HideLoading -> toast("complete")
56 | }
57 | })
58 |
59 | viewModel.result.observe(this, Observer { movies ->
60 | movieData.addAll(movies)
61 | adapter.notifyDataSetChanged()
62 | })
63 |
64 | viewModel.error.observe(this, Observer { error ->
65 | toast(error)
66 | })
67 | }
68 |
69 | private fun initInjector() {
70 | DaggerMovieComponent.builder()
71 | .movieModule(MovieModule())
72 | .build()
73 | .inject(this)
74 | }
75 |
76 | override fun onDestroy() {
77 | super.onDestroy()
78 | viewModel.clear()
79 | }
80 |
81 | }
--------------------------------------------------------------------------------
/features/movies/src/main/java/isfaaghyth/app/movies/ui/MovieModule.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movies.ui
2 |
3 | import dagger.Module
4 | import dagger.Provides
5 | import isfaaghyth.app.abstraction.util.thread.ApplicationSchedulerProvider
6 | import isfaaghyth.app.abstraction.util.thread.SchedulerProvider
7 | import isfaaghyth.app.data.di.DataModule
8 | import isfaaghyth.app.data.di.DataScope
9 | import isfaaghyth.app.data.repository.movie.MovieRepository
10 | import isfaaghyth.app.data.repository.movie.MovieRepositoryImpl
11 | import isfaaghyth.app.data.service.NetworkServices
12 | import isfaaghyth.app.movies.di.MovieScope
13 | import isfaaghyth.app.movies.domain.MovieUseCase
14 |
15 | @Module(includes = [DataModule::class])
16 | class MovieModule {
17 |
18 | @MovieScope @Provides
19 | fun provideRepository(@DataScope service: NetworkServices): MovieRepository {
20 | return MovieRepositoryImpl(service)
21 | }
22 |
23 | @MovieScope @Provides
24 | fun provideUseCase(repository: MovieRepository): MovieUseCase {
25 | return MovieUseCase(repository)
26 | }
27 |
28 | @MovieScope @Provides
29 | fun provideSchedulerProvider(): SchedulerProvider = ApplicationSchedulerProvider()
30 |
31 | }
--------------------------------------------------------------------------------
/features/movies/src/main/java/isfaaghyth/app/movies/ui/MovieViewModel.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movies.ui
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import isfaaghyth.app.abstraction.base.BaseViewModel
6 | import isfaaghyth.app.abstraction.helper.FetchingIdlingResource
7 | import isfaaghyth.app.abstraction.util.state.LoaderState
8 | import isfaaghyth.app.abstraction.util.state.ResultState
9 | import isfaaghyth.app.abstraction.util.thread.SchedulerProvider
10 | import isfaaghyth.app.data.entity.Movie
11 | import isfaaghyth.app.movies.domain.MovieUseCase
12 | import kotlinx.coroutines.Dispatchers
13 | import kotlinx.coroutines.launch
14 | import kotlinx.coroutines.withContext
15 | import javax.inject.Inject
16 |
17 | interface MovieContract {
18 | fun getPopularMovie()
19 | }
20 |
21 | class MovieViewModel @Inject constructor(
22 | private val useCase: MovieUseCase,
23 | dispatcher: SchedulerProvider
24 | ): BaseViewModel(dispatcher), MovieContract {
25 |
26 | private val _state = MutableLiveData()
27 | val state: LiveData
28 | get() = _state
29 |
30 | private val _result = MutableLiveData>()
31 | val result: LiveData>
32 | get() = _result
33 |
34 | private val _error = MutableLiveData()
35 | val error: LiveData
36 | get() = _error
37 |
38 | init {
39 | getPopularMovie()
40 | }
41 |
42 | override fun getPopularMovie() {
43 | FetchingIdlingResource.begin()
44 | _state.value = LoaderState.ShowLoading
45 | launch(coroutineContext) {
46 | val result = useCase.getPopularMovie()
47 | withContext(Dispatchers.Main) {
48 | FetchingIdlingResource.complete()
49 | _state.value = LoaderState.HideLoading
50 | when (result) {
51 | is ResultState.Success -> _result.postValue(result.data.resultsIntent)
52 | is ResultState.Error -> _error.postValue(result.error)
53 | }
54 | }
55 | }
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/features/movies/src/main/java/isfaaghyth/app/movies/ui/MovieViewModelModule.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movies.ui
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import dagger.Binds
6 | import dagger.Module
7 | import dagger.multibindings.IntoMap
8 | import isfaaghyth.app.abstraction.util.viewmodel.ViewModelFactory
9 | import isfaaghyth.app.abstraction.util.viewmodel.ViewModelKey
10 | import isfaaghyth.app.movies.di.MovieScope
11 |
12 | @Module abstract class MovieViewModelModule {
13 |
14 | @MovieScope
15 | @Binds
16 | internal abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory
17 |
18 | @Binds
19 | @IntoMap
20 | @ViewModelKey(MovieViewModel::class)
21 | internal abstract fun bindMovieViewModel(viewModel: MovieViewModel): ViewModel
22 |
23 | }
--------------------------------------------------------------------------------
/features/movies/src/main/res/layout/fragment_movie.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
--------------------------------------------------------------------------------
/features/movies/src/main/res/layout/item_movie.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
19 |
20 |
27 |
28 |
33 |
34 |
39 |
40 |
52 |
53 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/features/movies/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | movies
3 |
4 |
--------------------------------------------------------------------------------
/features/movies/src/test/java/isfaaghyth/app/movies/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movies;
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 | }
--------------------------------------------------------------------------------
/features/movies/src/test/java/isfaaghyth/app/movies/domain/MovieUseCaseTest.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movies.domain
2 |
3 | import isfaaghyth.app.abstraction.util.state.ResultState
4 | import isfaaghyth.app.data.entity.Movie
5 | import isfaaghyth.app.data.entity.Movies
6 | import isfaaghyth.app.data.repository.movie.MovieRepository
7 | import kotlinx.coroutines.runBlocking
8 | import okhttp3.MediaType
9 | import okhttp3.ResponseBody
10 | import org.junit.Before
11 | import org.junit.Test
12 | import org.mockito.Mockito.`when`
13 | import org.mockito.Mockito.mock
14 | import retrofit2.Response
15 |
16 | class MovieUseCaseTest {
17 |
18 | private var repository = mock(MovieRepository::class.java)
19 | private lateinit var useCase: MovieUseCase
20 |
21 | private val movies = listOf(
22 | Movie(
23 | "id",
24 | "movieId",
25 | "title",
26 | "posterPath",
27 | "overview",
28 | "backdrop",
29 | 0,
30 | 0f,
31 | "relateDate"
32 | )
33 | )
34 |
35 | @Before fun setUp() {
36 | useCase = MovieUseCase(repository)
37 | }
38 |
39 | @Test fun `should return success`() {
40 | val actual = ResultState.Success(Movies(movies))
41 | val result = runBlocking {
42 | `when`(repository.getPopularMovie())
43 | .thenReturn(Response.success(Movies(movies)))
44 | useCase.getPopularMovie()
45 | }
46 | assert(result == actual)
47 | }
48 |
49 | @Test fun `should return error`() {
50 | val actual = ResultState.Error("")
51 | val result = runBlocking {
52 | `when`(repository.getPopularMovie())
53 | .thenReturn(
54 | Response.error(401, ResponseBody.create(MediaType.parse("application/json"), ""))
55 | )
56 | useCase.getPopularMovie()
57 | }
58 |
59 | //probably has different error message, so you can check by type of java class
60 | assert(result.javaClass === actual.javaClass)
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/features/movies/src/test/java/isfaaghyth/app/movies/ui/MovieViewModelTest.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.movies.ui
2 |
3 | import androidx.arch.core.executor.testing.InstantTaskExecutorRule
4 | import androidx.lifecycle.Observer
5 | import isfaaghyth.app.abstraction.util.state.ResultState
6 | import isfaaghyth.app.abstraction.util.thread.TestSchedulerProvider
7 | import isfaaghyth.app.data.entity.Movie
8 | import isfaaghyth.app.data.entity.Movies
9 | import isfaaghyth.app.movies.domain.MovieUseCase
10 | import junit.framework.Assert.assertEquals
11 | import kotlinx.coroutines.Dispatchers
12 | import kotlinx.coroutines.ExperimentalCoroutinesApi
13 | import kotlinx.coroutines.runBlocking
14 | import kotlinx.coroutines.test.resetMain
15 | import kotlinx.coroutines.test.setMain
16 | import org.junit.*
17 | import org.mockito.ArgumentCaptor
18 | import org.mockito.Captor
19 | import org.mockito.Mock
20 | import org.mockito.Mockito.*
21 | import org.mockito.MockitoAnnotations
22 |
23 | @ExperimentalCoroutinesApi
24 | class MovieViewModelTest {
25 |
26 | @get:Rule val instantExecutorRule = InstantTaskExecutorRule()
27 |
28 | @Mock lateinit var result: Observer>
29 | @Mock lateinit var error: Observer
30 |
31 | @Captor lateinit var argResultCaptor: ArgumentCaptor>
32 | @Captor lateinit var argErrorCaptor: ArgumentCaptor
33 |
34 | @Mock lateinit var useCase: MovieUseCase
35 |
36 | private lateinit var viewModel: MovieViewModel
37 |
38 | private val movies = listOf(
39 | Movie(
40 | "id",
41 | "movieId",
42 | "title",
43 | "posterPath",
44 | "overview",
45 | "backdrop",
46 | 0,
47 | 0f,
48 | "relateDate"
49 | )
50 | )
51 |
52 | private val moviesData = Movies(movies)
53 | private val schedulerProvider = TestSchedulerProvider()
54 |
55 | @Before fun setUp() {
56 | MockitoAnnotations.initMocks(this)
57 | Dispatchers.setMain(schedulerProvider.ui())
58 |
59 | viewModel = MovieViewModel(useCase, schedulerProvider)
60 | viewModel.result.observeForever(result)
61 | viewModel.error.observeForever(error)
62 | }
63 |
64 | @Test fun `should return a response of movies data`() = runBlocking {
65 | val returnValue = ResultState.Success(moviesData)
66 | `when`(useCase.getPopularMovie()).thenReturn(returnValue)
67 | viewModel.getPopularMovie()
68 | verify(result, atLeastOnce()).onChanged(argResultCaptor.capture())
69 | assertEquals(returnValue.data.resultsIntent, argResultCaptor.allValues.first())
70 | clearInvocations(useCase, result)
71 | }
72 |
73 | @Test fun `should return an error without api key`() = runBlocking {
74 | val returnValue = ResultState.Error("API Key Not Found")
75 | `when`(useCase.getPopularMovie()).thenReturn(returnValue)
76 | viewModel.getPopularMovie()
77 | verify(error, atLeastOnce()).onChanged(argErrorCaptor.capture())
78 | assertEquals(returnValue.error, argErrorCaptor.allValues.first())
79 | clearInvocations(useCase, error)
80 | }
81 |
82 | @After fun tearDown() {
83 | Dispatchers.resetMain()
84 | }
85 | }
--------------------------------------------------------------------------------
/features/tvshows/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/features/tvshows/build.gradle:
--------------------------------------------------------------------------------
1 | apply from: "$rootDir/common.gradle"
2 |
3 | dependencies {
4 | //android support
5 | implementation Android.appCompat
6 | implementation Android.ktx
7 | implementation Android.constraintLayout
8 |
9 | //network
10 | implementation Retrofit.retrofit
11 | implementation Retrofit.gsonConverter
12 | implementation Retrofit.okHttpLogging
13 | implementation Retrofit.coroutinesAdapter
14 |
15 | //coroutines
16 | implementation Coroutines.core
17 | implementation Coroutines.android
18 |
19 | //jetpack
20 | implementation Jetpack.recyclerView
21 | implementation Jetpack.lifecycle
22 | kapt Jetpack.lifecycleCompiler
23 |
24 | //coroutines
25 | implementation Coroutines.core
26 | implementation Coroutines.android
27 |
28 | //modules
29 | implementation project(Modules.abstraction)
30 | implementation project(Modules.network)
31 | implementation project(Modules.data)
32 |
33 | //movie_detail features (easily purpose, just pass it with parcelable)
34 | implementation project(Modules.movieDetail)
35 | }
--------------------------------------------------------------------------------
/features/tvshows/build/generated/source/buildConfig/debug/isfaaghyth/app/tvshows/BuildConfig.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Automatically generated file. DO NOT MODIFY
3 | */
4 | package isfaaghyth.app.tvshows;
5 |
6 | public final class BuildConfig {
7 | public static final boolean DEBUG = Boolean.parseBoolean("true");
8 | public static final String APPLICATION_ID = "isfaaghyth.app.tvshows";
9 | public static final String BUILD_TYPE = "debug";
10 | public static final String FLAVOR = "";
11 | public static final int VERSION_CODE = 1;
12 | public static final String VERSION_NAME = "1.0";
13 | // Fields from default config.
14 | public static final String API_KEY = "9351b653885866a95fcef04c4f0c7426";
15 | public static final String IMAGE_URL = "https://image.tmdb.org/t/p/w500";
16 | public static final String MOVIE_URL = "https://api.themoviedb.org/3/";
17 | }
18 |
--------------------------------------------------------------------------------
/features/tvshows/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 |
--------------------------------------------------------------------------------
/features/tvshows/src/androidTest/java/isfaaghyth/app/tvshows/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.tvshows;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("isfaaghyth.app.tvshows.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/features/tvshows/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/features/tvshows/src/main/java/isfaaghyth/app/tvshows/di/TVShowComponent.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.tvshows.di
2 |
3 | import dagger.Component
4 | import isfaaghyth.app.tvshows.ui.TVShowFragment
5 | import isfaaghyth.app.tvshows.ui.TVShowModule
6 | import isfaaghyth.app.tvshows.ui.TVShowViewModelModule
7 |
8 | @TVShowScope
9 | @Component(modules = [
10 | TVShowModule::class,
11 | TVShowViewModelModule::class
12 | ])
13 | interface TVShowComponent {
14 | fun inject(activity: TVShowFragment)
15 | }
--------------------------------------------------------------------------------
/features/tvshows/src/main/java/isfaaghyth/app/tvshows/di/TVShowScope.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.tvshows.di
2 |
3 | annotation class TVShowScope
--------------------------------------------------------------------------------
/features/tvshows/src/main/java/isfaaghyth/app/tvshows/domain/TVShowUseCase.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.tvshows.domain
2 |
3 | import isfaaghyth.app.abstraction.util.state.ResultState
4 | import isfaaghyth.app.abstraction.util.UNSUCCESSFUL_MESSAGE
5 | import isfaaghyth.app.abstraction.util.ext.fetchState
6 | import isfaaghyth.app.data.entity.TVShows
7 | import isfaaghyth.app.data.repository.tvshow.TVShowRepository
8 | import javax.inject.Inject
9 |
10 | class TVShowUseCase @Inject constructor(private val repository: TVShowRepository) {
11 |
12 | suspend fun getPopularTvShow(): ResultState {
13 | return fetchState {
14 | val response = repository.getPopularTVShow()
15 | if (response.isSuccessful) {
16 | ResultState.Success(response.body()!!)
17 | } else {
18 | ResultState.Error(UNSUCCESSFUL_MESSAGE)
19 | }
20 | }
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/features/tvshows/src/main/java/isfaaghyth/app/tvshows/ui/TVShowAdapter.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.tvshows.ui
2 |
3 | import android.content.Intent
4 | import android.net.Uri
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.recyclerview.widget.RecyclerView
9 | import isfaaghyth.app.abstraction.util.ext.load
10 | import isfaaghyth.app.data.entity.TVShow
11 | import isfaaghyth.app.tvshows.R
12 | import kotlinx.android.synthetic.main.item_tvshow.view.*
13 |
14 | class TVShowAdapter(private val tvs: List): RecyclerView.Adapter() {
15 |
16 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
17 | return Holder.inflate(parent)
18 | }
19 |
20 | override fun getItemCount(): Int = tvs.size
21 |
22 | override fun onBindViewHolder(holder: Holder, position: Int) {
23 | holder.bind(tvs[position])
24 | }
25 |
26 | class Holder(private val view: View): RecyclerView.ViewHolder(view) {
27 | private val title = view.txt_movie_name
28 | private val cardItem = view.card_movie
29 | private val poster = view.img_poster
30 | private val year = view.txt_year
31 |
32 | companion object {
33 | fun inflate(parent: ViewGroup): Holder {
34 | return Holder(
35 | LayoutInflater.from(parent.context).inflate(
36 | R.layout.item_tvshow,
37 | parent,
38 | false)
39 | )
40 | }
41 | }
42 |
43 | fun bind(tvshow: TVShow) {
44 | title.text = tvshow.title
45 | year.text = tvshow.releaseDate
46 | poster.load(tvshow.bannerUrl())
47 | cardItem.setOnClickListener {
48 | val context = view.context
49 | context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("jetmovie://detail/tv/${tvshow.id}")))
50 | }
51 | }
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/features/tvshows/src/main/java/isfaaghyth/app/tvshows/ui/TVShowFragment.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.tvshows.ui
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import androidx.lifecycle.Observer
9 | import androidx.lifecycle.ViewModelProvider
10 | import androidx.lifecycle.ViewModelProviders
11 | import isfaaghyth.app.abstraction.util.ext.toast
12 | import isfaaghyth.app.abstraction.util.state.LoaderState
13 | import isfaaghyth.app.data.entity.TVShow
14 | import isfaaghyth.app.tvshows.R
15 | import isfaaghyth.app.tvshows.di.DaggerTVShowComponent
16 | import kotlinx.android.synthetic.main.fragment_tvshow.*
17 | import javax.inject.Inject
18 |
19 | class TVShowFragment: Fragment() {
20 |
21 | private fun contentView(): Int = R.layout.fragment_tvshow
22 |
23 | @Inject
24 | lateinit var viewModelFactory: ViewModelProvider.Factory
25 | private lateinit var viewModel: TVShowViewModel
26 |
27 | private var tvshowData = arrayListOf()
28 | private val adapter: TVShowAdapter by lazy {
29 | TVShowAdapter(tvshowData)
30 | }
31 |
32 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
33 | return inflater.inflate(contentView(), container, false)
34 | }
35 |
36 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
37 | super.onViewCreated(view, savedInstanceState)
38 | initInjector()
39 | initView()
40 | }
41 |
42 | private fun initView() {
43 | viewModel = ViewModelProviders
44 | .of(this, viewModelFactory)
45 | .get(TVShowViewModel::class.java)
46 |
47 | lstTvShows.adapter = adapter
48 |
49 | initObserver()
50 | }
51 |
52 | private fun initObserver() {
53 | viewModel.state.observe(this, Observer { state ->
54 | when (state) {
55 | is LoaderState.ShowLoading -> toast("loading")
56 | is LoaderState.HideLoading -> toast("complete")
57 | }
58 | })
59 |
60 | viewModel.result.observe(this, Observer { tvshows ->
61 | tvshowData.addAll(tvshows)
62 | adapter.notifyDataSetChanged()
63 | })
64 |
65 | viewModel.error.observe(this, Observer { error ->
66 | toast(error)
67 | })
68 | }
69 |
70 | private fun initInjector() {
71 | DaggerTVShowComponent.builder()
72 | .tVShowModule(TVShowModule())
73 | .build()
74 | .inject(this)
75 | }
76 |
77 | override fun onDestroyView() {
78 | super.onDestroyView()
79 | viewModel.clear()
80 | }
81 |
82 | }
--------------------------------------------------------------------------------
/features/tvshows/src/main/java/isfaaghyth/app/tvshows/ui/TVShowModule.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.tvshows.ui
2 |
3 | import dagger.Module
4 | import dagger.Provides
5 | import isfaaghyth.app.abstraction.util.thread.ApplicationSchedulerProvider
6 | import isfaaghyth.app.abstraction.util.thread.SchedulerProvider
7 | import isfaaghyth.app.data.di.DataModule
8 | import isfaaghyth.app.data.di.DataScope
9 | import isfaaghyth.app.data.repository.tvshow.TVShowRepository
10 | import isfaaghyth.app.data.repository.tvshow.TVShowRepositoryImpl
11 | import isfaaghyth.app.data.service.NetworkServices
12 | import isfaaghyth.app.tvshows.di.TVShowScope
13 | import isfaaghyth.app.tvshows.domain.TVShowUseCase
14 |
15 | @Module(includes = [DataModule::class])
16 | class TVShowModule {
17 |
18 | @TVShowScope @Provides
19 | fun provideRepository(@DataScope service: NetworkServices): TVShowRepository {
20 | return TVShowRepositoryImpl(service)
21 | }
22 |
23 | @TVShowScope @Provides
24 | fun provideUseCase(repository: TVShowRepository): TVShowUseCase {
25 | return TVShowUseCase(repository)
26 | }
27 |
28 | @TVShowScope @Provides
29 | fun provideSchedulerProvider(): SchedulerProvider = ApplicationSchedulerProvider()
30 |
31 | }
--------------------------------------------------------------------------------
/features/tvshows/src/main/java/isfaaghyth/app/tvshows/ui/TVShowViewModel.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.tvshows.ui
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import isfaaghyth.app.abstraction.base.BaseViewModel
6 | import isfaaghyth.app.abstraction.helper.FetchingIdlingResource
7 | import isfaaghyth.app.abstraction.util.state.LoaderState
8 | import isfaaghyth.app.abstraction.util.state.ResultState
9 | import isfaaghyth.app.abstraction.util.thread.SchedulerProvider
10 | import isfaaghyth.app.data.entity.TVShow
11 | import isfaaghyth.app.tvshows.domain.TVShowUseCase
12 | import kotlinx.coroutines.Dispatchers
13 | import kotlinx.coroutines.launch
14 | import kotlinx.coroutines.withContext
15 | import javax.inject.Inject
16 |
17 | interface TVShowContract {
18 | fun getPopularTvShow()
19 | }
20 |
21 | class TVShowViewModel @Inject constructor(
22 | private val useCase: TVShowUseCase,
23 | dispatcher: SchedulerProvider
24 | ): BaseViewModel(dispatcher), TVShowContract {
25 |
26 | private val _state = MutableLiveData()
27 | val state: LiveData
28 | get() = _state
29 |
30 | private val _result = MutableLiveData>()
31 | val result: LiveData>
32 | get() = _result
33 |
34 | private val _error = MutableLiveData()
35 | val error: LiveData
36 | get() = _error
37 |
38 | init {
39 | getPopularTvShow()
40 | }
41 |
42 | override fun getPopularTvShow() {
43 | FetchingIdlingResource.begin()
44 | _state.value = LoaderState.ShowLoading
45 | launch {
46 | val result = useCase.getPopularTvShow()
47 | withContext(Dispatchers.Main) {
48 | FetchingIdlingResource.complete()
49 | _state.value = LoaderState.HideLoading
50 | when (result) {
51 | is ResultState.Success -> _result.postValue(result.data.resultsIntent)
52 | is ResultState.Error -> _error.postValue(result.error)
53 | }
54 | }
55 | }
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/features/tvshows/src/main/java/isfaaghyth/app/tvshows/ui/TVShowViewModelModule.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.tvshows.ui
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import dagger.Binds
6 | import dagger.Module
7 | import dagger.multibindings.IntoMap
8 | import isfaaghyth.app.abstraction.util.viewmodel.ViewModelFactory
9 | import isfaaghyth.app.abstraction.util.viewmodel.ViewModelKey
10 | import isfaaghyth.app.tvshows.di.TVShowScope
11 |
12 | @Module abstract class TVShowViewModelModule {
13 |
14 | @TVShowScope
15 | @Binds
16 | internal abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory
17 |
18 | @Binds
19 | @IntoMap
20 | @ViewModelKey(TVShowViewModel::class)
21 | internal abstract fun bindMovieViewModel(viewModel: TVShowViewModel): ViewModel
22 |
23 | }
--------------------------------------------------------------------------------
/features/tvshows/src/main/res/layout/fragment_tvshow.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
--------------------------------------------------------------------------------
/features/tvshows/src/main/res/layout/item_tvshow.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
19 |
20 |
27 |
28 |
33 |
34 |
39 |
40 |
52 |
53 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/features/tvshows/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | tvshows
3 |
4 |
--------------------------------------------------------------------------------
/features/tvshows/src/test/java/isfaaghyth/app/tvshows/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.tvshows;
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 | }
--------------------------------------------------------------------------------
/features/tvshows/src/test/java/isfaaghyth/app/tvshows/domain/TVShowUseCaseTest.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.tvshows.domain
2 |
3 | import isfaaghyth.app.abstraction.util.state.ResultState
4 | import isfaaghyth.app.data.entity.TVShow
5 | import isfaaghyth.app.data.entity.TVShows
6 | import isfaaghyth.app.data.repository.tvshow.TVShowRepository
7 | import kotlinx.coroutines.runBlocking
8 | import okhttp3.MediaType
9 | import okhttp3.ResponseBody
10 | import org.junit.Before
11 | import org.junit.Test
12 | import org.mockito.Mockito.`when`
13 | import org.mockito.Mockito.mock
14 | import retrofit2.Response
15 |
16 | class TVShowUseCaseTest {
17 |
18 | private var repository = mock(TVShowRepository::class.java)
19 | private lateinit var usecase: TVShowUseCase
20 |
21 | private val tvshows = listOf(
22 | TVShow(
23 | "id",
24 | "movieId",
25 | "title",
26 | "posterPath",
27 | "overview",
28 | "backdrop",
29 | 0,
30 | 0f,
31 | "relateDate"
32 | )
33 | )
34 |
35 | @Before fun setUp() {
36 | usecase = TVShowUseCase(repository)
37 | }
38 |
39 | @Test fun `should return success`() {
40 | val actual = ResultState.Success(TVShows(tvshows))
41 | val result = runBlocking {
42 | `when`(repository.getPopularTVShow())
43 | .thenReturn(
44 | Response.success(TVShows(tvshows))
45 | )
46 | usecase.getPopularTvShow()
47 | }
48 | assert(actual.javaClass === result.javaClass)
49 | }
50 |
51 | @Test fun `should return error`() {
52 | val actual = ResultState.Error("")
53 | val result = runBlocking {
54 | `when`(repository.getPopularTVShow())
55 | .thenReturn(
56 | Response.error(401, ResponseBody.create(MediaType.parse("application/json"), ""))
57 | )
58 | usecase.getPopularTvShow()
59 | }
60 | assert(actual.javaClass === result.javaClass)
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/features/tvshows/src/test/java/isfaaghyth/app/tvshows/ui/TVShowViewModelTest.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.tvshows.ui
2 |
3 | import androidx.arch.core.executor.testing.InstantTaskExecutorRule
4 | import androidx.lifecycle.Observer
5 | import isfaaghyth.app.abstraction.util.state.ResultState
6 | import isfaaghyth.app.abstraction.util.thread.TestSchedulerProvider
7 | import isfaaghyth.app.data.entity.TVShow
8 | import isfaaghyth.app.data.entity.TVShows
9 | import isfaaghyth.app.tvshows.domain.TVShowUseCase
10 | import kotlinx.coroutines.Dispatchers
11 | import kotlinx.coroutines.ExperimentalCoroutinesApi
12 | import kotlinx.coroutines.runBlocking
13 | import kotlinx.coroutines.test.resetMain
14 | import kotlinx.coroutines.test.setMain
15 | import org.junit.After
16 | import org.junit.Assert.assertEquals
17 | import org.junit.Before
18 | import org.junit.Rule
19 | import org.junit.Test
20 | import org.mockito.*
21 | import org.mockito.Mockito.*
22 |
23 | @ExperimentalCoroutinesApi
24 | class TVShowViewModelTest {
25 |
26 | @get:Rule val instantExecutorRule = InstantTaskExecutorRule()
27 |
28 | @Mock lateinit var result: Observer>
29 | @Mock lateinit var error: Observer
30 |
31 | @Captor lateinit var argResultCaptor: ArgumentCaptor>
32 | @Captor lateinit var argErrorCaptor: ArgumentCaptor
33 |
34 | @Mock lateinit var useCase: TVShowUseCase
35 |
36 | private lateinit var viewModel: TVShowViewModel
37 |
38 | private val tvShow = listOf(
39 | TVShow(
40 | "id",
41 | "movieId",
42 | "title",
43 | "posterPath",
44 | "overview",
45 | "backdrop",
46 | 0,
47 | 0f,
48 | "relateDate"
49 | )
50 | )
51 |
52 | private val moviesData = TVShows(tvShow)
53 | private val schedulerProvider = TestSchedulerProvider()
54 |
55 | @Before fun setUp() {
56 | MockitoAnnotations.initMocks(this)
57 | Dispatchers.setMain(schedulerProvider.ui())
58 |
59 | viewModel = TVShowViewModel(useCase, schedulerProvider)
60 | viewModel.result.observeForever(result)
61 | viewModel.error.observeForever(error)
62 | }
63 |
64 | @Test fun `should return a response of tvShows data`() = runBlocking {
65 | val returnValue = ResultState.Success(moviesData)
66 | Mockito.`when`(useCase.getPopularTvShow()).thenReturn(returnValue)
67 | viewModel.getPopularTvShow()
68 | verify(result, atLeastOnce()).onChanged(argResultCaptor.capture())
69 | assertEquals(returnValue.data.resultsIntent, argResultCaptor.allValues.first())
70 | clearInvocations(useCase, result)
71 | }
72 |
73 | @Test fun `should return an error without api key`() = runBlocking {
74 | val returnValue = ResultState.Error("error")
75 | Mockito.`when`(useCase.getPopularTvShow()).thenReturn(returnValue)
76 | viewModel.getPopularTvShow()
77 | verify(error, atLeastOnce()).onChanged(argErrorCaptor.capture())
78 | assertEquals(returnValue.error, argErrorCaptor.allValues.first())
79 | clearInvocations(useCase, error)
80 | }
81 |
82 | @After fun tearDown() {
83 | Dispatchers.resetMain()
84 | }
85 |
86 | }
--------------------------------------------------------------------------------
/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 | # the movie db
23 | movieApiKey="9351b653885866a95fcef04c4f0c7426"
24 | movieImageUrl="https://image.tmdb.org/t/p/w500"
25 | movieApiUrl="https://api.themoviedb.org/3/"
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jul 31 23:35:46 WIB 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 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/libraries/abstraction/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/abstraction/build.gradle:
--------------------------------------------------------------------------------
1 | apply from: "$rootDir/common.gradle"
2 |
3 | dependencies {
4 | //android support
5 | implementation Android.appCompat
6 | implementation Android.ktx
7 | implementation Android.constraintLayout
8 | implementation Jetpack.recyclerView
9 |
10 | //network
11 | implementation Retrofit.retrofit
12 |
13 | //coroutines
14 | implementation Coroutines.core
15 | implementation Jetpack.lifecycle
16 |
17 | //glide
18 | implementation Glide.glide
19 | kapt Glide.compiler
20 | }
--------------------------------------------------------------------------------
/libraries/abstraction/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 |
--------------------------------------------------------------------------------
/libraries/abstraction/src/androidTest/java/isfaaghyth/app/abstraction/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("isfaaghyth.app.abstraction.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/ic_placeholder-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/ic_placeholder-web.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/ic_rating-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/ic_rating-web.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/ic_voter-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/ic_voter-web.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/annotation/Testable.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.annotation
2 |
3 | @Target(AnnotationTarget.ANNOTATION_CLASS)
4 | annotation class OpenClass
5 |
6 | @OpenClass
7 | @Target(AnnotationTarget.CLASS)
8 | annotation class Testable
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/base/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.base
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import isfaaghyth.app.abstraction.util.view.KeyboardUtils
6 | import isfaaghyth.app.abstraction.util.ext.toast
7 |
8 | abstract class BaseActivity: AppCompatActivity(), BaseView {
9 |
10 | /**
11 | * lifecycle method
12 | * @method contentView(): @return resLayoutId
13 | * @method initView()depe
14 | */
15 | abstract fun contentView(): Int
16 | abstract fun initView()
17 | abstract fun initInjector()
18 |
19 | /**
20 | * (optional, use it if needed)
21 | */
22 | protected lateinit var savedInstanceState: Bundle
23 |
24 | override fun onCreate(savedInstanceState: Bundle?) {
25 | super.onCreate(savedInstanceState)
26 | if (savedInstanceState != null) {
27 | this.savedInstanceState = savedInstanceState
28 | }
29 | setContentView(contentView())
30 | initInjector()
31 | initView()
32 | }
33 |
34 | override fun onMessage(message: String?) {
35 | toast(message)
36 | }
37 |
38 | override fun onMessage(stringResId: Int) {
39 | onMessage(getString(stringResId))
40 | }
41 |
42 | /**
43 | * check internet connection
44 | */
45 | override fun isNetworkConnect(): Boolean {
46 | return true //TODO(make a utilities class for this)
47 | }
48 |
49 | /**
50 | * hide keyboard layout
51 | */
52 | override fun hideKeyboard() {
53 | return KeyboardUtils().hide(this)
54 | }
55 |
56 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/base/BaseView.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.base
2 |
3 | interface BaseView {
4 | fun onMessage(message: String?)
5 | fun onMessage(stringResId: Int)
6 | fun isNetworkConnect(): Boolean
7 | fun hideKeyboard()
8 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/base/BaseViewModel.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.base
2 |
3 | import androidx.lifecycle.ViewModel
4 | import isfaaghyth.app.abstraction.util.thread.SchedulerProvider
5 | import kotlinx.coroutines.CoroutineScope
6 | import kotlinx.coroutines.SupervisorJob
7 | import kotlinx.coroutines.isActive
8 | import kotlin.coroutines.CoroutineContext
9 |
10 | abstract class BaseViewModel(private val baseDispatcher: SchedulerProvider): ViewModel(), CoroutineScope {
11 |
12 | private val supervisorJob = SupervisorJob()
13 |
14 | override val coroutineContext: CoroutineContext
15 | get() = baseDispatcher.ui() + supervisorJob
16 |
17 | open fun clear() {
18 | if (isActive && !supervisorJob.isCancelled) {
19 | supervisorJob.children.map {
20 | it.cancel()
21 | }
22 | }
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/helper/EspressoIdlingResource.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.helper
2 |
3 | import androidx.test.espresso.IdlingResource
4 |
5 | //fetcher contract
6 | interface FetcherListener {
7 | fun begin()
8 | fun complete()
9 | }
10 |
11 | //singleton idling resources
12 | object FetchingIdlingResource {
13 | private var idlingResource = EspressoIdlingResource()
14 | fun begin() = idlingResource.begin()
15 | fun complete() = idlingResource.complete()
16 | fun get(): EspressoIdlingResource = idlingResource
17 | }
18 |
19 | class EspressoIdlingResource: IdlingResource, FetcherListener {
20 | private var idle = true
21 | private var resourceCallback: IdlingResource.ResourceCallback? = null
22 |
23 | override fun getName(): String {
24 | return EspressoIdlingResource::class.java.simpleName
25 | }
26 |
27 | override fun isIdleNow() = idle
28 |
29 | override fun registerIdleTransitionCallback(
30 | callback: IdlingResource.ResourceCallback?) {
31 | resourceCallback = callback
32 | }
33 |
34 | override fun complete() {
35 | idle = true
36 | resourceCallback?.onTransitionToIdle()
37 | }
38 |
39 | override fun begin() {
40 | idle = false
41 | }
42 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/ui/ViewPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.ui
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentManager
5 | import androidx.fragment.app.FragmentStatePagerAdapter
6 |
7 | class ViewPagerAdapter(fm: FragmentManager): FragmentStatePagerAdapter(fm) {
8 |
9 | private val mFragmentList = arrayListOf()
10 | private val mFragmentTitleList = arrayListOf()
11 |
12 | override fun getItem(position: Int): Fragment = mFragmentList[position]
13 |
14 | override fun getCount(): Int = mFragmentList.size
15 |
16 | override fun getPageTitle(position: Int): CharSequence? = mFragmentTitleList[position]
17 |
18 | fun addFragment(fragment: Fragment, title: String) {
19 | mFragmentList.add(fragment)
20 | mFragmentTitleList.add(title)
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/util/Consts.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.util
2 |
3 | const val UNSUCCESSFUL_MESSAGE = "Something Error!"
4 |
5 | object AppLink {
6 | object MovieDetail {
7 | const val MOVIE_DETAIL = "jetmovie://detail/{type}/{movie_id}"
8 | const val PARAM_TYPE = "type"
9 | const val PARAM_MOVIE_ID = "movie_id"
10 | }
11 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/util/ext/CoroutinesUtil.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.util.ext
2 |
3 | import isfaaghyth.app.abstraction.util.state.ResultState
4 | import java.net.ConnectException
5 |
6 | suspend fun fetchState(call: suspend () -> ResultState): ResultState {
7 | return try {
8 | call.invoke()
9 | } catch (e: ConnectException) {
10 | ResultState.Error(e.message)
11 | } catch (e: Exception) {
12 | ResultState.Error(e.message)
13 | } catch (e: Throwable) {
14 | ResultState.Error(e.message)
15 | }
16 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/util/ext/ImageExt.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.util.ext
2 |
3 | import android.widget.ImageView
4 | import com.bumptech.glide.Glide
5 | import com.bumptech.glide.load.resource.bitmap.CircleCrop
6 | import com.bumptech.glide.request.RequestOptions
7 |
8 | /**
9 | * Created by isfaaghyth on 27/04/19.
10 | * github: @isfaaghyth
11 | */
12 |
13 | fun ImageView.load(imageUri: Any) {
14 | Glide.with(context)
15 | .load(imageUri)
16 | .apply(RequestOptions())
17 | .into(this)
18 | }
19 |
20 | fun ImageView.circle(imageUri: Any) {
21 | Glide.with(context)
22 | .asBitmap()
23 | .load(imageUri)
24 | .apply(RequestOptions()
25 | .transform(CircleCrop())
26 | )
27 | .into(this)
28 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/util/ext/UtilExt.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.util.ext
2 |
3 | import android.app.Activity
4 | import android.view.View
5 | import android.widget.Toast
6 | import androidx.fragment.app.Fragment
7 |
8 | /**
9 | * Created by isfaaghyth on 29/04/19.
10 | * github: @isfaaghyth
11 | */
12 |
13 | fun Activity.toast(message: String?) {
14 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
15 | }
16 |
17 | fun Fragment.toast(message: String?) {
18 | Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
19 | }
20 |
21 | fun View.show() {
22 | visibility = View.VISIBLE
23 | }
24 |
25 | fun View.hide() {
26 | visibility = View.GONE
27 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/util/state/LoaderState.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.util.state
2 |
3 | sealed class LoaderState {
4 | object ShowLoading: LoaderState()
5 | object HideLoading: LoaderState()
6 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/util/state/ResultState.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.util.state
2 |
3 | sealed class ResultState {
4 | data class Success(val data: T): ResultState()
5 | data class Error(val error: String?): ResultState()
6 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/util/thread/ApplicationSchedulerProvider.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.util.thread
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.Dispatchers
5 |
6 | class ApplicationSchedulerProvider: SchedulerProvider {
7 | override fun ui(): CoroutineDispatcher = Dispatchers.Main
8 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/util/thread/SchedulerProvider.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.util.thread
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 |
5 | interface SchedulerProvider {
6 | fun ui(): CoroutineDispatcher
7 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/util/thread/TestSchedulerProvider.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.util.thread
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.Dispatchers
5 |
6 | class TestSchedulerProvider: SchedulerProvider {
7 | override fun ui(): CoroutineDispatcher = Dispatchers.Unconfined
8 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/util/view/KeyboardUtils.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.util.view
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.view.View
6 | import android.view.inputmethod.InputMethodManager
7 | import android.widget.EditText
8 |
9 | class KeyboardUtils {
10 |
11 | fun hide(activity: Activity) {
12 | var view = activity.currentFocus
13 | if (view == null) view = View(activity)
14 | val imm = activity.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
15 | imm.hideSoftInputFromWindow(view.windowToken, 0)
16 | }
17 |
18 | fun show(edit: EditText, context: Context) {
19 | edit.isFocusable = true
20 | edit.isFocusableInTouchMode = true
21 | edit.requestFocus()
22 | val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
23 | imm.showSoftInput(edit, 0)
24 | }
25 |
26 | fun toggle(context: Context) {
27 | val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
28 | imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/util/viewmodel/ViewModelFactory.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.util.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import javax.inject.Inject
6 | import javax.inject.Provider
7 |
8 | class ViewModelFactory @Inject constructor(
9 | private val creators: Map, @JvmSuppressWildcards Provider>): ViewModelProvider.Factory {
10 |
11 | override fun create(modelClass: Class): T {
12 | val creator = creators[modelClass] ?: creators.asIterable().firstOrNull {
13 | modelClass.isAssignableFrom(it.key)
14 | }?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
15 |
16 | return try {
17 | creator.get() as T
18 | } catch (e: Exception) {
19 | throw RuntimeException(e)
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/java/isfaaghyth/app/abstraction/util/viewmodel/ViewModelKey.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction.util.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import dagger.MapKey
5 | import kotlin.reflect.KClass
6 |
7 | @MustBeDocumented
8 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
9 | @Retention(AnnotationRetention.RUNTIME)
10 | @MapKey
11 | annotation class ViewModelKey(val value: KClass)
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/drawable/bg_gradient.xml:
--------------------------------------------------------------------------------
1 |
2 | -
3 |
4 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-hdpi/ic_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-hdpi/ic_placeholder.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-hdpi/ic_rating.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-hdpi/ic_rating.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-hdpi/ic_voter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-hdpi/ic_voter.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-mdpi/ic_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-mdpi/ic_placeholder.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-mdpi/ic_rating.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-mdpi/ic_rating.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-mdpi/ic_voter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-mdpi/ic_voter.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-xhdpi/ic_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-xhdpi/ic_placeholder.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-xhdpi/ic_rating.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-xhdpi/ic_rating.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-xhdpi/ic_voter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-xhdpi/ic_voter.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-xxhdpi/ic_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-xxhdpi/ic_placeholder.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-xxhdpi/ic_rating.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-xxhdpi/ic_rating.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-xxhdpi/ic_voter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-xxhdpi/ic_voter.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-xxxhdpi/ic_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-xxxhdpi/ic_placeholder.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-xxxhdpi/ic_rating.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-xxxhdpi/ic_rating.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/mipmap-xxxhdpi/ic_voter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isfaaghyth/kotlin-everywhere-example/c055a424869755f32b058a4c46f90a39dff97c17/libraries/abstraction/src/main/res/mipmap-xxxhdpi/ic_voter.png
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/libraries/abstraction/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/libraries/abstraction/src/test/java/isfaaghyth/app/abstraction/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.abstraction;
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 | }
--------------------------------------------------------------------------------
/libraries/network/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/network/build.gradle:
--------------------------------------------------------------------------------
1 | apply from: "$rootDir/common.gradle"
2 |
3 | dependencies {
4 | //network
5 | implementation Retrofit.retrofit
6 | implementation Retrofit.gsonConverter
7 | implementation Retrofit.coroutinesAdapter
8 | implementation Retrofit.okHttpLogging
9 | }
--------------------------------------------------------------------------------
/libraries/network/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 |
--------------------------------------------------------------------------------
/libraries/network/src/androidTest/java/isfaaghyth/app/network/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.network;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("isfaaghyth.app.network.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/libraries/network/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/libraries/network/src/main/java/isfaaghyth/app/network/Network.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.network
2 |
3 | import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
4 | import okhttp3.OkHttpClient
5 | import okhttp3.logging.HttpLoggingInterceptor
6 | import retrofit2.Retrofit
7 | import retrofit2.converter.gson.GsonConverterFactory
8 | import java.util.concurrent.TimeUnit
9 |
10 | object Network {
11 |
12 | fun retrofitClient(url: String = BuildConfig.MOVIE_URL): Retrofit {
13 | return Retrofit.Builder()
14 | .baseUrl(url)
15 | .addCallAdapterFactory(CoroutineCallAdapterFactory())
16 | .addConverterFactory(GsonConverterFactory.create())
17 | .client(okHttpClient())
18 | .build()
19 | }
20 |
21 | private fun okHttpClient(): OkHttpClient {
22 | return OkHttpClient.Builder()
23 | .retryOnConnectionFailure(true)
24 | .addInterceptor(NetworkInterceptor())
25 | .addInterceptor(createLoggingInterceptor())
26 | .pingInterval(30, TimeUnit.SECONDS)
27 | .readTimeout(1, TimeUnit.MINUTES)
28 | .connectTimeout(1, TimeUnit.MINUTES)
29 | .build()
30 | }
31 |
32 | private fun createLoggingInterceptor(): HttpLoggingInterceptor {
33 | val interceptor = HttpLoggingInterceptor()
34 | interceptor.level = HttpLoggingInterceptor.Level.BODY
35 | return interceptor
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/libraries/network/src/main/java/isfaaghyth/app/network/NetworkInterceptor.kt:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.network
2 |
3 | import okhttp3.Interceptor
4 | import okhttp3.Response
5 |
6 | class NetworkInterceptor: Interceptor {
7 |
8 | override fun intercept(chain: Interceptor.Chain): Response {
9 | var request = chain.request()
10 | val url = request.url()
11 | .newBuilder()
12 | .addQueryParameter("api_key", BuildConfig.API_KEY)
13 | .build()
14 | request = request.newBuilder().url(url).build()
15 | return chain.proceed(request)
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/libraries/network/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/libraries/network/src/test/java/isfaaghyth/app/network/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package isfaaghyth.app.network;
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 | //app
2 | include Modules.app
3 |
4 | //features
5 | include Modules.movies
6 | include Modules.tvshows
7 | include Modules.movieDetail
8 |
9 | //libraries
10 | include Modules.network
11 | include Modules.abstraction
12 |
13 | //data
14 | include Modules.data
--------------------------------------------------------------------------------