├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── softwareman │ │ └── mvpkotlin │ │ └── ExampleInstrumentationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── softwareman │ │ │ └── mvpkotlin │ │ │ ├── model │ │ │ └── User.kt │ │ │ ├── presenter │ │ │ ├── CreateUserPresenterImpl.kt │ │ │ ├── Presenter.kt │ │ │ └── UserDetailsPresenterImpl.kt │ │ │ ├── utils │ │ │ └── ViewExtensions.kt │ │ │ └── view │ │ │ ├── Constants.kt │ │ │ ├── View.kt │ │ │ └── activity │ │ │ ├── MainActivity.kt │ │ │ └── UserDetailsActivity.kt │ └── res │ │ ├── layout │ │ ├── activity_main.xml │ │ └── activity_user_details.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── softwareman │ └── mvpkotlin │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .idea/ 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MVPKotlin 2 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.0.1-2' 3 | repositories { 4 | jcenter() 5 | } 6 | dependencies { 7 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 8 | classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | 16 | apply plugin: 'kotlin-android-extensions' 17 | apply plugin: 'com.android.application' 18 | apply plugin: 'kotlin-android' 19 | 20 | 21 | android { 22 | compileSdkVersion 23 23 | buildToolsVersion "23.0.3" 24 | defaultConfig { 25 | applicationId "com.softwareman.mvpkotlin" 26 | minSdkVersion 17 27 | targetSdkVersion 23 28 | versionCode 1 29 | versionName "1.0" 30 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 31 | } 32 | buildTypes { 33 | release { 34 | minifyEnabled false 35 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 36 | } 37 | } 38 | sourceSets { 39 | main.java.srcDirs += 'src/main/kotlin' 40 | } 41 | } 42 | 43 | dependencies { 44 | compile fileTree(dir: 'libs', include: ['*.jar']) 45 | compile 'com.android.support:appcompat-v7:23.4.0' 46 | compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha1' 47 | testCompile 'junit:junit:4.12' 48 | androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2' 49 | androidTestCompile 'com.android.support.test:runner:0.5' 50 | androidTestCompile 'com.android.support:support-annotations:23.4.0' 51 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 52 | compile "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" 53 | compile 'org.jetbrains.anko:anko-sdk15:0.8' 54 | compile 'org.jetbrains.anko:anko-support-v4:0.8.3' 55 | compile 'org.jetbrains.anko:anko-appcompat-v7:0.8.3' 56 | } 57 | repositories { 58 | mavenCentral() 59 | } 60 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/astalos/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/softwareman/mvpkotlin/ExampleInstrumentationTest.java: -------------------------------------------------------------------------------- 1 | package com.softwareman.mvpkotlin; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.filters.MediumTest; 6 | import android.support.test.runner.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | 12 | import static org.junit.Assert.*; 13 | 14 | /** 15 | * Instrumentation test, which will execute on an Android device. 16 | * 17 | * @see Testing documentation 18 | */ 19 | @MediumTest 20 | @RunWith(AndroidJUnit4.class) 21 | public class ExampleInstrumentationTest { 22 | @Test 23 | public void useAppContext() throws Exception { 24 | // Context of the app under test. 25 | Context appContext = InstrumentationRegistry.getTargetContext(); 26 | 27 | assertEquals("com.softwareman.mvpkotlin", appContext.getPackageName()); 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/softwareman/mvpkotlin/model/User.kt: -------------------------------------------------------------------------------- 1 | package com.softwareman.mvpkotlin.model 2 | 3 | import android.os.Parcel 4 | import android.os.Parcelable 5 | 6 | data class User(val name: String, val surname: String) : Parcelable{ 7 | constructor(source: Parcel): this(source.readString(), source.readString()) 8 | 9 | override fun describeContents(): Int { 10 | return 0 11 | } 12 | 13 | override fun writeToParcel(dest: Parcel?, flags: Int) { 14 | dest?.writeString(name) 15 | dest?.writeString(surname) 16 | } 17 | 18 | companion object { 19 | @JvmField final val CREATOR: Parcelable.Creator = object : Parcelable.Creator { 20 | override fun createFromParcel(source: Parcel): User { 21 | return User(source) 22 | } 23 | 24 | override fun newArray(size: Int): Array { 25 | return arrayOfNulls(size) 26 | } 27 | } 28 | } 29 | } 30 | 31 | enum class UserError { 32 | EMPTY_NAME, 33 | EMPTY_SURNAME, 34 | NO_ERROR 35 | } 36 | 37 | object UserStore { 38 | fun saveUser(user: User){ 39 | //Save user somewhere: Database, SharedPreferences, send to web... 40 | } 41 | } 42 | 43 | object UserValidator { 44 | 45 | fun validateUser(user: User): UserError { 46 | with(user){ 47 | if(name.isNullOrEmpty()) return UserError.EMPTY_NAME 48 | if(surname.isNullOrEmpty()) return UserError.EMPTY_SURNAME 49 | } 50 | 51 | return UserError.NO_ERROR 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/softwareman/mvpkotlin/presenter/CreateUserPresenterImpl.kt: -------------------------------------------------------------------------------- 1 | package com.softwareman.mvpkotlin.presenter 2 | 3 | import com.softwareman.mvpkotlin.model.User 4 | import com.softwareman.mvpkotlin.model.UserError 5 | import com.softwareman.mvpkotlin.model.UserStore 6 | import com.softwareman.mvpkotlin.model.UserValidator 7 | import com.softwareman.mvpkotlin.view.CreateUserView 8 | 9 | class CreateUserPresenterImpl(override var view: CreateUserView?): CreateUserPresenter { 10 | 11 | override fun saveUser(name: String, surname: String) { 12 | val user = User(name, surname) 13 | when(UserValidator.validateUser(user)){ 14 | UserError.EMPTY_NAME -> view?.showEmptyNameError() 15 | UserError.EMPTY_SURNAME -> view?.showEmptySurnameError() 16 | UserError.NO_ERROR -> { 17 | UserStore.saveUser(user) 18 | view?.showUserSaved() 19 | view?.showUserDetails(user) 20 | } 21 | } 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/softwareman/mvpkotlin/presenter/Presenter.kt: -------------------------------------------------------------------------------- 1 | package com.softwareman.mvpkotlin.presenter 2 | 3 | import com.softwareman.mvpkotlin.model.User 4 | import com.softwareman.mvpkotlin.view.View 5 | 6 | interface Presenter { 7 | var view: T? 8 | 9 | fun onDestroy(){ 10 | view = null 11 | } 12 | } 13 | 14 | interface CreateUserPresenter: Presenter { 15 | fun saveUser(name: String, surname: String) 16 | } 17 | 18 | interface UserDetailsPresenter: Presenter { 19 | var user: User? 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/softwareman/mvpkotlin/presenter/UserDetailsPresenterImpl.kt: -------------------------------------------------------------------------------- 1 | package com.softwareman.mvpkotlin.presenter 2 | 3 | import com.softwareman.mvpkotlin.model.User 4 | import com.softwareman.mvpkotlin.view.UserDetailsView 5 | 6 | class UserDetailsPresenterImpl(override var view: UserDetailsView?): UserDetailsPresenter { 7 | override var user: User? = null 8 | set(value) { 9 | field = value 10 | if(field != null){ 11 | view?.showUserDetails(field!!) 12 | } else { 13 | view?.showNoUserError() 14 | } 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/softwareman/mvpkotlin/utils/ViewExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.softwareman.mvpkotlin.utils 2 | 3 | import android.widget.EditText 4 | 5 | fun EditText.textValue(): String{ 6 | return text.toString() 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/softwareman/mvpkotlin/view/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.softwareman.mvpkotlin.view 2 | 3 | val USER_KEY = "user" 4 | -------------------------------------------------------------------------------- /app/src/main/java/com/softwareman/mvpkotlin/view/View.kt: -------------------------------------------------------------------------------- 1 | package com.softwareman.mvpkotlin.view 2 | 3 | import com.softwareman.mvpkotlin.model.User 4 | 5 | interface View 6 | 7 | interface CreateUserView: View { 8 | fun showEmptyNameError() /* show error when name is empty */ 9 | fun showEmptySurnameError() /* show error when surname is empty */ 10 | fun showUserSaved() /* show user saved info */ 11 | fun showUserDetails(user: User) /* show user details */ 12 | } 13 | 14 | interface UserDetailsView: View { 15 | fun showUserDetails(user: User) 16 | fun showNoUserError() 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/softwareman/mvpkotlin/view/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.softwareman.mvpkotlin.view.activity 2 | 3 | import android.os.Bundle 4 | import android.support.v7.app.AppCompatActivity 5 | import com.softwareman.mvpkotlin.R 6 | import com.softwareman.mvpkotlin.model.User 7 | import com.softwareman.mvpkotlin.presenter.CreateUserPresenter 8 | import com.softwareman.mvpkotlin.presenter.CreateUserPresenterImpl 9 | import com.softwareman.mvpkotlin.utils.textValue 10 | import com.softwareman.mvpkotlin.view.CreateUserView 11 | import com.softwareman.mvpkotlin.view.USER_KEY 12 | import kotlinx.android.synthetic.main.activity_main.* 13 | import org.jetbrains.anko.startActivity 14 | import org.jetbrains.anko.toast 15 | 16 | 17 | class MainActivity : AppCompatActivity(), CreateUserView { 18 | 19 | private val presenter: CreateUserPresenter by lazy { 20 | CreateUserPresenterImpl(this) 21 | } 22 | 23 | override fun onCreate(savedInstanceState: Bundle?) { 24 | super.onCreate(savedInstanceState) 25 | setContentView(R.layout.activity_main) 26 | 27 | saveUserBtn.setOnClickListener{ 28 | presenter.saveUser(userName.textValue(), userSurname.textValue()) /*use of textValue() extension, mentioned earlier */ 29 | } 30 | } 31 | 32 | override fun showEmptyNameError() { 33 | userName.error = getString(R.string.name_empty_error) /* it's equal to userName.setError() - Kotlin allows to use property */ 34 | } 35 | 36 | override fun showEmptySurnameError() { 37 | userSurname.error = getString(R.string.surname_empty_error) 38 | } 39 | 40 | override fun showUserSaved() { 41 | toast(R.string.user_saved) /* anko extension - equal to Toast.makeText(this, R.string.user_saved, Toast.LENGTH_LONG) */ 42 | } 43 | 44 | override fun showUserDetails(user: User) { 45 | startActivity(USER_KEY to user) /* anko extension - starts UserDetailsActivity and pass user as USER_KEY in intent */ 46 | } 47 | 48 | override fun onDestroy() { 49 | presenter.onDestroy() 50 | super.onDestroy() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/softwareman/mvpkotlin/view/activity/UserDetailsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.softwareman.mvpkotlin.view.activity 2 | 3 | import android.os.Bundle 4 | import android.support.v7.app.AppCompatActivity 5 | import com.softwareman.mvpkotlin.R 6 | import com.softwareman.mvpkotlin.model.User 7 | import com.softwareman.mvpkotlin.presenter.UserDetailsPresenter 8 | import com.softwareman.mvpkotlin.presenter.UserDetailsPresenterImpl 9 | import com.softwareman.mvpkotlin.view.USER_KEY 10 | import com.softwareman.mvpkotlin.view.UserDetailsView 11 | import kotlinx.android.synthetic.main.activity_user_details.* 12 | import org.jetbrains.anko.toast 13 | 14 | class UserDetailsActivity: AppCompatActivity(), UserDetailsView { 15 | 16 | private val presenter: UserDetailsPresenter by lazy { 17 | UserDetailsPresenterImpl(this) 18 | } 19 | 20 | override fun onCreate(savedInstanceState: Bundle?) { 21 | super.onCreate(savedInstanceState) 22 | setContentView(R.layout.activity_user_details) 23 | 24 | val user = intent.getParcelableExtra(USER_KEY) 25 | presenter.user = user 26 | } 27 | 28 | override fun showUserDetails(user: User) { 29 | userFullName.text = "${user.name} ${user.surname}" 30 | } 31 | 32 | override fun showNoUserError() { 33 | toast(R.string.no_user_error) 34 | finish() 35 | } 36 | 37 | override fun onDestroy() { 38 | presenter.onDestroy() 39 | super.onDestroy(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | 27 | 28 |