diff --git a/app/build.gradle b/app/build.gradle
index 7586326..b455807 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,14 +1,16 @@
apply plugin: 'com.android.application'
-apply plugin: 'com.neenbedankt.android-apt'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
android {
- compileSdkVersion 23
- buildToolsVersion "23.0.2"
+ compileSdkVersion 27
+ buildToolsVersion "27.0.3"
defaultConfig {
applicationId "io.caster.simplemvp"
minSdkVersion 16
- targetSdkVersion 23
+ targetSdkVersion 27
versionCode 1
versionName "1.0"
}
@@ -20,16 +22,42 @@ android {
}
}
+kotlin {
+ experimental {
+ coroutines "enable"
+ }
+}
+
+androidExtensions {
+ experimental true
+}
+
+kapt {
+ generateStubs true
+}
+
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- testCompile 'junit:junit:4.12'
- testCompile "org.mockito:mockito-core:1.10.19"
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ // Test dependencies
+ testImplementation 'junit:junit:4.12'
+ testImplementation 'org.mockito:mockito-core:2.16.0'
+
+ // Kotlin dependencies
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlinVer"
+ implementation "org.jetbrains.anko:anko:$ankoVer"
+ implementation "org.jetbrains.anko:anko-design:$ankoVer"
- compile 'com.android.support:appcompat-v7:23.1.1'
- compile 'com.android.support:design:23.1.1'
+ // Support Library dependencies
+ implementation 'com.android.support:appcompat-v7:27.1.0'
+ implementation 'com.android.support:design:27.1.0'
+ implementation 'com.android.support:support-v4:27.1.0'
- compile 'com.jakewharton:butterknife:7.0.1'
- compile 'com.google.dagger:dagger:2.0.2'
- apt 'com.google.dagger:dagger-compiler:2.0.2'
- provided 'javax.annotation:jsr250-api:1.0'
+ // Dagger 2 dependencies
+ implementation 'com.google.dagger:dagger:2.15'
+ implementation 'com.google.dagger:dagger-android:2.15'
+ implementation 'com.google.dagger:dagger-android-support:2.15'
+ kapt 'com.google.dagger:dagger-compiler:2.15'
+ kapt 'com.google.dagger:dagger-android-processor:2.15'
+ compileOnly 'javax.annotation:jsr250-api:1.0'
}
diff --git a/app/src/androidTest/java/io/caster/simplemvp/ApplicationTest.java b/app/src/androidTest/java/io/caster/simplemvp/ApplicationTest.java
deleted file mode 100644
index 8810cfe..0000000
--- a/app/src/androidTest/java/io/caster/simplemvp/ApplicationTest.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package io.caster.simplemvp;
-
-import android.app.Application;
-import android.test.ApplicationTestCase;
-
-/**
- * Testing Fundamentals
- */
-public class ApplicationTest extends ApplicationTestCase {
- public ApplicationTest() {
- super(Application.class);
- }
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/io/caster/simplemvp/ApplicationTest.kt b/app/src/androidTest/java/io/caster/simplemvp/ApplicationTest.kt
new file mode 100644
index 0000000..06b883b
--- /dev/null
+++ b/app/src/androidTest/java/io/caster/simplemvp/ApplicationTest.kt
@@ -0,0 +1,9 @@
+package io.caster.simplemvp
+
+import android.app.Application
+import android.test.ApplicationTestCase
+
+/**
+ * [Testing Fundamentals](http://d.android.com/tools/testing/testing_android.html)
+ */
+class ApplicationTest : ApplicationTestCase(Application::class.java)
\ No newline at end of file
diff --git a/app/src/main/java/io/caster/simplemvp/AppComponent.java b/app/src/main/java/io/caster/simplemvp/AppComponent.java
deleted file mode 100644
index 103a29c..0000000
--- a/app/src/main/java/io/caster/simplemvp/AppComponent.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.caster.simplemvp;
-
-import javax.inject.Singleton;
-
-import dagger.Component;
-import io.caster.simplemvp.view.fragment.UserFragment;
-
-@Singleton
-@Component(modules = { AppModule.class })
-public interface AppComponent {
- void inject(UserFragment target);
-}
diff --git a/app/src/main/java/io/caster/simplemvp/AppComponent.kt b/app/src/main/java/io/caster/simplemvp/AppComponent.kt
new file mode 100644
index 0000000..e3b8561
--- /dev/null
+++ b/app/src/main/java/io/caster/simplemvp/AppComponent.kt
@@ -0,0 +1,11 @@
+package io.caster.simplemvp
+
+import dagger.Component
+import io.caster.simplemvp.view.fragment.UserFragment
+import javax.inject.Singleton
+
+@Singleton
+@Component(modules = [AppModule::class])
+interface AppComponent {
+ fun inject(target: UserFragment)
+}
diff --git a/app/src/main/java/io/caster/simplemvp/AppModule.java b/app/src/main/java/io/caster/simplemvp/AppModule.java
deleted file mode 100644
index 345616c..0000000
--- a/app/src/main/java/io/caster/simplemvp/AppModule.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package io.caster.simplemvp;
-
-import javax.inject.Singleton;
-
-import dagger.Module;
-import dagger.Provides;
-import io.caster.simplemvp.presentation.UserPresenter;
-import io.caster.simplemvp.presentation.UserPresenterImpl;
-import io.caster.simplemvp.repository.UserRepository;
-import io.caster.simplemvp.repository.InMemoryUserRepositoryImpl;
-
-@Module
-public class AppModule {
- @Provides @Singleton
- public UserRepository provideUserRepository() {
- return new InMemoryUserRepositoryImpl();
- }
-
- @Provides
- public UserPresenter provideUserPresenter(UserRepository userRepository) {
- return new UserPresenterImpl(userRepository);
- }
-}
diff --git a/app/src/main/java/io/caster/simplemvp/AppModule.kt b/app/src/main/java/io/caster/simplemvp/AppModule.kt
new file mode 100644
index 0000000..02a0d41
--- /dev/null
+++ b/app/src/main/java/io/caster/simplemvp/AppModule.kt
@@ -0,0 +1,19 @@
+package io.caster.simplemvp
+
+import dagger.Module
+import dagger.Provides
+import io.caster.simplemvp.presentation.UserPresenter
+import io.caster.simplemvp.presentation.UserPresenterImpl
+import io.caster.simplemvp.repository.InMemoryUserRepositoryImpl
+import io.caster.simplemvp.repository.UserRepository
+import javax.inject.Singleton
+
+@Module
+class AppModule {
+ @Provides
+ @Singleton
+ fun provideUserRepository(): UserRepository = InMemoryUserRepositoryImpl()
+
+ @Provides
+ fun provideUserPresenter(userRepository: UserRepository): UserPresenter = UserPresenterImpl(userRepository)
+}
diff --git a/app/src/main/java/io/caster/simplemvp/MvpApplication.java b/app/src/main/java/io/caster/simplemvp/MvpApplication.java
deleted file mode 100644
index 01b35e6..0000000
--- a/app/src/main/java/io/caster/simplemvp/MvpApplication.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package io.caster.simplemvp;
-
-import android.app.Application;
-
-public class MvpApplication extends Application {
-
- private AppComponent component;
-
-
- @Override
- public void onCreate() {
- super.onCreate();
-
- component = DaggerAppComponent.builder().appModule(new AppModule()).build();
-
- }
-
- public AppComponent getComponent() {
- return component;
- }
-}
diff --git a/app/src/main/java/io/caster/simplemvp/MvpApplication.kt b/app/src/main/java/io/caster/simplemvp/MvpApplication.kt
new file mode 100644
index 0000000..f49394b
--- /dev/null
+++ b/app/src/main/java/io/caster/simplemvp/MvpApplication.kt
@@ -0,0 +1,14 @@
+package io.caster.simplemvp
+
+import android.app.Application
+
+class MvpApplication : Application() {
+
+ var component: AppComponent? = null
+
+ override fun onCreate() {
+ super.onCreate()
+
+ component = DaggerAppComponent.builder().appModule(AppModule()).build()
+ }
+}
diff --git a/app/src/main/java/io/caster/simplemvp/model/User.java b/app/src/main/java/io/caster/simplemvp/model/User.java
deleted file mode 100644
index e40523a..0000000
--- a/app/src/main/java/io/caster/simplemvp/model/User.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package io.caster.simplemvp.model;
-
-public class User {
- private int id;
- private String firstName;
- private String lastName;
-
-
- public int getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
- }
-
- public String getFirstName() {
- return firstName;
- }
-
- public void setFirstName(String firstName) {
- this.firstName = firstName;
- }
-
- public String getLastName() {
- return lastName;
- }
-
- public void setLastName(String lastName) {
- this.lastName = lastName;
- }
-}
diff --git a/app/src/main/java/io/caster/simplemvp/model/User.kt b/app/src/main/java/io/caster/simplemvp/model/User.kt
new file mode 100644
index 0000000..3192dab
--- /dev/null
+++ b/app/src/main/java/io/caster/simplemvp/model/User.kt
@@ -0,0 +1,7 @@
+package io.caster.simplemvp.model
+
+data class User (
+ var id: Int = 0,
+ var firstName: String? = null,
+ var lastName: String? = null
+)
diff --git a/app/src/main/java/io/caster/simplemvp/presentation/UserPresenter.java b/app/src/main/java/io/caster/simplemvp/presentation/UserPresenter.java
deleted file mode 100644
index d07e2f5..0000000
--- a/app/src/main/java/io/caster/simplemvp/presentation/UserPresenter.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package io.caster.simplemvp.presentation;
-
-import io.caster.simplemvp.view.UserView;
-
-public interface UserPresenter {
- void loadUserDetails();
- void setView(UserView view);
- void saveUser();
-}
diff --git a/app/src/main/java/io/caster/simplemvp/presentation/UserPresenter.kt b/app/src/main/java/io/caster/simplemvp/presentation/UserPresenter.kt
new file mode 100644
index 0000000..1c9c839
--- /dev/null
+++ b/app/src/main/java/io/caster/simplemvp/presentation/UserPresenter.kt
@@ -0,0 +1,11 @@
+package io.caster.simplemvp.presentation
+
+import io.caster.simplemvp.view.UserView
+
+interface UserPresenter {
+ fun loadUserDetails()
+
+ fun setView(view: UserView)
+
+ fun saveUser()
+}
diff --git a/app/src/main/java/io/caster/simplemvp/presentation/UserPresenterImpl.java b/app/src/main/java/io/caster/simplemvp/presentation/UserPresenterImpl.java
deleted file mode 100644
index 08304d9..0000000
--- a/app/src/main/java/io/caster/simplemvp/presentation/UserPresenterImpl.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package io.caster.simplemvp.presentation;
-
-import io.caster.simplemvp.model.User;
-import io.caster.simplemvp.repository.UserRepository;
-import io.caster.simplemvp.view.UserView;
-
-public class UserPresenterImpl implements UserPresenter {
-
- private UserView view;
- private UserRepository userRepository;
- private User u;
-
- public UserPresenterImpl(UserRepository userRepository) {
- this.userRepository = userRepository;
- }
-
- @Override
- public void loadUserDetails() {
- int userId = view.getUserId();
- u = userRepository.getUser(userId);
- if(u == null) {
- view.showUserNotFoundMessage();
- } else {
- view.displayFirstName(u.getFirstName());
- view.displayLastName(u.getLastName());
- }
- }
-
- @Override
- public void setView(UserView view) {
- this.view = view;
- loadUserDetails();
- }
-
- @Override
- public void saveUser() {
- if(u != null) {
- if(view.getFirstName().trim().equals("") || view.getLastName().trim().equals("")) {
- view.showUserNameIsRequired();
- } else {
- u.setFirstName(view.getFirstName());
- u.setLastName(view.getLastName());
- userRepository.save(u);
- view.showUserSavedMessage();
- }
-
- }
- }
-}
diff --git a/app/src/main/java/io/caster/simplemvp/presentation/UserPresenterImpl.kt b/app/src/main/java/io/caster/simplemvp/presentation/UserPresenterImpl.kt
new file mode 100644
index 0000000..532f60e
--- /dev/null
+++ b/app/src/main/java/io/caster/simplemvp/presentation/UserPresenterImpl.kt
@@ -0,0 +1,41 @@
+package io.caster.simplemvp.presentation
+
+import io.caster.simplemvp.model.User
+import io.caster.simplemvp.repository.UserRepository
+import io.caster.simplemvp.view.UserView
+
+class UserPresenterImpl(val userRepository: UserRepository) : UserPresenter {
+
+ private var view: UserView? = null
+ private var u: User? = null
+
+ override fun loadUserDetails() {
+ val userId = view!!.userId
+ u = userRepository.getUser(userId)
+ if (u == null) {
+ view!!.showUserNotFoundMessage()
+ } else {
+ view!!.displayFirstName(u!!.firstName!!)
+ view!!.displayLastName(u!!.lastName!!)
+ }
+ }
+
+ override fun setView(view: UserView) {
+ this.view = view
+ loadUserDetails()
+ }
+
+ override fun saveUser() {
+ if (u != null) {
+ if (view!!.firstName.trim { it <= ' ' } == "" || view!!.lastName.trim { it <= ' ' } == "") {
+ view!!.showUserNameIsRequired()
+ } else {
+ u!!.firstName = view!!.firstName
+ u!!.lastName = view!!.lastName
+ userRepository.save(u!!)
+ view!!.showUserSavedMessage()
+ }
+
+ }
+ }
+}
diff --git a/app/src/main/java/io/caster/simplemvp/repository/InMemoryUserRepositoryImpl.java b/app/src/main/java/io/caster/simplemvp/repository/InMemoryUserRepositoryImpl.java
deleted file mode 100644
index 35bc0b6..0000000
--- a/app/src/main/java/io/caster/simplemvp/repository/InMemoryUserRepositoryImpl.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package io.caster.simplemvp.repository;
-
-import io.caster.simplemvp.model.User;
-
-public class InMemoryUserRepositoryImpl implements UserRepository {
- private User u;
-
- /**
- * Gets the user from memory.
- *
- * @param id The id of the user.
- * @return a {@link User}
- */
- @Override
- public User getUser(int id) {
- // Normally this would go to a DB/etc and fetch the user with an ID.
- // Here though, we're just doing something in memory, so we're kind of just
- // faking it out.
- if (u == null) {
- u = new User();
- u.setId(id);
- u.setFirstName("Captain");
- u.setLastName("Crunch");
- }
- return u;
- }
-
- /**
- * Save's the in-memory user.
- *
- * @param u The user.
- */
- @Override
- public void save(User u) {
- if(this.u == null) {
- this.u = getUser(0); // create the in memory user.
- }
- this.u.setId(u.getId());
- this.u.setFirstName(u.getFirstName());
- this.u.setLastName(u.getLastName());
- }
-}
diff --git a/app/src/main/java/io/caster/simplemvp/repository/InMemoryUserRepositoryImpl.kt b/app/src/main/java/io/caster/simplemvp/repository/InMemoryUserRepositoryImpl.kt
new file mode 100644
index 0000000..10c67af
--- /dev/null
+++ b/app/src/main/java/io/caster/simplemvp/repository/InMemoryUserRepositoryImpl.kt
@@ -0,0 +1,40 @@
+package io.caster.simplemvp.repository
+
+import io.caster.simplemvp.model.User
+
+class InMemoryUserRepositoryImpl : UserRepository {
+ private var u: User? = null
+
+ /**
+ * Gets the user from memory.
+ *
+ * @param id The id of the user.
+ * @return a [User]
+ */
+ override fun getUser(id: Int): User {
+ // Normally this would go to a DB/etc and fetch the user with an ID.
+ // Here though, we're just doing something in memory, so we're kind of just
+ // faking it out.
+ if (u == null) {
+ u = User()
+ u!!.id = id
+ u!!.firstName = "Captain"
+ u!!.lastName = "Crunch"
+ }
+ return u!!
+ }
+
+ /**
+ * Save's the in-memory user.
+ *
+ * @param u The user.
+ */
+ override fun save(u: User) {
+ if (this.u == null) {
+ this.u = getUser(0) // create the in memory user.
+ }
+ this.u!!.id = u.id
+ this.u!!.firstName = u.firstName
+ this.u!!.lastName = u.lastName
+ }
+}
diff --git a/app/src/main/java/io/caster/simplemvp/repository/UserRepository.java b/app/src/main/java/io/caster/simplemvp/repository/UserRepository.java
deleted file mode 100644
index e071fd0..0000000
--- a/app/src/main/java/io/caster/simplemvp/repository/UserRepository.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.caster.simplemvp.repository;
-
-import io.caster.simplemvp.model.User;
-
-public interface UserRepository {
- User getUser(int id);
- void save(User u);
-}
diff --git a/app/src/main/java/io/caster/simplemvp/repository/UserRepository.kt b/app/src/main/java/io/caster/simplemvp/repository/UserRepository.kt
new file mode 100644
index 0000000..7ef9acf
--- /dev/null
+++ b/app/src/main/java/io/caster/simplemvp/repository/UserRepository.kt
@@ -0,0 +1,9 @@
+package io.caster.simplemvp.repository
+
+import io.caster.simplemvp.model.User
+
+interface UserRepository {
+ fun getUser(id: Int): User
+
+ fun save(u: User)
+}
diff --git a/app/src/main/java/io/caster/simplemvp/view/UserView.java b/app/src/main/java/io/caster/simplemvp/view/UserView.java
deleted file mode 100644
index 5592871..0000000
--- a/app/src/main/java/io/caster/simplemvp/view/UserView.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package io.caster.simplemvp.view;
-
-public interface UserView {
- int getUserId();
-
- void displayFirstName(String name);
- void displayLastName(String name);
-
- void showUserNotFoundMessage();
- void showUserSavedMessage();
-
- String getFirstName();
- String getLastName();
-
- void showUserNameIsRequired();
-}
diff --git a/app/src/main/java/io/caster/simplemvp/view/UserView.kt b/app/src/main/java/io/caster/simplemvp/view/UserView.kt
new file mode 100644
index 0000000..fbd7e23
--- /dev/null
+++ b/app/src/main/java/io/caster/simplemvp/view/UserView.kt
@@ -0,0 +1,19 @@
+package io.caster.simplemvp.view
+
+interface UserView {
+ val userId: Int
+
+ val firstName: String
+
+ val lastName: String
+
+ fun displayFirstName(name: String)
+
+ fun displayLastName(name: String)
+
+ fun showUserNotFoundMessage()
+
+ fun showUserSavedMessage()
+
+ fun showUserNameIsRequired()
+}
diff --git a/app/src/main/java/io/caster/simplemvp/view/activity/MainActivity.java b/app/src/main/java/io/caster/simplemvp/view/activity/MainActivity.java
deleted file mode 100644
index f33d4c7..0000000
--- a/app/src/main/java/io/caster/simplemvp/view/activity/MainActivity.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package io.caster.simplemvp.view.activity;
-
-import android.os.Bundle;
-import android.support.design.widget.FloatingActionButton;
-import android.support.design.widget.Snackbar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.View;
-import android.view.Menu;
-import android.view.MenuItem;
-
-import io.caster.simplemvp.R;
-
-public class MainActivity extends AppCompatActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
- .setAction("Action", null).show();
- }
- });
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.menu_main, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- // Handle action bar item clicks here. The action bar will
- // automatically handle clicks on the Home/Up button, so long
- // as you specify a parent activity in AndroidManifest.xml.
- int id = item.getItemId();
-
- //noinspection SimplifiableIfStatement
- if (id == R.id.action_settings) {
- return true;
- }
-
- return super.onOptionsItemSelected(item);
- }
-}
diff --git a/app/src/main/java/io/caster/simplemvp/view/activity/MainActivity.kt b/app/src/main/java/io/caster/simplemvp/view/activity/MainActivity.kt
new file mode 100644
index 0000000..fa8b936
--- /dev/null
+++ b/app/src/main/java/io/caster/simplemvp/view/activity/MainActivity.kt
@@ -0,0 +1,44 @@
+package io.caster.simplemvp.view.activity
+
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.view.Menu
+import android.view.MenuItem
+import io.caster.simplemvp.R
+import kotlinx.android.synthetic.main.activity_main.*
+import org.jetbrains.anko.design.snackbar
+import org.jetbrains.anko.toast
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ setSupportActionBar(toolbar)
+
+ fab.setOnClickListener { view ->
+ snackbar(view, "Replace with your own action", "Action") {
+ toast("You clicked on the FAB!")
+ }.show()
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ menuInflater.inflate(R.menu.menu_main, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ val id = item.itemId
+
+
+ return if (id == R.id.action_settings) {
+ true
+ } else super.onOptionsItemSelected(item)
+
+ }
+}
diff --git a/app/src/main/java/io/caster/simplemvp/view/fragment/UserFragment.java b/app/src/main/java/io/caster/simplemvp/view/fragment/UserFragment.java
deleted file mode 100644
index fb07e43..0000000
--- a/app/src/main/java/io/caster/simplemvp/view/fragment/UserFragment.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package io.caster.simplemvp.view.fragment;
-
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import javax.inject.Inject;
-
-import butterknife.Bind;
-import butterknife.ButterKnife;
-import butterknife.OnClick;
-import io.caster.simplemvp.MvpApplication;
-import io.caster.simplemvp.R;
-import io.caster.simplemvp.presentation.UserPresenter;
-import io.caster.simplemvp.view.UserView;
-
-/**
- * The V in MVP (Model View Presenter)
- */
-public class UserFragment extends Fragment implements UserView {
-
- @Inject UserPresenter userPresenter;
-
- protected EditText userFirstName;
- protected EditText userLastName;
-
- private static final String USER_ID = "user_id";
-
- public UserFragment() {
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ((MvpApplication)getActivity().getApplication()).getComponent().inject(this);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.fragment_main, container, false);
- userFirstName = (EditText) v.findViewById(R.id.user_first_name);
- userLastName = (EditText) v.findViewById(R.id.user_last_name);
- v.findViewById(R.id.user_save).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- userPresenter.saveUser();
- }
- });
- return v;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- userPresenter.setView(this);
- }
-
- @Override
- public int getUserId() {
- return getArguments() == null ? 0 : getArguments().getInt(USER_ID, 0);
- }
-
- @Override
- public void displayFirstName(String name) {
- userFirstName.setText(name);
- }
-
- @Override
- public void displayLastName(String name) {
- userLastName.setText(name);
- }
-
- @Override
- public void showUserNotFoundMessage() {
- Toast.makeText(getActivity(), R.string.user_not_found, Toast.LENGTH_LONG).show();
- }
-
- @Override
- public void showUserSavedMessage() {
- Toast.makeText(getActivity(), R.string.user_saved, Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public String getFirstName() {
- return userFirstName.getText().toString();
- }
-
- @Override
- public String getLastName() {
- return userLastName.getText().toString();
- }
-
- @Override
- public void showUserNameIsRequired() {
- Toast.makeText(getActivity(), R.string.user_name_required_message, Toast.LENGTH_SHORT).show();
- }
-}
diff --git a/app/src/main/java/io/caster/simplemvp/view/fragment/UserFragment.kt b/app/src/main/java/io/caster/simplemvp/view/fragment/UserFragment.kt
new file mode 100644
index 0000000..b490f85
--- /dev/null
+++ b/app/src/main/java/io/caster/simplemvp/view/fragment/UserFragment.kt
@@ -0,0 +1,71 @@
+package io.caster.simplemvp.view.fragment
+
+import android.os.Bundle
+import android.support.v4.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import io.caster.simplemvp.MvpApplication
+import io.caster.simplemvp.R
+import io.caster.simplemvp.presentation.UserPresenter
+import io.caster.simplemvp.view.UserView
+import kotlinx.android.synthetic.main.fragment_main.*
+import org.jetbrains.anko.support.v4.toast
+import javax.inject.Inject
+
+/**
+ * The V in MVP (Model View Presenter)
+ */
+class UserFragment : Fragment(), UserView {
+ @Inject lateinit var userPresenter: UserPresenter
+
+ override val userId: Int
+ get() = if (arguments == null) 0 else arguments!!.getInt(USER_ID, 0)
+
+ override val firstName: String
+ get() = userFirstName.text.toString()
+
+ override val lastName: String
+ get() = userLastName.text.toString()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ (activity!!.application as MvpApplication).component!!.inject(this)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ val v = inflater.inflate(R.layout.fragment_main, container, false)
+ userSave.setOnClickListener { userPresenter.saveUser() }
+ return v
+ }
+
+ override fun onResume() {
+ super.onResume()
+ userPresenter.setView(this)
+ }
+
+ override fun displayFirstName(name: String) {
+ userFirstName.setText(name)
+ }
+
+ override fun displayLastName(name: String) {
+ userLastName.setText(name)
+ }
+
+ override fun showUserNotFoundMessage() {
+ toast(R.string.user_not_found)
+ }
+
+ override fun showUserSavedMessage() {
+ toast(R.string.user_saved)
+ }
+
+ override fun showUserNameIsRequired() {
+ toast(R.string.user_name_required_message)
+ }
+
+ companion object {
+ const val USER_ID = "user_id"
+ }
+}
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
index 393a25e..896899a 100644
--- a/app/src/main/res/layout/fragment_main.xml
+++ b/app/src/main/res/layout/fragment_main.xml
@@ -11,19 +11,19 @@
tools:showIn="@layout/activity_main">
diff --git a/app/src/test/java/io/caster/simplemvp/PresenterTests.java b/app/src/test/java/io/caster/simplemvp/PresenterTests.java
deleted file mode 100644
index e67a852..0000000
--- a/app/src/test/java/io/caster/simplemvp/PresenterTests.java
+++ /dev/null
@@ -1,163 +0,0 @@
-package io.caster.simplemvp;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import io.caster.simplemvp.model.User;
-import io.caster.simplemvp.presentation.UserPresenter;
-import io.caster.simplemvp.presentation.UserPresenterImpl;
-import io.caster.simplemvp.repository.UserRepository;
-import io.caster.simplemvp.view.UserView;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-
-public class PresenterTests {
-
- UserRepository mockUserRepository;
- UserView mockView;
- UserPresenter presenter;
- User user;
-
- @Before
- public void setup() {
- mockUserRepository = mock(UserRepository.class);
-
- user = new User();
- user.setId(1);
- user.setFirstName("Mighty");
- user.setLastName("Mouse");
- when(mockUserRepository.getUser(anyInt())).thenReturn(user);
-
- mockView = mock(UserView.class);
-
- presenter = new UserPresenterImpl(mockUserRepository);
- }
-
- @Test
- public void noInteractionsWithViewShouldTakePlaceIfUserIsNull() {
- presenter.saveUser();
-
- // user object is not initialized, lets verify no interactions take place
- verifyZeroInteractions(mockView);
- }
-
- @Test
- public void shouldBeABleToLoadTheUserFromTheRepositoryWhenValidUserIsPresent() {
- when(mockView.getUserId()).thenReturn(1);
-
- presenter.setView(mockView );
-
- // Verify repository interactions
- verify(mockUserRepository, times(1)).getUser(anyInt());
-
- // Verify view interactions
- verify(mockView, times(1)).getUserId();
- verify(mockView, times(1)).displayFirstName("Mighty");
- verify(mockView, times(1)).displayLastName("Mouse");
- verify(mockView, never()).showUserNotFoundMessage();
- }
-
- @Test
- public void shouldShowErrorMessageOnViewWhenUserIsNotPresent() {
- when(mockView.getUserId()).thenReturn(1);
-
- // Return null when we ask the repo for a user.
- when(mockUserRepository.getUser(anyInt())).thenReturn(null);
-
- presenter.setView(mockView);
-
- // Verify repository interactions
- verify(mockUserRepository, times(1)).getUser(anyInt());
-
- // verify view interactions
- verify(mockView, times(1)).getUserId();
- verify(mockView, times(1)).showUserNotFoundMessage();
- verify(mockView, never()).displayFirstName(anyString());
- verify(mockView, never()).displayLastName(anyString());
- }
-
- @Test
- public void shouldShouldErrorMessageDuringSaveIfFirstOrLastNameIsMissing() {
- when(mockView.getUserId()).thenReturn(1);
-
- // Load the user
- presenter.setView(mockView);
-
- verify(mockView, times(1)).getUserId();
-
- // Set up the view mock
- when(mockView.getFirstName()).thenReturn(""); // empty string
-
- presenter.saveUser();
-
- verify(mockView, times(1)).getFirstName();
- verify(mockView, never()).getLastName();
- verify(mockView, times(1)).showUserNameIsRequired();
-
- // Now tell mockView to return a value for first name and an empty last name
- when(mockView.getFirstName()).thenReturn("Foo");
- when(mockView.getLastName()).thenReturn("");
-
- presenter.saveUser();
-
- verify(mockView, times(2)).getFirstName(); // Called two times now, once before, and once now
- verify(mockView, times(1)).getLastName(); // Only called once
- verify(mockView, times(2)).showUserNameIsRequired(); // Called two times now, once before and once now
- }
-
- @Test
- public void shouldBeAbleToSaveAValidUser() {
- when(mockView.getUserId()).thenReturn(1);
-
- // Load the user
- presenter.setView(mockView);
-
- verify(mockView, times(1)).getUserId();
-
- when(mockView.getFirstName()).thenReturn("Foo");
- when(mockView.getLastName()).thenReturn("Bar");
-
- presenter.saveUser();
-
- // Called two more times in the saveUser call.
- verify(mockView, times(2)).getFirstName();
- verify(mockView, times(2)).getLastName();
-
- assertThat(user.getFirstName(), is("Foo"));
- assertThat(user.getLastName(), is("Bar"));
-
- // Make sure the repository saved the user
- verify(mockUserRepository, times(1)).save(user);
-
- // Make sure that the view showed the user saved message
- verify(mockView, times(1)).showUserSavedMessage();
- }
-
- @Test
- public void shouldLoadUserDetailsWhenTheViewIsSet() {
- presenter.setView(mockView);
- verify(mockUserRepository, times(1)).getUser(anyInt());
- verify(mockView, times(1)).displayFirstName(anyString());
- verify(mockView, times(1)).displayLastName(anyString());
- }
-
- @Test(expected = NullPointerException.class)
- public void shouldThrowNullPointerExceptionWhenViewIsNull() {
- // Null out the view
- presenter.setView(null);
-
- // Try to load the screen which will force interactions with the view
- presenter.loadUserDetails();
- }
-}
diff --git a/app/src/test/java/io/caster/simplemvp/PresenterTests.kt b/app/src/test/java/io/caster/simplemvp/PresenterTests.kt
new file mode 100644
index 0000000..76f7538
--- /dev/null
+++ b/app/src/test/java/io/caster/simplemvp/PresenterTests.kt
@@ -0,0 +1,154 @@
+package io.caster.simplemvp
+
+import io.caster.simplemvp.model.User
+import io.caster.simplemvp.presentation.UserPresenter
+import io.caster.simplemvp.presentation.UserPresenterImpl
+import io.caster.simplemvp.repository.UserRepository
+import io.caster.simplemvp.view.UserView
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito.*
+
+
+class PresenterTests {
+
+ lateinit var mockUserRepository: UserRepository
+ lateinit var mockView: UserView
+ lateinit var presenter: UserPresenter
+ lateinit var user: User
+
+ @Before
+ fun setup() {
+ mockUserRepository = mock(UserRepository::class.java)
+
+ user = User()
+ user.id = 1
+ user.firstName = "Mighty"
+ user.lastName = "Mouse"
+ `when`(mockUserRepository.getUser(ArgumentMatchers.anyInt())).thenReturn(user)
+
+ mockView = mock(UserView::class.java)
+
+ presenter = UserPresenterImpl(mockUserRepository)
+ }
+
+ @Test
+ fun noInteractionsWithViewShouldTakePlaceIfUserIsNull() {
+ presenter.saveUser()
+
+ // user object is not initialized, lets verify no interactions take place
+ verifyZeroInteractions(mockView)
+ }
+
+ @Test
+ fun shouldBeABleToLoadTheUserFromTheRepositoryWhenValidUserIsPresent() {
+ `when`(mockView.userId).thenReturn(1)
+
+ presenter.setView(mockView)
+
+ // Verify repository interactions
+ verify(mockUserRepository, times(1)).getUser(ArgumentMatchers.anyInt())
+
+ // Verify view interactions
+ verify(mockView, times(1)).userId
+ verify(mockView, times(1)).displayFirstName("Mighty")
+ verify(mockView, times(1)).displayLastName("Mouse")
+ verify(mockView, never()).showUserNotFoundMessage()
+ }
+
+ @Test
+ fun shouldShowErrorMessageOnViewWhenUserIsNotPresent() {
+ `when`(mockView.userId).thenReturn(1)
+
+ // Return null when we ask the repo for a user.
+ `when`(mockUserRepository.getUser(ArgumentMatchers.anyInt())).thenReturn(null)
+
+ presenter.setView(mockView)
+
+ // Verify repository interactions
+ verify(mockUserRepository, times(1)).getUser(ArgumentMatchers.anyInt())
+
+ // verify view interactions
+ verify(mockView, times(1)).userId
+ verify(mockView, times(1)).showUserNotFoundMessage()
+ verify(mockView, never()).displayFirstName(ArgumentMatchers.anyString())
+ verify(mockView, never()).displayLastName(ArgumentMatchers.anyString())
+ }
+
+ @Test
+ fun shouldShouldErrorMessageDuringSaveIfFirstOrLastNameIsMissing() {
+ `when`(mockView.userId).thenReturn(1)
+
+ // Load the user
+ presenter.setView(mockView)
+
+ verify(mockView, times(1)).userId
+
+ // Set up the view mock
+ `when`(mockView.firstName).thenReturn("") // empty string
+
+ presenter.saveUser()
+
+ verify(mockView, times(1)).firstName
+ verify(mockView, never()).lastName
+ verify(mockView, times(1)).showUserNameIsRequired()
+
+ // Now tell mockView to return a value for first name and an empty last name
+ `when`(mockView.firstName).thenReturn("Foo")
+ `when`(mockView.lastName).thenReturn("")
+
+ presenter.saveUser()
+
+ verify(mockView, times(2)).firstName // Called two times now, once before, and once now
+ verify(mockView, times(1)).lastName // Only called once
+ verify(mockView, times(2)).showUserNameIsRequired() // Called two times now, once before and once now
+ }
+
+ @Test
+ fun shouldBeAbleToSaveAValidUser() {
+ `when`(mockView.userId).thenReturn(1)
+
+ // Load the user
+ presenter.setView(mockView)
+
+ verify(mockView, times(1)).userId
+
+ `when`(mockView.firstName).thenReturn("Foo")
+ `when`(mockView.lastName).thenReturn("Bar")
+
+ presenter.saveUser()
+
+ // Called two more times in the saveUser call.
+ verify(mockView, times(2)).firstName
+ verify(mockView, times(2)).lastName
+
+ assertThat(user.firstName, `is`("Foo"))
+ assertThat(user.lastName, `is`("Bar"))
+
+ // Make sure the repository saved the user
+ verify(mockUserRepository, times(1)).save(user)
+
+ // Make sure that the view showed the user saved message
+ verify(mockView, times(1)).showUserSavedMessage()
+ }
+
+ @Test
+ fun shouldLoadUserDetailsWhenTheViewIsSet() {
+ presenter.setView(mockView)
+ verify(mockUserRepository, times(1)).getUser(ArgumentMatchers.anyInt())
+ verify(mockView, times(1)).displayFirstName(ArgumentMatchers.anyString())
+ verify(mockView, times(1)).displayLastName(ArgumentMatchers.anyString())
+ }
+
+ @Test(expected = NullPointerException::class)
+ fun shouldThrowNullPointerExceptionWhenViewIsNull() {
+ // Null out the view
+ presenter.setView(null!!)
+
+ // Try to load the screen which will force interactions with the view
+ presenter.loadUserDetails()
+ }
+}
diff --git a/build.gradle b/build.gradle
index a6cf621..15dcb9a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,23 +1,30 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext {
+ kotlinVer = '1.2.31'
+ ankoVer = '0.10.4'
+ }
repositories {
jcenter()
+ google()
+ mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.0.0-rc2'
- classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
+ classpath 'com.android.tools.build:gradle:3.0.1'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVer"
}
}
allprojects {
repositories {
jcenter()
+ google()
+ mavenCentral()
}
}
+
task clean(type: Delete) {
delete rootProject.buildDir
}
diff --git a/gradle.properties b/gradle.properties
index 1d3591c..f92ca66 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,18 +1,3 @@
-# Project-wide Gradle settings.
-
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
\ No newline at end of file
+org.gradle.parallel=true
+org.gradle.daemon=true
+org.gradle.jvmargs=-Xms256m -Xmx1024m
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 122a0dc..c3510d6 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip