├── .gitignore ├── HOW-IT-WORKS.md ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.txt └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── antonioleiva │ │ │ └── mvpexample │ │ │ └── app │ │ │ ├── login │ │ │ ├── LoginActivity.java │ │ │ ├── LoginInteractor.java │ │ │ ├── LoginPresenter.java │ │ │ └── LoginView.java │ │ │ └── main │ │ │ ├── FindItemsInteractor.java │ │ │ ├── MainActivity.java │ │ │ ├── MainAdapter.java │ │ │ ├── MainPresenter.java │ │ │ └── MainView.java │ └── res │ │ ├── drawable-hdpi │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ │ ├── drawable │ │ ├── ic_password.xml │ │ └── ic_username.xml │ │ ├── layout │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ └── view_main_item.xml │ │ ├── menu │ │ ├── login.xml │ │ └── main.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── antonioleiva │ └── mvpexample │ └── app │ └── main │ └── MainPresenterTest.java ├── appkotlin ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── antonioleiva │ │ └── com │ │ └── appkotlin │ │ ├── Extensions.kt │ │ ├── login │ │ ├── LoginActivity.kt │ │ ├── LoginInteractor.kt │ │ ├── LoginPresenter.kt │ │ └── LoginView.kt │ │ └── main │ │ ├── FindItemsInteractor.kt │ │ ├── MainActivity.kt │ │ ├── MainAdapter.kt │ │ ├── MainPresenter.kt │ │ └── MainView.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── ic_launcher_background.xml │ ├── ic_password.xml │ └── ic_username.xml │ ├── layout │ ├── activity_login.xml │ ├── activity_main.xml │ └── view_main_item.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 │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── login-classes.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | ./gradlew.bat 18 | ./gradlew 19 | build/ 20 | 21 | # Mirror files 22 | mirror/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Intellij project files 31 | *.iws 32 | .idea/workspace.xml 33 | .idea/tasks.xml 34 | .idea 35 | 36 | *.iml 37 | 38 | # OS 39 | .DS_Store -------------------------------------------------------------------------------- /HOW-IT-WORKS.md: -------------------------------------------------------------------------------- 1 | How it Works 2 | ========== 3 | ![](login-classes.png) 4 | 5 | 1. View(Activity, Fragment, ...) calls presenter methods whenever there're user interaction 6 | 2. Presenter implementation calls the interactor(use case handler) to get results from business/domain layer 7 | 3. Interactor implementation returns the results or just returns the control to presenter implementation by calling listener methods 8 | 4. Presenter implementation calls view methods to update the UI by calling view interface. 9 | 10 | View, Presenter, Interactor and Listener interfaces are used to remove tight coupling. 11 | 12 | View becomes too humble to test. We can use fake(simulator) presenter to test view. 13 | 14 | Presenter and Interactor can be tested. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | androidmvp 2 | ========== 3 | 4 | MVP Android Example used to explain how to use this pattern in our Android apps. This code was created to support an article explanation: 5 | 6 | [Android MVP @ antonioleiva.com (English)](http://antonioleiva.com/mvp-android) 7 | 8 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-androidmvp-brightgreen.svg?style=flat)](https://android-arsenal.com/details/3/1514) 9 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.antonioleiva.mvpexample.app" 7 | minSdkVersion 21 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | compileOptions { 20 | sourceCompatibility JavaVersion.VERSION_1_8 21 | targetCompatibility JavaVersion.VERSION_1_8 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation 'com.android.support:appcompat-v7:27.1.1' 27 | implementation 'com.android.support:recyclerview-v7:27.1.1' 28 | testImplementation 'junit:junit:4.12' 29 | testImplementation 'org.mockito:mockito-core:2.15.0' 30 | } 31 | -------------------------------------------------------------------------------- /app/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:/Desarrollo/Entorno Android/01 Entorno de Desarrollo/adt-bundle-windows/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 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 | #} -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/antonioleiva/mvpexample/app/login/LoginActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2018 Antonio Leiva Gordillo. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.antonioleiva.mvpexample.app.login; 20 | 21 | import android.content.Intent; 22 | import android.os.Bundle; 23 | import android.support.v7.app.AppCompatActivity; 24 | import android.view.View; 25 | import android.widget.EditText; 26 | import android.widget.ProgressBar; 27 | 28 | import com.antonioleiva.mvpexample.app.R; 29 | import com.antonioleiva.mvpexample.app.main.MainActivity; 30 | 31 | public class LoginActivity extends AppCompatActivity implements LoginView { 32 | 33 | private ProgressBar progressBar; 34 | private EditText username; 35 | private EditText password; 36 | private LoginPresenter presenter; 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_login); 42 | 43 | progressBar = findViewById(R.id.progress); 44 | username = findViewById(R.id.username); 45 | password = findViewById(R.id.password); 46 | findViewById(R.id.button).setOnClickListener(v -> validateCredentials()); 47 | 48 | presenter = new LoginPresenter(this, new LoginInteractor()); 49 | } 50 | 51 | @Override 52 | protected void onDestroy() { 53 | presenter.onDestroy(); 54 | super.onDestroy(); 55 | } 56 | 57 | @Override 58 | public void showProgress() { 59 | progressBar.setVisibility(View.VISIBLE); 60 | } 61 | 62 | @Override 63 | public void hideProgress() { 64 | progressBar.setVisibility(View.GONE); 65 | } 66 | 67 | @Override 68 | public void setUsernameError() { 69 | username.setError(getString(R.string.username_error)); 70 | } 71 | 72 | @Override 73 | public void setPasswordError() { 74 | password.setError(getString(R.string.password_error)); 75 | } 76 | 77 | @Override 78 | public void navigateToHome() { 79 | startActivity(new Intent(this, MainActivity.class)); 80 | finish(); 81 | } 82 | 83 | private void validateCredentials() { 84 | presenter.validateCredentials(username.getText().toString(), password.getText().toString()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/antonioleiva/mvpexample/app/login/LoginInteractor.java: -------------------------------------------------------------------------------- 1 | package com.antonioleiva.mvpexample.app.login; 2 | 3 | import android.os.Handler; 4 | import android.text.TextUtils; 5 | 6 | public class LoginInteractor { 7 | 8 | interface OnLoginFinishedListener { 9 | void onUsernameError(); 10 | 11 | void onPasswordError(); 12 | 13 | void onSuccess(); 14 | } 15 | 16 | public void login(final String username, final String password, final OnLoginFinishedListener listener) { 17 | // Mock login. I'm creating a handler to delay the answer a couple of seconds 18 | new Handler().postDelayed(() -> { 19 | if (TextUtils.isEmpty(username)) { 20 | listener.onUsernameError(); 21 | return; 22 | } 23 | if (TextUtils.isEmpty(password)) { 24 | listener.onPasswordError(); 25 | return; 26 | } 27 | listener.onSuccess(); 28 | }, 2000); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/antonioleiva/mvpexample/app/login/LoginPresenter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2018 Antonio Leiva Gordillo. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.antonioleiva.mvpexample.app.login; 20 | 21 | public class LoginPresenter implements LoginInteractor.OnLoginFinishedListener { 22 | 23 | private LoginView loginView; 24 | private LoginInteractor loginInteractor; 25 | 26 | LoginPresenter(LoginView loginView, LoginInteractor loginInteractor) { 27 | this.loginView = loginView; 28 | this.loginInteractor = loginInteractor; 29 | } 30 | 31 | public void validateCredentials(String username, String password) { 32 | if (loginView != null) { 33 | loginView.showProgress(); 34 | } 35 | 36 | loginInteractor.login(username, password, this); 37 | } 38 | 39 | public void onDestroy() { 40 | loginView = null; 41 | } 42 | 43 | @Override 44 | public void onUsernameError() { 45 | if (loginView != null) { 46 | loginView.setUsernameError(); 47 | loginView.hideProgress(); 48 | } 49 | } 50 | 51 | @Override 52 | public void onPasswordError() { 53 | if (loginView != null) { 54 | loginView.setPasswordError(); 55 | loginView.hideProgress(); 56 | } 57 | } 58 | 59 | @Override 60 | public void onSuccess() { 61 | if (loginView != null) { 62 | loginView.navigateToHome(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/antonioleiva/mvpexample/app/login/LoginView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2018 Antonio Leiva Gordillo. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.antonioleiva.mvpexample.app.login; 20 | 21 | public interface LoginView { 22 | void showProgress(); 23 | 24 | void hideProgress(); 25 | 26 | void setUsernameError(); 27 | 28 | void setPasswordError(); 29 | 30 | void navigateToHome(); 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/antonioleiva/mvpexample/app/main/FindItemsInteractor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2018 Antonio Leiva Gordillo. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.antonioleiva.mvpexample.app.main; 20 | 21 | import android.os.Handler; 22 | 23 | import java.util.Arrays; 24 | import java.util.List; 25 | 26 | public class FindItemsInteractor { 27 | 28 | interface OnFinishedListener { 29 | void onFinished(List items); 30 | } 31 | 32 | public void findItems(final OnFinishedListener listener) { 33 | new Handler().postDelayed(() -> listener.onFinished(createArrayList()), 2000); 34 | } 35 | 36 | private List createArrayList() { 37 | return Arrays.asList( 38 | "Item 1", 39 | "Item 2", 40 | "Item 3", 41 | "Item 4", 42 | "Item 5", 43 | "Item 6", 44 | "Item 7", 45 | "Item 8", 46 | "Item 9", 47 | "Item 10" 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/antonioleiva/mvpexample/app/main/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2018 Antonio Leiva Gordillo. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.antonioleiva.mvpexample.app.main; 20 | 21 | import android.os.Bundle; 22 | import android.support.v7.app.AppCompatActivity; 23 | import android.support.v7.widget.RecyclerView; 24 | import android.view.Menu; 25 | import android.view.MenuItem; 26 | import android.view.View; 27 | import android.widget.ProgressBar; 28 | import android.widget.Toast; 29 | 30 | import com.antonioleiva.mvpexample.app.R; 31 | 32 | import java.util.List; 33 | 34 | public class MainActivity extends AppCompatActivity implements MainView { 35 | 36 | private RecyclerView recyclerView; 37 | private ProgressBar progressBar; 38 | private MainPresenter presenter; 39 | 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.activity_main); 44 | recyclerView = findViewById(R.id.list); 45 | progressBar = findViewById(R.id.progress); 46 | presenter = new MainPresenter(this, new FindItemsInteractor()); 47 | } 48 | 49 | @Override 50 | protected void onResume() { 51 | super.onResume(); 52 | presenter.onResume(); 53 | } 54 | 55 | @Override 56 | public boolean onCreateOptionsMenu(Menu menu) { 57 | getMenuInflater().inflate(R.menu.main, menu); 58 | return true; 59 | } 60 | 61 | @Override 62 | public boolean onOptionsItemSelected(MenuItem item) { 63 | switch (item.getItemId()) { 64 | case R.id.action_settings: 65 | return true; 66 | default: 67 | return super.onOptionsItemSelected(item); 68 | } 69 | } 70 | 71 | @Override 72 | protected void onDestroy() { 73 | presenter.onDestroy(); 74 | super.onDestroy(); 75 | } 76 | 77 | @Override 78 | public void showProgress() { 79 | progressBar.setVisibility(View.VISIBLE); 80 | recyclerView.setVisibility(View.INVISIBLE); 81 | } 82 | 83 | @Override 84 | public void hideProgress() { 85 | progressBar.setVisibility(View.INVISIBLE); 86 | recyclerView.setVisibility(View.VISIBLE); 87 | } 88 | 89 | @Override 90 | public void setItems(List items) { 91 | recyclerView.setAdapter(new MainAdapter(items, presenter::onItemClicked)); 92 | } 93 | 94 | @Override 95 | public void showMessage(String message) { 96 | Toast.makeText(this, message, Toast.LENGTH_LONG).show(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/antonioleiva/mvpexample/app/main/MainAdapter.java: -------------------------------------------------------------------------------- 1 | package com.antonioleiva.mvpexample.app.main; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | 9 | import com.antonioleiva.mvpexample.app.R; 10 | 11 | import java.util.List; 12 | 13 | public class MainAdapter extends RecyclerView.Adapter { 14 | 15 | public MainAdapter(List items, Listener listener) { 16 | this.items = items; 17 | this.listener = listener; 18 | } 19 | 20 | interface Listener { 21 | void onItemClicked(String item); 22 | } 23 | 24 | private List items; 25 | private Listener listener; 26 | 27 | @NonNull 28 | @Override 29 | public MainViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 30 | TextView v = (TextView) LayoutInflater.from(parent.getContext()) 31 | .inflate(R.layout.view_main_item, parent, false); 32 | return new MainViewHolder(v); 33 | } 34 | 35 | @Override 36 | public void onBindViewHolder(@NonNull MainViewHolder holder, int position) { 37 | final String item = items.get(position); 38 | holder.textView.setText(item); 39 | holder.textView.setOnClickListener(v -> listener.onItemClicked(item)); 40 | } 41 | 42 | @Override 43 | public int getItemCount() { 44 | return items.size(); 45 | } 46 | 47 | static class MainViewHolder extends RecyclerView.ViewHolder { 48 | 49 | TextView textView; 50 | 51 | MainViewHolder(TextView textView) { 52 | super(textView); 53 | this.textView = textView; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/antonioleiva/mvpexample/app/main/MainPresenter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2018 Antonio Leiva Gordillo. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.antonioleiva.mvpexample.app.main; 20 | 21 | import java.util.List; 22 | 23 | class MainPresenter { 24 | 25 | private MainView mainView; 26 | private FindItemsInteractor findItemsInteractor; 27 | 28 | MainPresenter(MainView mainView, FindItemsInteractor findItemsInteractor) { 29 | this.mainView = mainView; 30 | this.findItemsInteractor = findItemsInteractor; 31 | } 32 | 33 | void onResume() { 34 | if (mainView != null) { 35 | mainView.showProgress(); 36 | } 37 | 38 | findItemsInteractor.findItems(this::onFinished); 39 | } 40 | 41 | void onItemClicked(String item) { 42 | if (mainView != null) { 43 | mainView.showMessage(String.format("%s clicked", item)); 44 | } 45 | } 46 | 47 | void onDestroy() { 48 | mainView = null; 49 | } 50 | 51 | public void onFinished(List items) { 52 | if (mainView != null) { 53 | mainView.setItems(items); 54 | mainView.hideProgress(); 55 | } 56 | } 57 | 58 | public MainView getMainView() { 59 | return mainView; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/antonioleiva/mvpexample/app/main/MainView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2018 Antonio Leiva Gordillo. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.antonioleiva.mvpexample.app.main; 20 | 21 | import java.util.List; 22 | 23 | public interface MainView { 24 | 25 | void showProgress(); 26 | 27 | void hideProgress(); 28 | 29 | void setItems(List items); 30 | 31 | void showMessage(String message); 32 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniolg/androidmvp/b7056a68806f723885c4c0e83853b7fdfc34e9ce/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniolg/androidmvp/b7056a68806f723885c4c0e83853b7fdfc34e9ce/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniolg/androidmvp/b7056a68806f723885c4c0e83853b7fdfc34e9ce/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniolg/androidmvp/b7056a68806f723885c4c0e83853b7fdfc34e9ce/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_password.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_username.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 18 | 19 | 28 | 29 |