├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── kotlincleancode4android
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── kotlincleancode4android
│ │ │ ├── AppController.kt
│ │ │ ├── FlightModel.kt
│ │ │ ├── Util.kt
│ │ │ ├── boardingScreen
│ │ │ ├── BoardingActivity.kt
│ │ │ ├── BoardingConfigurator.kt
│ │ │ ├── BoardingInteractor.kt
│ │ │ ├── BoardingPresenter.kt
│ │ │ ├── BoardingRouter.kt
│ │ │ ├── BoardingWorker.kt
│ │ │ └── CheckINModel.kt
│ │ │ ├── homescreen
│ │ │ ├── FlightListAdapter.kt
│ │ │ ├── FlightWorker.kt
│ │ │ ├── HomeConfigurator.kt
│ │ │ ├── HomeFragment.kt
│ │ │ ├── HomeInteractor.kt
│ │ │ ├── HomeModels.kt
│ │ │ ├── HomePresenter.kt
│ │ │ ├── HomeRouter.kt
│ │ │ └── MainActivity.kt
│ │ │ └── pastTripScreen
│ │ │ └── PastTripFragment.kt
│ └── res
│ │ ├── drawable-v24
│ │ ├── ic_launcher_foreground.xml
│ │ ├── ic_plan_landing.xml
│ │ ├── ic_plan_takeoff.xml
│ │ ├── shape_rectangle_fill_light.xml
│ │ └── shape_rectangle_stroke.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_boarding.xml
│ │ ├── activity_main.xml
│ │ ├── activity_past_trip.xml
│ │ ├── cell_trip_list.xml
│ │ ├── fragment_home.xml
│ │ └── fragment_past_trip.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
│ │ ├── ids.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ └── backup_descriptor.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── kotlincleancode4android
│ ├── HomeFragmentUnitTest.kt
│ ├── HomeInteractorUnitTest.kt
│ ├── HomePresenterUnitTest.kt
│ └── HomeRouterUnitTest.kt
├── build.gradle
├── doc
└── images
│ └── kotlinCleanCode.png
├── gradle.properties
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches/build_file_checksums.ser
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | .DS_Store
9 | /build
10 | /captures
11 | .externalNativeBuild
12 |
13 |
14 | gradlew*
15 | *.iml
16 | .gradle
17 | /local.properties
18 | /.idea/workspace.xml
19 | /.idea/libraries
20 | .DS_Store
21 | /build
22 | /captures
23 | .externalNativeBuild
24 | build/
25 | projectFilesBackup/
26 | gradle/
27 | .gradle/
28 | .idea/**
29 | .notidea/
30 | *.DS_Store
31 | *.iml
32 | app/build/**
33 |
34 |
35 | # built application files
36 | *.apk
37 | *.ap_
38 |
39 | # files for the dex VM
40 | *.dex
41 |
42 | # Java class files
43 | *.class
44 |
45 | # built native files (uncomment if you build your own)
46 | # *.o
47 | # *.so
48 |
49 | # generated files
50 | bin/
51 | gen/
52 |
53 | # Ignore gradle files
54 | .gradle/
55 | build/
56 |
57 | # Local configuration file (sdk path, etc)
58 | local.properties
59 |
60 | # Proguard folder generated by Eclipse
61 | proguard/
62 |
63 | # Eclipse Metadata
64 | .metadata/
65 |
66 | # Mac OS X clutter
67 | *.DS_Store
68 |
69 | # Windows clutter
70 | Thumbs.db
71 | # Intellij IDEA (see https://intellij-support.jetbrains.com/entries/23393067)
72 | .idea/workspace.xml
73 | .idea/tasks.xml
74 | .idea/datasources.xml
75 | .idea/dataSources.ids
76 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Mohanraj Karatadipalayam
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Test Driven Android Development - Kotlin Clean Code For Android
2 | #### Inspired from [Clean Architecture][1] from Uncle Bob, [Google Android samples][3] and [Clean Swift][2]
3 | ##### This design is chosen with a singular focus - testability.
4 | ##### Want to learn about Android Clean Code ? follow [here][7]
5 |
6 |
7 | 
8 |
9 |
10 | ## Quick Start
11 | 1. Clone and open in Android Studio
12 | 2. It should work with out any issues
13 | 3. When running the unit test cases choose the debug variant `unitTest`, **it will not work with `debug` or `release` variants**
14 |
15 |
16 | ## Whats Next ?
17 | Want to use it your projects - bothered about the too much boilerplate code ? You can generate them with in few seconds - check [here][5]
18 |
19 | ### Contribute
20 | Welcome to contribute, feel free to change and open a PR.
21 |
22 | ### License
23 | [MIT License][6]
24 |
25 | #### TODO
26 | 1. Create example test cases for Router
27 | 2. Create code and test examples for the detail page.
28 | 3. Create example for Fragments
29 |
30 | #### Inspiration
31 |
32 | This project ideas are n't new in any way. Credit has to be given to the following projects, listed in autobiographical order.
33 |
34 | [Clean Architecture][1]
35 |
36 | [clean-swift][2]
37 |
38 | [1]: https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
39 | [2]: http://clean-swift.com
40 | [3]: https://github.com/googlesamples/android-testing
41 | [4]: https://riggaroo.co.za/custom-file-templates-android-studio/
42 | [5]: https://github.com/kmmraj/kt-clean-code4Android-generator
43 | [6]: ./LICENSE
44 | [7]: https://medium.com/@kmmraj/kotlin-clean-code-4-android-part-1-3d01703ebea8
45 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | gradlew*
3 | *.iml
4 | .gradle
5 | /local.properties
6 | /.idea/workspace.xml
7 | /.idea/libraries
8 | .DS_Store
9 | /build
10 | /captures
11 | .externalNativeBuild
12 | build/
13 | projectFilesBackup/
14 | gradle/
15 | .gradle/
16 | .idea/**
17 | .notidea/
18 | *.DS_Store
19 | *.iml
20 | app/build/**
21 |
22 |
23 | # built application files
24 | *.apk
25 | *.ap_
26 |
27 | # files for the dex VM
28 | *.dex
29 |
30 | # Java class files
31 | *.class
32 |
33 | # built native files (uncomment if you build your own)
34 | # *.o
35 | # *.so
36 |
37 | # generated files
38 | bin/
39 | gen/
40 |
41 | # Ignore gradle files
42 | .gradle/
43 | build/
44 |
45 | # Local configuration file (sdk path, etc)
46 | local.properties
47 |
48 | # Proguard folder generated by Eclipse
49 | proguard/
50 |
51 | # Eclipse Metadata
52 | .metadata/
53 |
54 | # Mac OS X clutter
55 | *.DS_Store
56 |
57 | # Windows clutter
58 | Thumbs.db
59 | # Intellij IDEA (see https://intellij-support.jetbrains.com/entries/23393067)
60 | .idea/workspace.xml
61 | .idea/tasks.xml
62 | .idea/datasources.xml
63 | .idea/dataSources.ids
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | apply plugin: 'kotlin-android-extensions'
6 |
7 |
8 | ext {
9 | leakcanary_android_version = "1.5.4"
10 | support_lib_version = "27.1.1"
11 | constraint_layout_version = "1.1.3"
12 | ktlint_version = "0.29.0"
13 | junit_version = "4.12"
14 | robolectric_version = "3.8"
15 | test_runner_version = "1.0.2"
16 | espresso_core_version = "3.0.2"
17 | }
18 |
19 | android {
20 | compileSdkVersion 27
21 | defaultConfig {
22 | applicationId "com.example.kotlincleancode4android"
23 | minSdkVersion 16
24 | targetSdkVersion 27
25 | versionCode 1
26 | versionName "1.0"
27 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
28 | }
29 | buildTypes {
30 | release {
31 | minifyEnabled false
32 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
33 | buildConfigField 'boolean', 'memoryTest', 'true'
34 | }
35 | debug {
36 | buildConfigField 'boolean', 'memoryTest', 'true'
37 | }
38 |
39 | unitTest {
40 | buildConfigField 'boolean', 'memoryTest', 'false'
41 | }
42 | }
43 |
44 | testOptions {
45 | unitTests {
46 | includeAndroidResources = true
47 | }
48 | }
49 |
50 | configurations {
51 | ktlint
52 | }
53 |
54 | defaultConfig {
55 | vectorDrawables.useSupportLibrary = true
56 | }
57 | }
58 |
59 | dependencies {
60 | implementation fileTree(dir: 'libs', include: ['*.jar'])
61 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
62 | implementation "com.android.support:appcompat-v7:$support_lib_version"
63 | implementation "com.android.support.constraint:constraint-layout:$constraint_layout_version"
64 | implementation "com.android.support:design:$support_lib_version"
65 |
66 | ktlint "com.github.shyiko:ktlint:$ktlint_version"
67 |
68 | testImplementation "junit:junit:$junit_version"
69 | testImplementation "org.robolectric:robolectric:$robolectric_version"
70 |
71 |
72 | androidTestImplementation "com.android.support.test:runner:$test_runner_version"
73 | androidTestImplementation "com.android.support.test.espresso:espresso-core:$espresso_core_version"
74 |
75 |
76 | debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakcanary_android_version"
77 | implementation "com.squareup.leakcanary:leakcanary-android:$leakcanary_android_version"
78 | releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakcanary_android_version"
79 |
80 |
81 | }
82 |
83 |
84 | task ktlint(type: JavaExec, group: "verification") {
85 | description = "Check Kotlin code style."
86 | main = "com.github.shyiko.ktlint.Main"
87 | classpath = configurations.ktlint
88 | args "src/**/*.kt"
89 | // to generate report in checkstyle format prepend following args:
90 | // "--reporter=plain", "--reporter=checkstyle,output=${buildDir}/ktlint.xml"
91 | // see https://github.com/shyiko/ktlint#usage for more
92 | }
93 | check.dependsOn ktlint
94 |
95 | task ktlintFormat(type: JavaExec, group: "formatting") {
96 | description = "Fix Kotlin code style deviations."
97 | main = "com.github.shyiko.ktlint.Main"
98 | classpath = configurations.ktlint
99 | args "-F", "src/**/*.kt"
100 | }
101 |
--------------------------------------------------------------------------------
/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/com/example/kotlincleancode4android/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android
2 |
3 | import android.support.test.InstrumentationRegistry
4 | import android.support.test.runner.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getTargetContext()
22 | assertEquals("com.example.kotlincleancode4android", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/AppController.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android
2 |
3 | import android.app.Application
4 | import com.squareup.leakcanary.LeakCanary
5 |
6 | /**
7 | * Created by Mohanraj Karatadipalayam on 12/10/18.
8 | */
9 | class AppController: Application() {
10 | override fun onCreate() {
11 | super.onCreate()
12 | // TODO - Create a debugTest build variant
13 | if(BuildConfig.memoryTest) {
14 | if (LeakCanary.isInAnalyzerProcess(this)) {
15 | // This process is dedicated to LeakCanary for heap analysis.
16 | // You should not init your app in this process.
17 | return
18 | }
19 | LeakCanary.install(this)
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/FlightModel.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android
2 |
3 | import android.os.Parcel
4 | import android.os.Parcelable
5 |
6 | /**
7 | * Created by Mohanraj Karatadipalayam on 06/10/18.
8 | */
9 |
10 | // Inline function to create Parcel Creator
11 | inline fun createParcel(crossinline createFromParcel: (Parcel) -> T?): Parcelable.Creator =
12 | object : Parcelable.Creator {
13 | override fun createFromParcel(source: Parcel): T? = createFromParcel(source)
14 | override fun newArray(size: Int): Array = arrayOfNulls(size)
15 | }
16 |
17 | // custom readParcelable to avoid reflection
18 | fun Parcel.readParcelable(creator: Parcelable.Creator): T? {
19 | if (readString() != null) return creator.createFromParcel(this) else return null
20 | }
21 |
22 | open class FlightModel(
23 | var flightName: String,
24 | var startingTime: String,
25 | var departureCity: String,
26 | var arrivalCity: String,
27 | var departureTime: String,
28 | var arrivalTime: String
29 | ): Parcelable {
30 |
31 | constructor(parcelIn: Parcel) : this(
32 | flightName = parcelIn.readString(),
33 | startingTime = parcelIn.readString(),
34 | departureCity = parcelIn.readString(),
35 | arrivalCity = parcelIn.readString(),
36 | departureTime = parcelIn.readString(),
37 | arrivalTime = parcelIn.readString()
38 | )
39 |
40 |
41 |
42 | override fun describeContents(): Int {
43 | return 0
44 | }
45 |
46 | override fun writeToParcel(dest: Parcel, flags: Int) {
47 | dest.writeString(flightName)
48 | dest.writeString(startingTime)
49 | dest.writeString(departureCity)
50 | dest.writeString(arrivalCity)
51 | dest.writeString(departureTime)
52 | dest.writeString(arrivalTime)
53 | }
54 |
55 | companion object {
56 | @JvmField @Suppress("unused")
57 | val CREATOR = createParcel { FlightModel(it) }
58 | }
59 | }
60 |
61 |
62 | class FlightViewModel(
63 | var noOfDaysToFly: String? = null,
64 | flightName: String,
65 | startingTime: String,
66 | departureCity: String,
67 | arrivalCity: String,
68 | departureTime: String,
69 | arrivalTime: String
70 | ): FlightModel(
71 | flightName,
72 | startingTime,
73 | departureCity,
74 | arrivalCity,
75 | departureTime,
76 | arrivalTime
77 | ) {
78 |
79 | constructor(parcelIn: Parcel): this(
80 | noOfDaysToFly = parcelIn.readString(),
81 | flightName = parcelIn.readString(),
82 | startingTime = parcelIn.readString(),
83 | departureCity = parcelIn.readString(),
84 | arrivalCity = parcelIn.readString(),
85 | departureTime = parcelIn.readString(),
86 | arrivalTime = parcelIn.readString()
87 | )
88 |
89 |
90 | override fun describeContents(): Int {
91 | return 0
92 | }
93 |
94 | override fun writeToParcel(dest: Parcel, flags: Int) {
95 | dest.writeString(noOfDaysToFly)
96 | super.writeToParcel(dest, flags)
97 |
98 | }
99 |
100 | companion object {
101 | @JvmField @Suppress("unused")
102 | val CREATOR = createParcel { FlightViewModel(it) }
103 | }
104 |
105 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/Util.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android
2 |
3 | import android.support.v4.app.FragmentTransaction
4 | import android.support.v7.app.AppCompatActivity
5 | import java.util.Calendar
6 | import java.util.concurrent.TimeUnit
7 |
8 | /**
9 | * Created by Mohanraj Karatadipalayam on 06/10/18.
10 | */
11 |
12 | class ArrayEmptyException(var msg: String? = "ArrayEmptyException") : RuntimeException()
13 |
14 | object CalendarUtil {
15 |
16 | fun getCalendar(date: String?): Calendar {
17 | // Date should be in the format YYYY/MM/DD if not return
18 | if (date != null && !date.isEmpty() && date.length == 10) {
19 | val year = Integer.parseInt(date.substring(0, 4))
20 | val month = Integer.parseInt(date.substring(5, 7))
21 | val day = Integer.parseInt(date.substring(8, 10))
22 | val startingTime = Calendar.getInstance()
23 | startingTime.set(year, month - 1, day, 0, 0, 0)
24 | return startingTime
25 | }
26 | return Calendar.getInstance()
27 | }
28 |
29 | fun getDaysDiff(startTime: Long, endTime: Long): Long {
30 | val msDiff: Long
31 | if (endTime > startTime) {
32 | msDiff = endTime - startTime
33 | } else {
34 | msDiff = startTime - endTime
35 | }
36 | // Log.e(TAG,"diff is "+ daysDiff);
37 | return TimeUnit.MILLISECONDS.toDays(msDiff)
38 | }
39 | }
40 |
41 | inline fun AppCompatActivity.transact(action: FragmentTransaction.() -> Unit) {
42 | supportFragmentManager.beginTransaction().apply {
43 | action()
44 | }.commit()
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/boardingScreen/BoardingActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.boardingScreen
2 |
3 | import android.support.v7.app.AppCompatActivity
4 | import android.os.Bundle
5 | import android.widget.TextView
6 | import com.example.kotlincleancode4android.FlightModel
7 | import com.example.kotlincleancode4android.R
8 |
9 | interface BoardingActivityInput {
10 | fun displayBoardingData(viewModel: BoardingViewModel)
11 | }
12 |
13 |
14 | class BoardingActivity : AppCompatActivity(), BoardingActivityInput {
15 |
16 | var output: BoardingInteractorInput? = null
17 | var router: BoardingRouter? = null
18 |
19 | private var flightModel: FlightModel? = null
20 | private var passengerName: TextView? = null
21 | private var flightCode: TextView? = null
22 | private var departureCity: TextView? = null
23 | private var arrivalCity: TextView? = null
24 | private var boardingTime: TextView? = null
25 | private var departureTime: TextView? = null
26 | private var departureDate: TextView? = null
27 | private var arrivalTime: TextView? = null
28 | private var gate: TextView? = null
29 | private var terminal: TextView? = null
30 | private var seat: TextView? = null
31 |
32 | override fun onCreate(savedInstanceState: Bundle?) {
33 | super.onCreate(savedInstanceState)
34 | setContentView(R.layout.activity_boarding)
35 | flightModel = intent.getParcelableExtra("flight")
36 | bindViews()
37 |
38 | // Do the setup
39 | BoardingConfigurator.configure(this)
40 | val aBoardingRequest = BoardingRequest()
41 |
42 | // Populate the request
43 | output?.fetchBoardingData(aBoardingRequest)
44 |
45 | // Do other work
46 | }
47 |
48 | private fun bindViews() {
49 | passengerName = findViewById(R.id.tv_passengerName)
50 | flightCode = findViewById(R.id.tv_flightNumberValue)
51 | departureCity = findViewById(R.id.tv_departureAirport)
52 | arrivalCity = findViewById(R.id.tv_arrivalAirport)
53 | boardingTime = findViewById(R.id.tv_boardingTime)
54 | departureTime = findViewById(R.id.tv_departureTime)
55 | arrivalTime = findViewById(R.id.tv_arrivalTime)
56 | departureDate = findViewById(R.id.tv_departureDate)
57 | gate = findViewById(R.id.tv_gateValue)
58 | terminal = findViewById(R.id.tv_terminalValue)
59 | seat = findViewById(R.id.tv_seatValue)
60 | }
61 |
62 |
63 | override fun displayBoardingData(viewModel: BoardingViewModel) {
64 | // Log.e(TAG, "displayBoardingData() called with: viewModel = [$viewModel]")
65 | // Log.e(TAG, "displayBoardingData() called with: flightModel = [$flightModel]")
66 | // Deal with the data
67 | val checkINModel = viewModel.checkINModel
68 | passengerName?.text = "Mohan Karats"
69 | arrivalCity?.text = flightModel?.arrivalCity
70 | arrivalTime?.text = flightModel?.arrivalTime
71 | departureCity?.text = flightModel?.departureCity
72 | departureTime?.text = flightModel?.departureTime
73 | departureDate?.text = flightModel?.startingTime
74 |
75 | gate?.text = checkINModel?.gate
76 | terminal?.text = checkINModel?.terminal
77 | seat?.text = checkINModel?.seat
78 | }
79 |
80 | companion object {
81 | const val TAG = "BoardingActivity"
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/boardingScreen/BoardingConfigurator.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.boardingScreen
2 |
3 | import java.lang.ref.WeakReference
4 |
5 | /**
6 | * Created by Mohanraj Karatadipalayam on 08/10/18.
7 | */
8 |
9 | object BoardingConfigurator {
10 |
11 | fun configure(activity: BoardingActivity) {
12 |
13 | val router = BoardingRouter()
14 | router.activity = WeakReference(activity)
15 |
16 | val presenter = BoardingPresenter()
17 | presenter.output = WeakReference(activity)
18 |
19 | val interactor = BoardingInteractor()
20 | interactor.output = presenter
21 |
22 | // if (activity.output == null) {
23 | activity.output = interactor
24 | // }
25 | // if (activity.router == null) {
26 | activity.router = router
27 | // }
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/boardingScreen/BoardingInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.boardingScreen
2 |
3 | /**
4 | * Created by Mohanraj Karatadipalayam on 08/10/18.
5 | */
6 | interface BoardingInteractorInput {
7 | fun fetchBoardingData(request: BoardingRequest)
8 | }
9 |
10 | class BoardingInteractor: BoardingInteractorInput {
11 |
12 | var output: BoardingPresenterInput? = null
13 | private var internalBoardingWorkerInput: BoardingWorkerInput? = null
14 |
15 | private var boardingWorkerInput: BoardingWorkerInput
16 | get() = if (internalBoardingWorkerInput == null) BoardingWorker()
17 | else
18 | internalBoardingWorkerInput!!
19 | set(boardingWorkerInput) {
20 | this.internalBoardingWorkerInput = boardingWorkerInput
21 | }
22 |
23 | override fun fetchBoardingData(request: BoardingRequest) {
24 | internalBoardingWorkerInput = boardingWorkerInput
25 | val aBoardingResponse = BoardingResponse()
26 | // Call the workers
27 | aBoardingResponse.checkINModel = internalBoardingWorkerInput?.getCheckINDetails(request)
28 | output?.presentBoardingData(aBoardingResponse)
29 | }
30 |
31 | companion object {
32 | const val TAG = "BoardingInteractor"
33 | }
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/boardingScreen/BoardingPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.boardingScreen
2 |
3 | import java.lang.ref.WeakReference
4 |
5 | /**
6 | * Created by Mohanraj Karatadipalayam on 08/10/18.
7 | */
8 | interface BoardingPresenterInput {
9 | fun presentBoardingData(response: BoardingResponse)
10 | }
11 |
12 | class BoardingPresenter : BoardingPresenterInput {
13 |
14 | var output: WeakReference? = null
15 |
16 | override fun presentBoardingData(response: BoardingResponse) {
17 | // Log.e(TAG, "presentBoardingData() called with: response = [" + response + "]");
18 | // Do your decoration or filtering here
19 | // Model and Viewmodel is same here
20 | val boardingViewModel = BoardingViewModel()
21 | boardingViewModel.checkINModel = response.checkINModel
22 | output?.get()?.displayBoardingData(boardingViewModel)
23 | }
24 |
25 | companion object {
26 | const val TAG = "BoardingPresenter"
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/boardingScreen/BoardingRouter.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.boardingScreen
2 |
3 | import android.content.Intent
4 | import android.view.View
5 | import android.widget.AdapterView
6 | import java.lang.ref.WeakReference
7 |
8 | /**
9 | * Created by Mohanraj Karatadipalayam on 08/10/18.
10 | */
11 | internal interface BoardingRouterInput {
12 | fun navigateToSomeWhere(position: Int): Intent
13 | fun passDataToNextScene(position: Int, intent: Intent)
14 | }
15 |
16 | class BoardingRouter : BoardingRouterInput, AdapterView.OnItemClickListener {
17 |
18 | var activity: WeakReference? = null
19 |
20 | override fun navigateToSomeWhere(position: Int): Intent {
21 | // Based on the position or some other data decide what is the next scene
22 | // Intent intent = new Intent(activity.get(),NextActivity.class);
23 | // return intent;
24 | return Intent()
25 | }
26 |
27 | override fun passDataToNextScene(position: Int, intent: Intent) {
28 | // Based on the position or some other data decide the data for the next scene
29 | // BoardingModel flight = activity.get().listOfSomething.get(position);
30 | // intent.putExtra("flight",flight);
31 | }
32 |
33 | override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) {
34 | // Log.e(TAG, "onItemClick() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]");
35 | val intent = navigateToSomeWhere(position)
36 | passDataToNextScene(position, intent)
37 | activity?.get()?.startActivity(intent)
38 | }
39 |
40 | companion object {
41 | const val TAG = "BoardingRouter"
42 | }
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/boardingScreen/BoardingWorker.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.boardingScreen
2 |
3 | /**
4 | * Created by Mohanraj Karatadipalayam on 08/10/18.
5 | */
6 | interface BoardingWorkerInput {
7 | // Define needed interfaces
8 | fun getCheckINDetails(boardingRequest: BoardingRequest): CheckINModel
9 | }
10 |
11 | class BoardingWorker: BoardingWorkerInput {
12 |
13 | override fun getCheckINDetails(boardingRequest: BoardingRequest): CheckINModel {
14 | val checkINModel = CheckINModel()
15 | checkINModel.flightName = ""
16 | checkINModel.startingTime = ""
17 | checkINModel.gate = "24"
18 | checkINModel.terminal = "2"
19 | checkINModel.seat = "6A"
20 | return checkINModel
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/boardingScreen/CheckINModel.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.boardingScreen
2 |
3 | /**
4 | * Created by Mohanraj Karatadipalayam on 08/10/18.
5 | */
6 | data class CheckINModel(
7 | var flightName: String? = null,
8 | var startingTime: String? = null,
9 | var terminal: String? = null,
10 | var gate: String? = null,
11 | var seat: String? = null
12 | )
13 |
14 | // filter to have only the needed data
15 | data class BoardingViewModel(var checkINModel: CheckINModel? = null)
16 |
17 | data class BoardingRequest(var ffNumber: String? = null)
18 |
19 | data class BoardingResponse(var checkINModel: CheckINModel? = null)
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/homescreen/FlightListAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.homescreen
2 |
3 | import android.content.Context
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.BaseAdapter
8 | import android.widget.TextView
9 | import com.example.kotlincleancode4android.FlightViewModel
10 | import com.example.kotlincleancode4android.R
11 | import java.util.ArrayList
12 |
13 | /**
14 | * Created by Mohanraj Karatadipalayam on 08/10/18.
15 | */
16 | // TODO - check the context, should it be a WeakReference
17 | class FlightListAdapter(context: Context,
18 | private val listOfVMFlights: ArrayList) : BaseAdapter() {
19 |
20 | private val layoutInflater: LayoutInflater = LayoutInflater.from(context)
21 |
22 | override fun getCount(): Int {
23 | return listOfVMFlights.size
24 | }
25 |
26 | override fun getItem(position: Int): Any {
27 | return listOfVMFlights[position]
28 | }
29 |
30 | override fun getItemId(position: Int): Long {
31 | return position.toLong()
32 | }
33 |
34 | override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
35 | var view = convertView
36 |
37 | if (view == null) {
38 | view = layoutInflater.inflate(R.layout.cell_trip_list, null)
39 | view?.let {
40 | val viewHolder = ViewHolder()
41 | viewHolder.flightNameTextView = view.findViewById(R.id.tv_flightNumberValue) as TextView
42 | viewHolder.startTimeTextView = view.findViewById(R.id.tv_flightTimeDescription) as TextView
43 | view.tag = viewHolder
44 | }
45 | }
46 | val viewHolder = view?.tag as ViewHolder
47 | viewHolder.flightNameTextView?.text = listOfVMFlights[position].flightName
48 | viewHolder.startTimeTextView?.text = listOfVMFlights[position].noOfDaysToFly
49 | return view
50 | }
51 | }
52 |
53 | class ViewHolder {
54 | var flightNameTextView: TextView? = null
55 | var startTimeTextView: TextView? = null
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/homescreen/FlightWorker.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.homescreen
2 |
3 | import com.example.kotlincleancode4android.FlightModel
4 | import java.util.ArrayList
5 |
6 | /**
7 | * Created by Mohanraj Karatadipalayam on 06/10/18.
8 | */
9 | interface FlightWorkerInput {
10 | val futureFlights: ArrayList
11 | val pastFlights: ArrayList
12 | }
13 |
14 | class FlightWorker : FlightWorkerInput {
15 |
16 | override val futureFlights: ArrayList
17 | get() {
18 |
19 | val flightsList = ArrayList()
20 |
21 | val flight1 = FlightModel(flightName = "9Z 231",
22 | startingTime = "2018/10/31",
23 | departureCity = "BLR",
24 | arrivalCity = "CJB",
25 | departureTime = "06:00",
26 | arrivalTime = "06:50")
27 |
28 | flightsList.add(flight1)
29 |
30 | val flight2 = FlightModel(flightName = "9Z 15",
31 | startingTime = "2017/02/31",
32 | departureCity = "BLR",
33 | arrivalCity = "CJB",
34 | departureTime = "09:00",
35 | arrivalTime = "09:50")
36 | flightsList.add(flight2)
37 |
38 | val flight3 = FlightModel(flightName = "9Z 142",
39 | startingTime = "2017/12/31",
40 | departureCity = "BLR",
41 | arrivalCity = "CJB",
42 | departureTime = "18:10",
43 | arrivalTime = "19:00")
44 | flightsList.add(flight3)
45 |
46 | return flightsList
47 | }
48 |
49 | override val pastFlights: ArrayList
50 | get() {
51 |
52 | val flightsList = ArrayList()
53 |
54 | val flight1 = FlightModel(flightName = "9Z 231",
55 | startingTime = "2015/10/31",
56 | departureCity = "BLR",
57 | arrivalCity = "CJB",
58 | departureTime = "06:00",
59 | arrivalTime = "06:50")
60 |
61 | flightsList.add(flight1)
62 |
63 | val flight2 = FlightModel(flightName = "9Z 15",
64 | startingTime = "2015/11/31",
65 | departureCity = "BLR",
66 | arrivalCity = "CJB",
67 | departureTime = "09:00",
68 | arrivalTime = "09:50")
69 | flightsList.add(flight2)
70 |
71 | val flight3 = FlightModel(flightName = "9Z 142",
72 | startingTime = "2015/12/31",
73 | departureCity = "BLR",
74 | arrivalCity = "CJB",
75 | departureTime = "18:10",
76 | arrivalTime = "19:00")
77 | flightsList.add(flight3)
78 |
79 | return flightsList
80 | }
81 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/homescreen/HomeConfigurator.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.homescreen
2 |
3 | import java.lang.ref.WeakReference
4 |
5 | /**
6 | * Created by Mohanraj Karatadipalayam on 06/10/18.
7 | */
8 |
9 | object HomeConfigurator {
10 |
11 | fun configureFragment(fragment: HomeFragment) {
12 |
13 | val router = HomeRouter()
14 | router.fragment = WeakReference(fragment)
15 |
16 | val presenter = HomePresenter()
17 | presenter.output = WeakReference(fragment)
18 |
19 | val interactor = HomeInteractor()
20 | interactor.output = presenter
21 |
22 | fragment.output = interactor
23 | fragment.router = router
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/homescreen/HomeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.homescreen
2 |
3 |
4 | import android.content.Context
5 | import android.os.Bundle
6 | import android.support.v4.app.Fragment
7 | import android.util.Log
8 | import android.view.LayoutInflater
9 | import android.view.View
10 | import android.view.ViewGroup
11 | import android.widget.ListView
12 | import com.example.kotlincleancode4android.FlightViewModel
13 | import com.example.kotlincleancode4android.R
14 | import java.util.ArrayList
15 |
16 | /**
17 | * Created by Mohanraj Karatadipalayam on 08/10/18.
18 | */
19 |
20 | interface HomeFragmentInput {
21 | fun displayHomeMetaData(viewModel: HomeViewModel)
22 | }
23 |
24 | class HomeFragment : Fragment(), HomeFragmentInput {
25 |
26 | var listOfVMFlights: ArrayList = arrayListOf()
27 | lateinit var output: HomeInteractorInput
28 | lateinit var router: HomeRouter
29 | lateinit var homeFragmentListener: HomeFragmentListener
30 |
31 |
32 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
33 | savedInstanceState: Bundle?): View? {
34 | // Inflate the layout for this fragment
35 | val view = inflater.inflate(R.layout.fragment_home, container, false)
36 |
37 | HomeConfigurator.configureFragment(this)
38 | fetchData()
39 | createFlightListView(view)
40 |
41 | return view
42 | }
43 |
44 | override fun onAttach(context: Context?) {
45 | super.onAttach(context)
46 | // This makes sure that the container activity has implemented
47 | // the callback interface. If not, it throws an exception
48 | try {
49 | homeFragmentListener = activity as HomeFragmentListener
50 | } catch (e: ClassCastException) {
51 | throw ClassCastException(activity!!.toString() + " must implement HomeFragmentListener")
52 | }
53 | }
54 |
55 | fun fetchData() {
56 | // create Request and set the needed input
57 | val homeRequest = HomeRequest()
58 | homeRequest.isFutureTrips = true
59 | // Call the output to fetch the data
60 | output.fetchHomeData(homeRequest)
61 | }
62 |
63 | private fun createFlightListView(view: View) {
64 | val listView = view.findViewById(R.id.listOfFlights)
65 | listView.adapter = FlightListAdapter(this.requireContext(),listOfVMFlights)
66 | listView.isClickable = true
67 | listView.onItemClickListener = router
68 | }
69 |
70 | override fun displayHomeMetaData(viewModel: HomeViewModel) {
71 | Log.d(HomeFragment.TAG, "displayHomeMetaData() called with: viewModel = [$viewModel]")
72 | listOfVMFlights = viewModel.listOfFlights!!
73 | }
74 |
75 | companion object {
76 | const val TAG = "HomeFragment"
77 | }
78 | }
79 |
80 | interface HomeFragmentListener{
81 | fun startPastTripFragment(fragment: Fragment)
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/homescreen/HomeInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.homescreen
2 |
3 | import android.util.Log
4 | import com.example.kotlincleancode4android.ArrayEmptyException
5 |
6 | /**
7 | * Created by Mohanraj Karatadipalayam on 06/10/18.
8 | */
9 |
10 | interface HomeInteractorInput {
11 | fun fetchHomeData(request: HomeRequest)
12 | }
13 |
14 | class HomeInteractor : HomeInteractorInput {
15 |
16 | var output: HomePresenterInput? = null
17 | var flightWorkerInput: FlightWorkerInput? = null
18 | get() { return field ?: FlightWorker()}
19 |
20 |
21 | override fun fetchHomeData(request: HomeRequest) {
22 | Log.d(TAG, "In method fetchHomeData")
23 | val homeResponse = HomeResponse()
24 | if (request.isFutureTrips) {
25 | homeResponse.listOfFlights = flightWorkerInput?.futureFlights
26 | } else {
27 | homeResponse.listOfFlights = flightWorkerInput?.pastFlights
28 | }
29 | //TODO : Add failure case here
30 | if (null == homeResponse.listOfFlights || homeResponse.listOfFlights!!.isEmpty()) {
31 | throw ArrayEmptyException("Empty Flight List")
32 | }
33 |
34 | output?.presentHomeMetaData(homeResponse)
35 | }
36 |
37 | companion object {
38 | const val TAG = "HomeInteractor"
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/homescreen/HomeModels.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.homescreen
2 |
3 | import com.example.kotlincleancode4android.FlightModel
4 | import com.example.kotlincleancode4android.FlightViewModel
5 | import java.util.ArrayList
6 |
7 | /**
8 | * Created by Mohanraj Karatadipalayam on 06/10/18.
9 | */
10 | data class HomeViewModel(
11 | // TODO - filter to have only the needed data
12 | var listOfFlights: ArrayList? = null
13 | )
14 | data class HomeRequest( var isFutureTrips: Boolean = false)
15 | data class HomeResponse(var listOfFlights: ArrayList? = null)
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/homescreen/HomePresenter.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.homescreen
2 |
3 | import android.util.Log
4 | import com.example.kotlincleancode4android.FlightViewModel
5 | import java.lang.ref.WeakReference
6 | import java.util.Calendar
7 | import java.util.concurrent.TimeUnit
8 |
9 | /**
10 | * Created by Mohanraj Karatadipalayam on 06/10/18.
11 | */
12 | interface HomePresenterInput {
13 | fun presentHomeMetaData(response: HomeResponse)
14 | }
15 |
16 | class HomePresenter : HomePresenterInput {
17 |
18 | var output: WeakReference? = null
19 | var currentTime: Calendar? = null
20 | get() = if (field == null) Calendar.getInstance() else field
21 |
22 | override fun presentHomeMetaData(response: HomeResponse) {
23 | Log.d(TAG, "presentHomeMetaData() called with: response = [$response]");
24 | // Do your decoration or filtering here
25 | val homeViewModel = HomeViewModel()
26 | homeViewModel.listOfFlights = ArrayList()
27 |
28 | if (response.listOfFlights != null) {
29 |
30 | for (fm in response.listOfFlights!!) {
31 | val fvm = FlightViewModel(noOfDaysToFly = null,
32 | departureCity = fm.departureCity,
33 | arrivalCity = fm.arrivalCity,
34 | flightName = fm.flightName,
35 | startingTime = fm.startingTime,
36 | departureTime = fm.departureTime,
37 | arrivalTime = fm.arrivalTime)
38 |
39 | // Decoration
40 | val startingTime = getCalendar(fvm.startingTime)
41 | startingTime?.let {
42 | val startTime = it
43 | currentTime?.let {
44 | val daysDiff = getDaysDiff(it.timeInMillis, startTime.timeInMillis)
45 | setDaysFlyDecorationText(fvm, daysDiff, it.timeInMillis, startTime.timeInMillis)
46 | }
47 | }
48 | homeViewModel.listOfFlights?.add(fvm)
49 | }
50 | output?.get()?.displayHomeMetaData(homeViewModel)
51 | }
52 | }
53 |
54 | private fun setDaysFlyDecorationText(fvm: FlightViewModel, daysDiff: Long, startTime: Long, endTime: Long) {
55 | if (endTime > startTime) {
56 | fvm.noOfDaysToFly = "You have $daysDiff days to fly"
57 | } else {
58 | // daysDiff =-daysDiff;
59 | fvm.noOfDaysToFly = "It has been $daysDiff days since you flew"
60 | }
61 | }
62 |
63 | private fun getCalendar(date: String?): Calendar? {
64 | // Date should be in the format YYYY/MM/DD if not return
65 | if (date != null && !date.isEmpty() && date.length == 10) {
66 | val year = Integer.parseInt(date.substring(0, 4))
67 | val month = Integer.parseInt(date.substring(5, 7))
68 | val day = Integer.parseInt(date.substring(8, 10))
69 | val startingTime = Calendar.getInstance()
70 | startingTime.set(year, month - 1, day, 0, 0, 0)
71 | return startingTime
72 | }
73 | return null
74 | }
75 |
76 | private fun getDaysDiff(startTime: Long, endTime: Long): Long {
77 | val msDiff: Long = if (endTime > startTime) {
78 | endTime - startTime
79 | } else {
80 | startTime - endTime
81 | }
82 | val daysDiff = TimeUnit.MILLISECONDS.toDays(msDiff)
83 | Log.d(TAG, "diff is $daysDiff")
84 | return daysDiff
85 | }
86 |
87 | companion object {
88 | const val TAG = "HomePresenter"
89 | }
90 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/homescreen/HomeRouter.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.homescreen
2 |
3 |
4 | import android.os.Bundle
5 | import android.support.v4.app.Fragment
6 | import android.util.Log
7 | import android.view.View
8 | import android.widget.AdapterView
9 | import com.example.kotlincleancode4android.CalendarUtil
10 | import com.example.kotlincleancode4android.pastTripScreen.PastTripFragment
11 | import java.lang.ref.WeakReference
12 | import java.util.Calendar
13 |
14 | /**
15 | * Created by Mohanraj Karatadipalayam on 06/10/18.
16 | */
17 |
18 | internal interface HomeRouterInput {
19 | fun determineNextScreen(position: Int): Fragment
20 | fun passDataToNextScene(position: Int, nextFragment: Fragment)
21 | }
22 |
23 | class HomeRouter : HomeRouterInput, AdapterView.OnItemClickListener {
24 | var fragment: WeakReference? = null
25 | var currentTime: Calendar? = null
26 | get() = if (field == null) Calendar.getInstance() else field
27 |
28 |
29 | override fun determineNextScreen(position: Int): Fragment {
30 | // Based on the position or some other data decide what is the next scene
31 |
32 | val flight = fragment?.get()?.listOfVMFlights?.get(position)
33 | val startingTime = CalendarUtil.getCalendar(flight?.startingTime)
34 |
35 | return if (isFutureFlight(startingTime)) {
36 | // Intent(activity!!.get(), BoardingActivity::class.java)
37 | HomeFragment()
38 | } else {
39 | // Intent(activity!!.get(), PastTripFragment::class.java)
40 | PastTripFragment()
41 | }
42 | }
43 |
44 | override fun passDataToNextScene(position: Int, nextFragment: Fragment) {
45 | // Based on the position or some other data decide the data for the next scene
46 | val flight = fragment?.get()?.listOfVMFlights?.get(position)
47 | val args = Bundle()
48 | args.putParcelable("flight",flight)
49 | nextFragment.arguments = args
50 | }
51 |
52 | override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) {
53 | Log.d(TAG, "onItemClick() called with: parent = [$parent], view = [$view], position = [$position], id = [$id]");
54 | val nextFragment = determineNextScreen(position)
55 | passDataToNextScene(position, nextFragment)
56 | fragment?.get()?.homeFragmentListener?.startPastTripFragment(nextFragment)
57 | // TODO - Decide should we start the fragment from here or from the activity ?
58 | }
59 |
60 | private fun isFutureFlight(startingTime: Calendar): Boolean {
61 | val startTimeInMills = startingTime.timeInMillis
62 | val currentTimeInMills = currentTime?.timeInMillis
63 | currentTimeInMills?.let {
64 | return startTimeInMills >= currentTimeInMills
65 | }
66 | return false
67 | }
68 |
69 | companion object {
70 | const val TAG = "HomeRouter"
71 | }
72 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/homescreen/MainActivity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Mohanraj Karatadipalayam on 06/10/18.
3 | */
4 |
5 | package com.example.kotlincleancode4android.homescreen
6 |
7 | import android.os.Bundle
8 | import android.support.v4.app.Fragment
9 | import android.support.v7.app.AppCompatActivity
10 | import com.example.kotlincleancode4android.R
11 | import com.example.kotlincleancode4android.transact
12 |
13 |
14 | class MainActivity : AppCompatActivity(),HomeFragmentListener {
15 |
16 | override fun onCreate(savedInstanceState: Bundle?) {
17 | super.onCreate(savedInstanceState)
18 | setContentView(R.layout.activity_main)
19 | supportActionBar?.hide()
20 | showFragment(HomeFragment())
21 | }
22 |
23 | private fun showFragment(fragment: Fragment) {
24 | transact {
25 | replace(R.id.container, fragment)
26 | setCustomAnimations(R.anim.abc_fade_in, R.anim.abc_fade_out)
27 | }
28 | }
29 |
30 | override fun startPastTripFragment(fragment: Fragment) {
31 | showFragment(fragment)
32 | }
33 |
34 | companion object {
35 | const val TAG = "MainActivity"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/kotlincleancode4android/pastTripScreen/PastTripFragment.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android.pastTripScreen
2 |
3 | import android.os.Bundle
4 | import android.support.v4.app.Fragment
5 | import android.util.Log
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 | import android.widget.TextView
10 | import com.example.kotlincleancode4android.FlightModel
11 | import com.example.kotlincleancode4android.R
12 |
13 | class PastTripFragment : Fragment() {
14 |
15 | private var flightModel: FlightModel? = null
16 |
17 | private var passengerName: TextView? = null
18 | private var departureCity: TextView? = null
19 | private var arrivalCity: TextView? = null
20 | private var boardingTime: TextView? = null
21 | private var departureTime: TextView? = null
22 | private var arrivalTime: TextView? = null
23 |
24 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
25 | savedInstanceState: Bundle?): View? {
26 | // Inflate the layout for this fragment
27 | val view = inflater.inflate(R.layout.fragment_past_trip, container, false)
28 | flightModel = this.arguments?.getParcelable("flight")
29 | bindViews(view)
30 | displayTripData(flightModel)
31 | return view
32 | }
33 |
34 | private fun bindViews(view: View) {
35 | passengerName = view.findViewById(R.id.tv_passengerName)
36 | departureCity = view.findViewById(R.id.tv_departureAirport)
37 | arrivalCity = view.findViewById(R.id.tv_arrivalAirport)
38 | boardingTime = view.findViewById(R.id.tv_boardingTime)
39 | departureTime = view.findViewById(R.id.tv_departureTime)
40 | arrivalTime = view.findViewById(R.id.tv_arrivalTime)
41 | }
42 |
43 | private fun displayTripData(fightModel: FlightModel?) {
44 | Log.e(TAG, "displayBoardingData() called with: viewModel = [$fightModel]")
45 | // Deal with the data
46 | passengerName?.text = "Mr. Mohan Karats"
47 | // mFlightCode.setText(flightModel.flightName);
48 | arrivalCity?.text = flightModel?.arrivalCity
49 | arrivalTime?.text = flightModel?.arrivalTime
50 | departureCity?.text = flightModel?.departureCity
51 | departureTime?.text = flightModel?.departureTime
52 | }
53 |
54 | companion object {
55 | const val TAG = "PastTripFragment"
56 | }
57 | }
--------------------------------------------------------------------------------
/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/drawable-v24/ic_plan_landing.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_plan_takeoff.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/shape_rectangle_fill_light.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/shape_rectangle_stroke.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_boarding.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
17 |
18 |
24 |
25 |
36 |
37 |
47 |
48 |
59 |
60 |
71 |
72 |
84 |
85 |
95 |
96 |
106 |
107 |
118 |
119 |
130 |
131 |
142 |
143 |
155 |
156 |
157 |
158 |
168 |
169 |
178 |
179 |
189 |
190 |
191 |
200 |
201 |
211 |
212 |
223 |
224 |
234 |
235 |
247 |
260 |
261 |
277 |
278 |
289 |
290 |
301 |
302 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_past_trip.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
20 |
21 |
32 |
33 |
44 |
45 |
56 |
57 |
67 |
68 |
78 |
79 |
92 |
93 |
106 |
107 |
119 |
120 |
132 |
133 |
144 |
145 |
156 |
157 |
168 |
169 |
170 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/cell_trip_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
26 |
27 |
39 |
40 |
57 |
58 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
19 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_past_trip.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
20 |
21 |
32 |
33 |
44 |
45 |
56 |
57 |
67 |
68 |
78 |
79 |
92 |
93 |
106 |
107 |
119 |
120 |
132 |
133 |
144 |
145 |
156 |
157 |
168 |
169 |
--------------------------------------------------------------------------------
/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/kmmraj/KotlinCleanCode4Android/c2446d21576bfa079a1b5ff8a1bbd5bb038fda13/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kmmraj/KotlinCleanCode4Android/c2446d21576bfa079a1b5ff8a1bbd5bb038fda13/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kmmraj/KotlinCleanCode4Android/c2446d21576bfa079a1b5ff8a1bbd5bb038fda13/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kmmraj/KotlinCleanCode4Android/c2446d21576bfa079a1b5ff8a1bbd5bb038fda13/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kmmraj/KotlinCleanCode4Android/c2446d21576bfa079a1b5ff8a1bbd5bb038fda13/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kmmraj/KotlinCleanCode4Android/c2446d21576bfa079a1b5ff8a1bbd5bb038fda13/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kmmraj/KotlinCleanCode4Android/c2446d21576bfa079a1b5ff8a1bbd5bb038fda13/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kmmraj/KotlinCleanCode4Android/c2446d21576bfa079a1b5ff8a1bbd5bb038fda13/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kmmraj/KotlinCleanCode4Android/c2446d21576bfa079a1b5ff8a1bbd5bb038fda13/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kmmraj/KotlinCleanCode4Android/c2446d21576bfa079a1b5ff8a1bbd5bb038fda13/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #3f51b5
5 | #303f9f
6 | #FF4081
7 | #807F80
8 | #80ff0000
9 | #3FBAEB
10 |
11 | #7986CB
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | KotlinCleanCode4Android
3 | Trip Detail
4 | PASSENGER
5 | MR. MOHAN KARATS
6 | BLR
7 | CJB
8 | 9F 2465
9 | BOARDING TIME
10 | 02:10 PM
11 | DEPARTURE
12 | 02:40 PM
13 | 03:10 PM
14 | ARRIVAL
15 | 00:15
16 | BOARDING IN
17 | TERMINAL
18 | GATE
19 | SEAT
20 | 1
21 | 6A
22 | 01A
23 | DEPARTURE DATE
24 | 2018 Jan 15
25 | Depature Airport Code
26 | Arrival Airport Code
27 | To Airport
28 | FLIGHT
29 | PNR
30 | Z1Y2X3
31 | Flight Image
32 | Image
33 | Image Departure City
34 | Image Arrival City
35 | Image Email Button
36 | Image Take Off
37 | Image Plane Landing
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_descriptor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/kotlincleancode4android/HomeFragmentUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android
2 |
3 | import com.example.kotlincleancode4android.homescreen.HomeFragment
4 | import com.example.kotlincleancode4android.homescreen.MainActivity
5 | import com.example.kotlincleancode4android.homescreen.HomeInteractorInput
6 | import com.example.kotlincleancode4android.homescreen.HomeRequest
7 | import org.junit.Assert
8 | import org.junit.Test
9 | import org.junit.runner.RunWith
10 | import org.robolectric.Robolectric
11 | import org.robolectric.RobolectricTestRunner
12 |
13 | /**
14 | * Created by Mohanraj Karatadipalayam on 07/10/18.
15 | */
16 | @RunWith(RobolectricTestRunner::class)
17 | class HomeFragmentUnitTest {
18 |
19 | @Test
20 | fun mainActivity_ShouldNOT_be_Null() {
21 | // Given
22 | val activity = Robolectric.setupActivity(MainActivity::class.java)
23 | // When
24 |
25 | // Then
26 | Assert.assertNotNull(activity)
27 | }
28 |
29 | @Test
30 | fun fragment_ShouldNOT_be_Null() {
31 | // Given
32 | val fragment = HomeFragment()
33 | // When
34 |
35 | // Then
36 | Assert.assertNotNull(fragment)
37 | }
38 |
39 | @Test
40 | fun onCreateView_shouldCall_fetchHomeData() {
41 | // Given
42 | val fragmentOutputSpy = HomeFragmentOutputSpy()
43 |
44 | // It must have called the onCreateView earlier,
45 | // we are injecting the mock and calling the fetchData to test our condition
46 | val homeFragment = HomeFragment()
47 | homeFragment.output = fragmentOutputSpy
48 |
49 | // When
50 | homeFragment.fetchData()
51 |
52 | // Then
53 | Assert.assertTrue(fragmentOutputSpy.fetchHomeDataIsCalled)
54 | }
55 |
56 | @Test
57 | fun onCreateView_Calls_fetchHomeMetaData_withCorrectData() {
58 | // Given
59 | val fragmentOutputSpy = HomeFragmentOutputSpy()
60 | val homeFragment = HomeFragment()
61 | homeFragment.output = fragmentOutputSpy
62 |
63 | // When
64 | homeFragment.fetchData()
65 |
66 | // Then
67 | Assert.assertNotNull(homeFragment)
68 | Assert.assertTrue(fragmentOutputSpy.homeRequestCopy.isFutureTrips)
69 | }
70 |
71 | private inner class HomeFragmentOutputSpy : HomeInteractorInput {
72 |
73 | var fetchHomeDataIsCalled = false
74 | lateinit var homeRequestCopy: HomeRequest
75 |
76 | override fun fetchHomeData(request: HomeRequest) {
77 | fetchHomeDataIsCalled = true
78 | homeRequestCopy = request
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/kotlincleancode4android/HomeInteractorUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android
2 |
3 | import com.example.kotlincleancode4android.homescreen.*
4 | import org.junit.Assert
5 | import org.junit.Test
6 | import org.junit.runner.RunWith
7 | import org.robolectric.RobolectricTestRunner
8 | import java.util.ArrayList
9 |
10 | /**
11 | * Created by Mohanraj Karatadipalayam on 07/10/18.
12 | */
13 | @RunWith(RobolectricTestRunner::class)
14 | class HomeInteractorUnitTest {
15 |
16 | @Test
17 | fun fetchHomeMetaData_with_validInput_shouldCall_presentHomeMetaData() {
18 | // Given
19 | val homeInteractor = HomeInteractor()
20 | val homeRequest = HomeRequest()
21 | homeRequest.isFutureTrips = true
22 | val homePresenterInputSpy = HomePresenterInputSpy()
23 | homeInteractor.output = homePresenterInputSpy
24 | // When
25 | homeInteractor.fetchHomeData(homeRequest)
26 |
27 | // Then
28 | Assert.assertTrue("When the valid input is passed to HomeInteractor "
29 | + "Then presentHomeMetaData should be called",
30 | homePresenterInputSpy.presentHomeMetaDataIsCalled)
31 | }
32 |
33 | @Test
34 | fun fetchHomeMetaData_with_validInput_FutureTrip_shouldCall_Worker_getFutureTrips() {
35 | // Given
36 | val homeInteractor = HomeInteractor()
37 | val homeRequest = HomeRequest()
38 | homeRequest.isFutureTrips = true
39 |
40 | // Setup TestDoubles
41 | homeInteractor.output = HomePresenterInputSpy()
42 | val flightWorkerInputSpy = FlightWorkerInputSpy()
43 | homeInteractor.flightWorkerInput = flightWorkerInputSpy
44 |
45 | // When
46 | homeInteractor.fetchHomeData(homeRequest)
47 |
48 | // Then
49 | Assert.assertTrue("When the input is passed to HomeInteractor is FutureTrip"
50 | + "Then getFutureFlights should be called in Worker",
51 | flightWorkerInputSpy.isGetFutureFlightsMethodCalled)
52 | }
53 |
54 | @Test
55 | fun fetchHomeMetaData_with_validInput_PastTrip_shouldCall_Worker_getPastTrips() {
56 | // Given
57 | val homeInteractor = HomeInteractor()
58 | val homeRequest = HomeRequest()
59 | homeRequest.isFutureTrips = false
60 |
61 | // Setup TestDoubles
62 | homeInteractor.output = HomePresenterInputSpy()
63 | val flightWorkerInputSpy = FlightWorkerInputSpy()
64 | homeInteractor.flightWorkerInput = flightWorkerInputSpy
65 |
66 | // When
67 | homeInteractor.fetchHomeData(homeRequest)
68 |
69 | // Then
70 | Assert.assertTrue("When the input is passed to HomeInteractor is FutureTrip"
71 | + "Then getFutureFlights should be called in Worker",
72 | flightWorkerInputSpy.isGetPastFlightsMethodCalled)
73 | }
74 |
75 | @Test(expected = ArrayEmptyException::class)
76 | fun fetchHomeMetaData_fetchingNull_shouldThrowArrayEmptyException() {
77 | // Given
78 | val homeInteractor = HomeInteractor()
79 | val homeRequest = HomeRequest()
80 | homeRequest.isFutureTrips = false
81 |
82 | // Setup TestDoubles
83 | homeInteractor.output = HomePresenterInputSpy()
84 | val flightWorkerInputReturnNullSpy = FlightWorkerInputReturnNullSpy()
85 | homeInteractor.flightWorkerInput = flightWorkerInputReturnNullSpy
86 |
87 | // When
88 | homeInteractor.fetchHomeData(homeRequest)
89 |
90 | // Then
91 | // Check for ArrayEmptyException -- See this method Annotation
92 | }
93 |
94 | private inner class HomePresenterInputSpy: HomePresenterInput {
95 |
96 | internal var presentHomeMetaDataIsCalled = false
97 | internal var homeResponseCopy: HomeResponse? = null
98 | override fun presentHomeMetaData(response: HomeResponse) {
99 | presentHomeMetaDataIsCalled = true
100 | homeResponseCopy = response
101 | }
102 | }
103 |
104 | private inner class FlightWorkerInputSpy : FlightWorkerInput {
105 |
106 | internal var isGetFutureFlightsMethodCalled = false
107 | internal var isGetPastFlightsMethodCalled = false
108 |
109 | override val futureFlights: ArrayList
110 | get() {
111 | isGetFutureFlightsMethodCalled = true
112 | return flightModels
113 | }
114 |
115 | override val pastFlights: ArrayList
116 | get() {
117 | isGetPastFlightsMethodCalled = true
118 | return flightModels
119 | }
120 |
121 | private val flightModels: ArrayList
122 | get() {
123 | val flightsList = ArrayList()
124 | val flight1 = FlightModel(
125 | flightName = "9Z 231",
126 | startingTime = "2018/10/31",
127 | departureCity = "BLR",
128 | arrivalCity = "CJB",
129 | departureTime = "18:10",
130 | arrivalTime = "19:00"
131 | )
132 | flightsList.add(flight1)
133 | return flightsList
134 | }
135 | }
136 |
137 | private inner class FlightWorkerInputReturnNullSpy : FlightWorkerInput {
138 |
139 | internal var isGetFlightsMethodCalled = false
140 | internal var isGetPastFlightsMethodCalled = false
141 |
142 | override val futureFlights: ArrayList = arrayListOf()
143 | get() {
144 | isGetFlightsMethodCalled = true
145 | return field
146 | }
147 |
148 | override val pastFlights: ArrayList = arrayListOf()
149 | get() {
150 | isGetPastFlightsMethodCalled = true
151 | return field
152 | }
153 | }
154 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/example/kotlincleancode4android/HomePresenterUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android
2 |
3 | //import android.util.Log
4 | import android.util.Log
5 | import com.example.kotlincleancode4android.homescreen.*
6 | import org.junit.Assert
7 | import org.junit.Test
8 | import org.junit.runner.RunWith
9 | import org.robolectric.RobolectricTestRunner
10 | import java.lang.ref.WeakReference
11 | import java.util.*
12 |
13 | /**
14 | * Created by Mohanraj Karatadipalayam on 07/10/18.
15 | */
16 | @RunWith(RobolectricTestRunner::class)
17 | class HomePresenterUnitTest {
18 |
19 | @Test
20 | fun presentHomeMetaData_with_validInput_shouldCall_displayHomeData() {
21 | // Given
22 | val homePresenter = HomePresenter()
23 | val homeResponse = HomeResponse()
24 | homeResponse.listOfFlights = FlightWorker().futureFlights
25 |
26 | val homeFragmentInputSpy = HomeFragmentInputSpy()
27 | homePresenter.output = WeakReference(homeFragmentInputSpy)
28 |
29 | // When
30 | homePresenter.presentHomeMetaData(homeResponse)
31 |
32 | // Then
33 | Assert.assertTrue("When the valid input is passed to HomePresenter " +
34 | "Then displayHomeData should be called",
35 | homeFragmentInputSpy.isDisplayHomeMetaDataCalled)
36 | }
37 |
38 | @Test
39 | fun presentHomeMetaData_with_inValidInput_shouldNotCall_displayHomeMetaData() {
40 | // Given
41 | val homePresenter = HomePresenter()
42 | val homeResponse = HomeResponse()
43 | homeResponse.listOfFlights = null
44 |
45 | val homeFragmentInputSpy = HomeFragmentInputSpy()
46 | homePresenter.output = WeakReference(homeFragmentInputSpy)
47 |
48 | // When
49 | homePresenter.presentHomeMetaData(homeResponse)
50 |
51 | // Then
52 | Assert.assertFalse("When the valid input is passed to HomePresenter " +
53 | "Then displayHomeMetaData should NOT be called",
54 | homeFragmentInputSpy.isDisplayHomeMetaDataCalled)
55 | }
56 |
57 | @Test
58 | fun verify_HomePresenter_getDaysDiff_is_CalculatedCorrectly_ForFutureTrips() {
59 | // Given
60 | val homePresenter = HomePresenter()
61 | val homeResponse = HomeResponse()
62 |
63 | val flightsList = ArrayList()
64 |
65 | val flight1 = FlightModel(
66 | flightName = "9Z 231",
67 | startingTime = "2017/12/31",
68 | departureCity = "BLR",
69 | arrivalCity = "CJB",
70 | departureTime = "18:10",
71 | arrivalTime = "19:00"
72 | )
73 | flightsList.add(flight1)
74 | homeResponse.listOfFlights = flightsList
75 |
76 | val homeActivityInputSpy = HomeFragmentInputSpy()
77 | homePresenter.output = WeakReference(homeActivityInputSpy)
78 |
79 | // When
80 | val currentTime = Calendar.getInstance()
81 | currentTime.set(2017, 5, 30, 0, 0, 0)
82 | homePresenter.currentTime = currentTime
83 | homePresenter.presentHomeMetaData(homeResponse)
84 |
85 | // Then
86 | // "It has been " + daysDiff + " days since you flew";
87 | val expectedText = "You have " + "184" + " days to fly"
88 | val actualText = homeActivityInputSpy.homeViewModelCopy.listOfFlights?.get(0)?.noOfDaysToFly
89 | Assert.assertEquals("When current date is 2016/10/12 & " +
90 | "Flying Date is 2016/10/31 Then no of days should be 19", expectedText, actualText)
91 | }
92 |
93 | @Test
94 | fun verify_HomePresenter_getDaysDiff_isCalculatedCorrectly_ForPastTrips() {
95 | // Given
96 | val homePresenter = HomePresenter()
97 | val homeResponse = HomeResponse()
98 |
99 | val flightsList = ArrayList()
100 |
101 | val flight1 = FlightModel(
102 | flightName = "9Z 231",
103 | startingTime = "2016/10/01",
104 | departureCity = "BLR",
105 | arrivalCity = "CJB",
106 | departureTime = "18:10",
107 | arrivalTime = "19:00"
108 | )
109 |
110 | flightsList.add(flight1)
111 |
112 | homeResponse.listOfFlights = flightsList
113 |
114 | val homeFragmentInputSpy = HomeFragmentInputSpy()
115 | homePresenter.output = WeakReference(homeFragmentInputSpy)
116 |
117 | // When
118 | val currentTime = Calendar.getInstance()
119 | // currentTime.set(2017,5,30,0,0,0);
120 | currentTime.set(2017, 5, 30)
121 | Log.d(TAG, "verify_HomePresenter_getDaysDiff_isCalculatedCorrectly_ForPastTrips: "
122 | + currentTime.toString())
123 | homePresenter.currentTime = currentTime
124 | homePresenter.presentHomeMetaData(homeResponse)
125 |
126 | // Then
127 | // "It has been " + daysDiff + " days since you flew";
128 | val expectedText = "It has been " + 272 + " days since you flew"
129 | val actualText = homeFragmentInputSpy.homeViewModelCopy.listOfFlights?.get(0)?.noOfDaysToFly
130 | Assert.assertEquals("When current date is 2017/05/30 & " +
131 | "Flying Date is 2016/10/01 Then no of days should be 271", expectedText, actualText)
132 | }
133 |
134 | private inner class HomeFragmentInputSpy : HomeFragmentInput {
135 | var isDisplayHomeMetaDataCalled = false
136 | lateinit var homeViewModelCopy: HomeViewModel
137 | override fun displayHomeMetaData(viewModel: HomeViewModel) {
138 | isDisplayHomeMetaDataCalled = true
139 | homeViewModelCopy = viewModel
140 | }
141 | }
142 |
143 | companion object {
144 | const val TAG = "HomePresenterUnitTest"
145 | }
146 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/example/kotlincleancode4android/HomeRouterUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.kotlincleancode4android
2 |
3 | import com.example.kotlincleancode4android.boardingScreen.BoardingActivity
4 | import com.example.kotlincleancode4android.homescreen.HomeFragment
5 | import com.example.kotlincleancode4android.homescreen.MainActivity
6 | import com.example.kotlincleancode4android.homescreen.HomeRouter
7 | import com.example.kotlincleancode4android.pastTripScreen.PastTripFragment
8 | import org.junit.Assert
9 | import org.junit.Test
10 | import org.junit.runner.RunWith
11 | import org.robolectric.Robolectric
12 | import org.robolectric.RobolectricTestRunner
13 | import java.lang.ref.WeakReference
14 | import java.util.*
15 |
16 | /**
17 | * Created by Mohanraj Karatadipalayam on 08/10/18.
18 | */
19 | @RunWith(RobolectricTestRunner::class)
20 | class HomeRouterUnitTest {
21 |
22 | @Test
23 | fun homeRouter_determineNextScreen_when_futureTripIs_Input() {
24 | // Given
25 | val homeRouter = HomeRouter()
26 | val flightList = ArrayList()
27 | val flight1 = FlightViewModel(
28 | flightName = "9Z 231",
29 | startingTime = "2017/12/31",
30 | departureCity = "BLR",
31 | arrivalCity = "CJB",
32 | departureTime = "18:10",
33 | arrivalTime = "19:00"
34 | )
35 | flightList.add(flight1)
36 |
37 | val flight2 = FlightViewModel(
38 | flightName = "9Z 222",
39 | startingTime = "2016/12/31",
40 | departureCity = "BLR",
41 | arrivalCity = "CJB",
42 | departureTime = "18:10",
43 | arrivalTime = "19:00"
44 | )
45 | flightList.add(flight2)
46 |
47 | val homeActivity = Robolectric.setupActivity(MainActivity::class.java)
48 | val homeFragment = HomeFragment()
49 | homeFragment.listOfVMFlights = flightList
50 | homeFragment.router = homeRouter
51 | homeRouter.fragment = WeakReference(homeFragment)
52 |
53 | val currentTime = Calendar.getInstance()
54 | currentTime.set(2017, 5, 30, 0, 0, 0)
55 | homeRouter.currentTime = currentTime
56 |
57 | // When - Future Trip is Input
58 | val fragment = homeRouter.determineNextScreen(0)
59 |
60 | // Then
61 | val targetFragmentName = fragment.javaClass.name
62 | Assert.assertEquals("When the future travel date is passed to HomeRouter" +
63 | " Then next Fragment should be BoardingFragment",
64 | targetFragmentName,
65 | BoardingActivity::class.java.name)
66 | }
67 |
68 | @Test
69 | fun homeRouter_determineNextScreen_when_pastTripIs_Input() {
70 | // Given
71 | val homeRouter = HomeRouter()
72 | val flightList = ArrayList()
73 | val flight1 = FlightViewModel(
74 | flightName = "9Z 231",
75 | startingTime = "2017/12/31",
76 | departureCity = "BLR",
77 | arrivalCity = "CJB",
78 | departureTime = "18:10",
79 | arrivalTime = "19:00"
80 | )
81 | flightList.add(flight1)
82 |
83 | val flight2 = FlightViewModel(
84 | flightName = "9Z 222",
85 | startingTime = "2016/12/31",
86 | departureCity = "BLR",
87 | arrivalCity = "CJB",
88 | departureTime = "18:10",
89 | arrivalTime = "19:00"
90 | )
91 | flightList.add(flight2)
92 |
93 | val homeFragment = HomeFragment()
94 | homeFragment.listOfVMFlights = flightList
95 | homeFragment.router = homeRouter
96 | homeRouter.fragment = WeakReference(homeFragment)
97 |
98 | val currentTime = Calendar.getInstance()
99 | currentTime.set(2017, 5, 30, 0, 0, 0)
100 | homeRouter.currentTime = currentTime
101 |
102 | // When - Past Trip is Input
103 | val fragment = homeRouter.determineNextScreen(1)
104 |
105 | // Then
106 | val targetFragmentName = fragment.javaClass.name
107 | Assert.assertEquals("When the past travel date is passed to HomeRouter"
108 | + " Then next Intent should be PastTripFragment",
109 | targetFragmentName, PastTripFragment::class.java.name)
110 | }
111 |
112 | companion object {
113 | const val TAG = "HomeRouterUnitTest"
114 | }
115 | }
--------------------------------------------------------------------------------
/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.2.61'
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.2.0'
11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/doc/images/kotlinCleanCode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kmmraj/KotlinCleanCode4Android/c2446d21576bfa079a1b5ff8a1bbd5bb038fda13/doc/images/kotlinCleanCode.png
--------------------------------------------------------------------------------
/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 |
15 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------