├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── _wiki ├── AndroidCleanArchitectureWithMVP_emulator.png ├── clean_architecture_layers.png ├── clean_architecture_layers.xcf ├── clean_architecture_layers_onion_detail.png ├── clean_architecture_layers_onion_detail.xcf ├── clean_architecture_layers_onion_overview.png ├── clean_architecture_layers_onion_overview.xcf ├── clean_architecture_presentation_layer.png └── gradle_modules.png ├── build.gradle ├── data-layer ├── .gitignore ├── build.gradle └── src │ ├── main │ └── java │ │ └── ro │ │ └── ovidiuconeac │ │ └── datalayer │ │ ├── dao │ │ ├── CheesesDao.java │ │ ├── CheesesDaoImpl.java │ │ ├── FruitsDao.java │ │ ├── FruitsDaoImpl.java │ │ ├── SweetsDao.java │ │ └── SweetsDaoImpl.java │ │ └── database │ │ ├── Database.java │ │ └── DatabaseImpl.java │ └── test │ └── java │ └── ro │ └── ovidiuconeac │ └── datalayer │ ├── dao │ ├── TestCheesesDaoImpl.java │ ├── TestFruitsDaoImpl.java │ └── TestSweetsDaoImpl.java │ └── database │ └── DatabaseImplTest.java ├── domain-layer ├── .gitignore ├── build.gradle └── src │ ├── main │ └── java │ │ └── ro │ │ └── ovidiuconeaac │ │ └── businesslayer │ │ └── beans │ │ ├── CheeseBean.java │ │ ├── CheeseBeanImpl.java │ │ ├── FruitsBean.java │ │ ├── FruitsBeanImpl.java │ │ ├── SweetsBean.java │ │ └── SweetsBeanImpl.java │ └── test │ └── java │ └── ro │ └── ovidiuconeaac │ └── businesslayer │ └── beans │ ├── TestCheeseBean.java │ ├── TestFruitsBean.java │ └── TestSweetsBean.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── models ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── ro │ └── ovidiuconeac │ └── models │ ├── Cheese.java │ ├── Food.java │ ├── Fruit.java │ └── Sweet.java ├── presentation-layer ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── ro │ │ │ └── ovidiuconeac │ │ │ └── presentationlayer │ │ │ ├── cache │ │ │ └── Cache.java │ │ │ ├── common │ │ │ └── Util.java │ │ │ └── features │ │ │ ├── Presenter.java │ │ │ ├── Screen.java │ │ │ └── featurex │ │ │ ├── model │ │ │ └── MainUseCases.java │ │ │ ├── presenter │ │ │ ├── MainPresenter.java │ │ │ └── MainPresenterImpl.java │ │ │ └── view │ │ │ ├── MainActivity.java │ │ │ └── MainView.java │ └── res │ │ ├── layout-land │ │ └── activity_main.xml │ │ ├── layout │ │ └── activity_main.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 │ └── ro │ └── ovidiuconeac │ └── presentationlayer │ └── features │ └── featurex │ ├── model │ └── MainUseCasesTest.java │ └── presenter │ └── MainPresenterImplTest.java └── 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 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 36 | 37 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 84 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 111 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidCleanArchitectureWithMVP 2 | Clean architecture in Android with MVP presentation layer. 3 | 4 | The problem 5 | ------- 6 | How to design a simple, decoupled, testable [clean architecture](https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html) for Android, using [MVP](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter) for the presentation layer. 7 | 8 | The solution 9 | ------- 10 | Systems based on clean architecture have the following characteristics: separation of concerns, multilayered, independent of frameworks, independent of user interfaces, independent of databases, testable by layer, with a dependency rule that says that code dependencies can only point inwards, from lower leves like the presentation layer, to higher levels like the data layer. 11 | 12 | The solution is an extension based on the [boilerpart code for asynchronous MVP design for Android, without 3rd party libraries](https://github.com/ovicon/AndroidAsynchronousMVPBoilerpart). 13 | 14 | From now on, I shall only talk about clean architecture, in the context of the example application: 15 | 16 | ![AndroidCleanArchitectureWithMVP](http://www.ovidiuconeac.ro/wp-content/uploads/2017/02/AndroidCleanArchitectureWithMVP_emulator-169x300.png "AndroidCleanArchitectureWithMVP") 17 | 18 | The example application is based on the following 3-tier clean arhitecture: the presentation layer, the domain layer, and the data layer. 19 | ![Generic 3-tier clean architecture](http://www.ovidiuconeac.ro/wp-content/uploads/2017/02/clean_architecture_layers.png "Generic 3-tier clean architecture") 20 | 21 | The presentation layer is implemented as an MVP design, the domain layer contains plain Java objects, and the data layer is just a dummy singleton implementation for a database. 22 | 23 | You can have as many layers as needed, and there is not rule that says that clean architecture should be implemented with only three layers. 24 | 25 | The presentation layer represents the application, and is an Android phone & table module. The domain layer and data layer are Java libraries, and used as gradle dependencies. The presentation layer depends of the domain layer and the domain layer depends on the data layer. There is also an extra module, named models, that contains the models of the application. For the sake of simplicity, these models are used in all the layers of the application. In a more complex application usually each layer has its own data modeling. 26 | 27 | ![Modules in Android Studio](http://www.ovidiuconeac.ro/wp-content/uploads/2017/02/gradle_modules.png "Modules in Android Studio") 28 | 29 | Because gradle manages dependencies between layers, the models module is only defined as a dependency in the data layer, which is used as a dependency in the domain layer, which in turn is used as a dependency in the presentation layer. In this way, the models dependency becomes available in all the layers. It is called transitivity, and it is how gradle handles dependencies. 30 | 31 | To better understand the details of the 3-tier clean architecture with MVP, let's see another overview, but from a different perspective. 32 | 33 | ![Clean architecture layers details](http://www.ovidiuconeac.ro/wp-content/uploads/2017/02/clean_architecture_layers_onion_detail.png "Clean architecture layers details") 34 | 35 | This is the same as the previous overview, but with more details about the presentation layer, which on its own contains three layers: the view, the presenter and the model. 36 | 37 | ![Presentation layer](http://www.ovidiuconeac.ro/wp-content/uploads/2017/02/clean_architecture_presentation_layer.png "Presentation layer") 38 | 39 | Following the dependency rule, the architecture is layered as follows: the presentation layer (the view, the presenter, the model), the domain layer and the data layer. 40 | 41 | The model in the presenter layer is also known as "uses cases" or "interactor". These use cases, or model, or interactor, or whatever you want to call it, represent application specific business rules. These use cases orchestrate the flow of data between the presentation layer and the domain layer, and they are specific to the application and not the domain. You can think of them, as what the application does, what the presentation layer does. Remember that the presentation layer can be anything from an android aplication, a web application, a standalone application, etc. 42 | 43 | The idea of clean architecture is to have separation of concerns. In our example the presentation layer contains everything that is android related, the domain layer contains plain java objects that represent the business entities, and the data layer contains everything that is database related. 44 | 45 | In a clean architecture, is should be possible to replace each layer with a different implementation of the same interface, and this should make no difference on the other layers. 46 | 47 | The conclusion 48 | ------- 49 | Having separation of concerns in an application might seem like a no-brainier. Yet it is surprisingly difficult to achieve. The best way to start is to understand the purpose of the clean architecture and just practice. 50 | 51 | The code of this example application can be used as boilerpart code, upon which to build your own application. 52 | 53 | See also a short video discussion about this solution [here](https://www.youtube.com/watch?v=btyF_Zl7uAk&feature=youtu.be). 54 | 55 | I do encourage further reading on this topic, starting with these excellent resources: [The Clean Architecture](https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html), [Architecting Android…The clean way?](http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/), [Robert C Martin - Clean Architecture and Design](https://www.youtube.com/watch?v=Nsjsiz2A9mg&t=9s). 56 | 57 | License 58 | ------- 59 | 60 | Copyright 2017 Ovidiu CONEAC 61 | 62 | Licensed under the Apache License, Version 2.0 (the "License"); 63 | you may not use this file except in compliance with the License. 64 | You may obtain a copy of the License at 65 | 66 | http://www.apache.org/licenses/LICENSE-2.0 67 | 68 | Unless required by applicable law or agreed to in writing, software 69 | distributed under the License is distributed on an "AS IS" BASIS, 70 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 71 | See the License for the specific language governing permissions and 72 | limitations under the License. 73 | -------------------------------------------------------------------------------- /_wiki/AndroidCleanArchitectureWithMVP_emulator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/_wiki/AndroidCleanArchitectureWithMVP_emulator.png -------------------------------------------------------------------------------- /_wiki/clean_architecture_layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/_wiki/clean_architecture_layers.png -------------------------------------------------------------------------------- /_wiki/clean_architecture_layers.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/_wiki/clean_architecture_layers.xcf -------------------------------------------------------------------------------- /_wiki/clean_architecture_layers_onion_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/_wiki/clean_architecture_layers_onion_detail.png -------------------------------------------------------------------------------- /_wiki/clean_architecture_layers_onion_detail.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/_wiki/clean_architecture_layers_onion_detail.xcf -------------------------------------------------------------------------------- /_wiki/clean_architecture_layers_onion_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/_wiki/clean_architecture_layers_onion_overview.png -------------------------------------------------------------------------------- /_wiki/clean_architecture_layers_onion_overview.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/_wiki/clean_architecture_layers_onion_overview.xcf -------------------------------------------------------------------------------- /_wiki/clean_architecture_presentation_layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/_wiki/clean_architecture_presentation_layer.png -------------------------------------------------------------------------------- /_wiki/gradle_modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/_wiki/gradle_modules.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /data-layer/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /data-layer/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile fileTree(dir: 'libs', include: ['*.jar']) 5 | // Modules 6 | compile project(':models') 7 | testCompile 'junit:junit:4.12' 8 | } 9 | 10 | sourceCompatibility = "1.7" 11 | targetCompatibility = "1.7" 12 | -------------------------------------------------------------------------------- /data-layer/src/main/java/ro/ovidiuconeac/datalayer/dao/CheesesDao.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.datalayer.dao; 2 | 3 | import ro.ovidiuconeac.models.Cheese; 4 | 5 | /** 6 | * Created by ovidiu on 2/7/17. 7 | */ 8 | 9 | public interface CheesesDao { 10 | Cheese getCheeseById(int id); 11 | } 12 | -------------------------------------------------------------------------------- /data-layer/src/main/java/ro/ovidiuconeac/datalayer/dao/CheesesDaoImpl.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.datalayer.dao; 2 | 3 | import ro.ovidiuconeac.datalayer.database.Database; 4 | import ro.ovidiuconeac.datalayer.database.DatabaseImpl; 5 | import ro.ovidiuconeac.models.Cheese; 6 | 7 | /** 8 | * Created by ovidiu on 2/7/17. 9 | */ 10 | 11 | public class CheesesDaoImpl implements CheesesDao { 12 | 13 | private Database database = DatabaseImpl.getInstance(); 14 | 15 | @Override 16 | public Cheese getCheeseById(int id) { 17 | return database.getCheeseById(id); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data-layer/src/main/java/ro/ovidiuconeac/datalayer/dao/FruitsDao.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.datalayer.dao; 2 | 3 | import ro.ovidiuconeac.models.Fruit; 4 | 5 | /** 6 | * Created by ovidiu on 2/7/17. 7 | */ 8 | 9 | public interface FruitsDao { 10 | Fruit getFruitById(int id); 11 | } 12 | -------------------------------------------------------------------------------- /data-layer/src/main/java/ro/ovidiuconeac/datalayer/dao/FruitsDaoImpl.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.datalayer.dao; 2 | 3 | import ro.ovidiuconeac.datalayer.database.Database; 4 | import ro.ovidiuconeac.datalayer.database.DatabaseImpl; 5 | import ro.ovidiuconeac.models.Fruit; 6 | 7 | /** 8 | * Created by ovidiu on 2/7/17. 9 | */ 10 | 11 | public class FruitsDaoImpl implements FruitsDao { 12 | 13 | private Database database = DatabaseImpl.getInstance(); 14 | 15 | @Override 16 | public Fruit getFruitById(int id) { 17 | return database.getFruitById(id); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data-layer/src/main/java/ro/ovidiuconeac/datalayer/dao/SweetsDao.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.datalayer.dao; 2 | 3 | import ro.ovidiuconeac.models.Sweet; 4 | 5 | /** 6 | * Created by ovidiu on 2/7/17. 7 | */ 8 | 9 | public interface SweetsDao { 10 | Sweet getSweetById(int id); 11 | } 12 | -------------------------------------------------------------------------------- /data-layer/src/main/java/ro/ovidiuconeac/datalayer/dao/SweetsDaoImpl.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.datalayer.dao; 2 | 3 | import ro.ovidiuconeac.datalayer.database.Database; 4 | import ro.ovidiuconeac.datalayer.database.DatabaseImpl; 5 | import ro.ovidiuconeac.models.Sweet; 6 | 7 | /** 8 | * Created by ovidiu on 2/7/17. 9 | */ 10 | 11 | public class SweetsDaoImpl implements SweetsDao { 12 | 13 | private Database database = DatabaseImpl.getInstance(); 14 | 15 | @Override 16 | public Sweet getSweetById(int id) { 17 | return database.getSweetById(id); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data-layer/src/main/java/ro/ovidiuconeac/datalayer/database/Database.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.datalayer.database; 2 | 3 | import ro.ovidiuconeac.models.Cheese; 4 | import ro.ovidiuconeac.models.Fruit; 5 | import ro.ovidiuconeac.models.Sweet; 6 | 7 | /** 8 | * Created by ovidiu on 2/7/17. 9 | */ 10 | 11 | public interface Database { 12 | Fruit getFruitById(int id); 13 | Cheese getCheeseById(int id); 14 | Sweet getSweetById(int id); 15 | } 16 | -------------------------------------------------------------------------------- /data-layer/src/main/java/ro/ovidiuconeac/datalayer/database/DatabaseImpl.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.datalayer.database; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import ro.ovidiuconeac.models.Cheese; 7 | import ro.ovidiuconeac.models.Fruit; 8 | import ro.ovidiuconeac.models.Sweet; 9 | 10 | /** 11 | * Created by ovidiu on 2/7/17. 12 | */ 13 | 14 | public final class DatabaseImpl implements Database { 15 | 16 | private static DatabaseImpl INSTANCE; 17 | 18 | private List fruits; 19 | private List cheeses; 20 | private List sweets; 21 | 22 | private DatabaseImpl() { 23 | initFruits(); 24 | initCheeses(); 25 | initSweets(); 26 | } 27 | 28 | private void initSweets() { 29 | sweets = new ArrayList<>(); 30 | sweets.add(new Sweet("Apple cake")); 31 | sweets.add(new Sweet("Avocado cake")); 32 | sweets.add(new Sweet("Babka")); 33 | sweets.add(new Sweet("Battenberg cake")); 34 | sweets.add(new Sweet("Bienenstic")); 35 | sweets.add(new Sweet("Cassata")); 36 | sweets.add(new Sweet("Cheesecake")); 37 | sweets.add(new Sweet("Cupcake")); 38 | sweets.add(new Sweet("Dacquoise")); 39 | sweets.add(new Sweet("Dobos cake")); 40 | sweets.add(new Sweet("Jaffa Cakes")); 41 | sweets.add(new Sweet("Makowiec")); 42 | sweets.add(new Sweet("Muffin")); 43 | sweets.add(new Sweet("Pancake")); 44 | sweets.add(new Sweet("Rum cake")); 45 | } 46 | 47 | public static DatabaseImpl getInstance() { 48 | if (DatabaseImpl.INSTANCE == null) { 49 | DatabaseImpl.INSTANCE = new DatabaseImpl(); 50 | } 51 | return DatabaseImpl.INSTANCE; 52 | } 53 | 54 | private void initCheeses() { 55 | cheeses = new ArrayList<>(); 56 | cheeses.add(new Cheese("Abondance")); 57 | cheeses.add(new Cheese("Allgauer Emmentaler")); 58 | cheeses.add(new Cheese("Banon")); 59 | cheeses.add(new Cheese("Beaufort")); 60 | cheeses.add(new Cheese("Bethmale")); 61 | cheeses.add(new Cheese("Brie")); 62 | cheeses.add(new Cheese("Cheddar")); 63 | cheeses.add(new Cheese("Emmental")); 64 | cheeses.add(new Cheese("Feta")); 65 | cheeses.add(new Cheese("Gorgonzola")); 66 | cheeses.add(new Cheese("Gouda")); 67 | cheeses.add(new Cheese("Munster")); 68 | cheeses.add(new Cheese("Roquefort")); 69 | cheeses.add(new Cheese("Stilton")); 70 | cheeses.add(new Cheese("Valancay")); 71 | } 72 | 73 | private void initFruits() { 74 | fruits = new ArrayList<>(); 75 | fruits.add(new Fruit("Apple")); 76 | fruits.add(new Fruit("Blackcurrant")); 77 | fruits.add(new Fruit("Cherries")); 78 | fruits.add(new Fruit("Feijoa")); 79 | fruits.add(new Fruit("Guava")); 80 | fruits.add(new Fruit("Honeydew Melon")); 81 | fruits.add(new Fruit("Java-Plum")); 82 | fruits.add(new Fruit("Lime")); 83 | fruits.add(new Fruit("Lychee")); 84 | fruits.add(new Fruit("Mulberries")); 85 | fruits.add(new Fruit("Prunes")); 86 | fruits.add(new Fruit("Rhubarb")); 87 | fruits.add(new Fruit("Strawberries")); 88 | fruits.add(new Fruit("Tangerine")); 89 | fruits.add(new Fruit("Watermelon")); 90 | } 91 | 92 | public Fruit getFruitById(int id) { 93 | return fruits.get(id); 94 | } 95 | 96 | public Cheese getCheeseById(int id) { 97 | return cheeses.get(id); 98 | } 99 | 100 | public Sweet getSweetById(int id) { 101 | return sweets.get(id); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /data-layer/src/test/java/ro/ovidiuconeac/datalayer/dao/TestCheesesDaoImpl.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.datalayer.dao; 2 | 3 | import org.junit.After; 4 | import static org.junit.Assert.assertNotNull; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.Random; 9 | 10 | /** 11 | * Created by ovidiu on 2/7/17. 12 | */ 13 | 14 | public class TestCheesesDaoImpl { 15 | 16 | private CheesesDao dao; 17 | private int id; 18 | 19 | @Before 20 | public void setUp() { 21 | dao = new CheesesDaoImpl(); 22 | id = new Random().nextInt(14); 23 | } 24 | 25 | @After 26 | public void tearDown() { 27 | dao = null; 28 | } 29 | 30 | @Test 31 | public void whenIdCorrectGetCheeseById() { 32 | assertNotNull(dao.getCheeseById(id)); 33 | } 34 | 35 | @Test(expected = IndexOutOfBoundsException.class) 36 | public void whenIdWrongThrowException() { 37 | dao.getCheeseById(-1); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /data-layer/src/test/java/ro/ovidiuconeac/datalayer/dao/TestFruitsDaoImpl.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.datalayer.dao; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.util.Random; 8 | 9 | import static org.junit.Assert.assertNotNull; 10 | 11 | /** 12 | * Created by ovidiu on 2/7/17. 13 | */ 14 | 15 | public class TestFruitsDaoImpl { 16 | 17 | private FruitsDao dao; 18 | private int id; 19 | 20 | @Before 21 | public void setUp() { 22 | dao = new FruitsDaoImpl(); 23 | id = new Random().nextInt(14); 24 | } 25 | 26 | @After 27 | public void tearDown() { 28 | dao = null; 29 | } 30 | 31 | @Test 32 | public void whenIdCorrectGetFruitById() { 33 | assertNotNull(dao.getFruitById(id)); 34 | } 35 | 36 | @Test(expected = IndexOutOfBoundsException.class) 37 | public void whenIdWrongThrowException() { 38 | dao.getFruitById(-1); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /data-layer/src/test/java/ro/ovidiuconeac/datalayer/dao/TestSweetsDaoImpl.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.datalayer.dao; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.util.Random; 8 | 9 | import static org.junit.Assert.assertNotNull; 10 | 11 | /** 12 | * Created by ovidiu on 2/7/17. 13 | */ 14 | 15 | public class TestSweetsDaoImpl { 16 | 17 | private SweetsDao dao; 18 | private int id; 19 | 20 | @Before 21 | public void setUp() { 22 | dao = new SweetsDaoImpl(); 23 | id = new Random().nextInt(14); 24 | } 25 | 26 | @After 27 | public void tearDown() { 28 | dao = null; 29 | } 30 | 31 | @Test 32 | public void whenIdCorrectGetFruitById() { 33 | assertNotNull(dao.getSweetById(id)); 34 | } 35 | 36 | @Test(expected = IndexOutOfBoundsException.class) 37 | public void whenIdWrongThrowException() { 38 | dao.getSweetById(-1); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /data-layer/src/test/java/ro/ovidiuconeac/datalayer/database/DatabaseImplTest.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.datalayer.database; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.util.Random; 8 | 9 | import static org.junit.Assert.assertNotNull; 10 | 11 | /** 12 | * Created by ovidiu on 2/7/17. 13 | */ 14 | 15 | public class DatabaseImplTest { 16 | 17 | private DatabaseImpl database; 18 | private int id; 19 | 20 | @Before 21 | public void setUp() { 22 | database = DatabaseImpl.getInstance(); 23 | } 24 | 25 | @After 26 | public void tearDown() { 27 | id = new Random().nextInt(14); 28 | } 29 | 30 | @Test 31 | public void whenCorrectIdGetFruitFromDatabase() { 32 | assertNotNull(database.getFruitById(id)); 33 | } 34 | 35 | @Test(expected = IndexOutOfBoundsException.class) 36 | public void whenWrongIdForFruitThrowExpectedException() { 37 | database.getFruitById(16); 38 | } 39 | 40 | @Test 41 | public void whenCorrectIdGetCheeseFromDatabase() { 42 | assertNotNull(database.getCheeseById(id)); 43 | } 44 | 45 | @Test(expected = IndexOutOfBoundsException.class) 46 | public void whenWrongIdForCheeseThrowExpectedException() { 47 | database.getCheeseById(-1); 48 | } 49 | 50 | @Test 51 | public void whenCorrectIdGetSweetFromDatabase() { 52 | assertNotNull(database.getSweetById(id)); 53 | } 54 | 55 | @Test(expected = IndexOutOfBoundsException.class) 56 | public void whenWrongIdForSweetThrowExpectedException() { 57 | database.getSweetById(123); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /domain-layer/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /domain-layer/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile fileTree(dir: 'libs', include: ['*.jar']) 5 | testCompile 'junit:junit:4.12' 6 | // Modules 7 | compile project(':data-layer') 8 | } 9 | 10 | sourceCompatibility = "1.7" 11 | targetCompatibility = "1.7" 12 | -------------------------------------------------------------------------------- /domain-layer/src/main/java/ro/ovidiuconeaac/businesslayer/beans/CheeseBean.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeaac.businesslayer.beans; 2 | 3 | import ro.ovidiuconeac.models.Cheese; 4 | 5 | /** 6 | * Created by ovidiu on 2/7/17. 7 | */ 8 | 9 | public interface CheeseBean { 10 | Cheese getRandomCheese(); 11 | } 12 | -------------------------------------------------------------------------------- /domain-layer/src/main/java/ro/ovidiuconeaac/businesslayer/beans/CheeseBeanImpl.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeaac.businesslayer.beans; 2 | 3 | import java.util.Random; 4 | 5 | import ro.ovidiuconeac.datalayer.dao.CheesesDao; 6 | import ro.ovidiuconeac.datalayer.dao.CheesesDaoImpl; 7 | import ro.ovidiuconeac.models.Cheese; 8 | 9 | /** 10 | * Created by ovidiu on 2/7/17. 11 | */ 12 | 13 | public class CheeseBeanImpl implements CheeseBean { 14 | 15 | private CheesesDao dao; 16 | 17 | public CheeseBeanImpl() { 18 | dao = new CheesesDaoImpl(); 19 | } 20 | 21 | @Override 22 | public Cheese getRandomCheese() { 23 | int id = new Random().nextInt(14); 24 | return dao.getCheeseById(id); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /domain-layer/src/main/java/ro/ovidiuconeaac/businesslayer/beans/FruitsBean.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeaac.businesslayer.beans; 2 | 3 | import ro.ovidiuconeac.models.Fruit; 4 | 5 | /** 6 | * Created by ovidiu on 2/7/17. 7 | */ 8 | 9 | public interface FruitsBean { 10 | Fruit getRandomFruit(); 11 | } 12 | -------------------------------------------------------------------------------- /domain-layer/src/main/java/ro/ovidiuconeaac/businesslayer/beans/FruitsBeanImpl.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeaac.businesslayer.beans; 2 | 3 | import java.util.Random; 4 | 5 | import ro.ovidiuconeac.datalayer.dao.FruitsDao; 6 | import ro.ovidiuconeac.datalayer.dao.FruitsDaoImpl; 7 | import ro.ovidiuconeac.models.Fruit; 8 | 9 | /** 10 | * Created by ovidiu on 2/7/17. 11 | */ 12 | 13 | public class FruitsBeanImpl implements FruitsBean { 14 | 15 | private FruitsDao dao; 16 | 17 | public FruitsBeanImpl() { 18 | dao = new FruitsDaoImpl(); 19 | } 20 | 21 | @Override 22 | public Fruit getRandomFruit() { 23 | int id = new Random().nextInt(14); 24 | return dao.getFruitById(id); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /domain-layer/src/main/java/ro/ovidiuconeaac/businesslayer/beans/SweetsBean.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeaac.businesslayer.beans; 2 | 3 | import ro.ovidiuconeac.models.Sweet; 4 | 5 | /** 6 | * Created by ovidiu on 2/7/17. 7 | */ 8 | 9 | public interface SweetsBean { 10 | Sweet getRandomSweet(); 11 | } 12 | -------------------------------------------------------------------------------- /domain-layer/src/main/java/ro/ovidiuconeaac/businesslayer/beans/SweetsBeanImpl.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeaac.businesslayer.beans; 2 | 3 | import java.util.Random; 4 | 5 | import ro.ovidiuconeac.datalayer.dao.SweetsDao; 6 | import ro.ovidiuconeac.datalayer.dao.SweetsDaoImpl; 7 | import ro.ovidiuconeac.models.Sweet; 8 | 9 | /** 10 | * Created by ovidiu on 2/7/17. 11 | */ 12 | 13 | public class SweetsBeanImpl implements SweetsBean { 14 | 15 | private SweetsDao dao; 16 | 17 | public SweetsBeanImpl() { 18 | dao = new SweetsDaoImpl(); 19 | } 20 | 21 | @Override 22 | public Sweet getRandomSweet() { 23 | int id = new Random().nextInt(14); 24 | return dao.getSweetById(id); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /domain-layer/src/test/java/ro/ovidiuconeaac/businesslayer/beans/TestCheeseBean.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeaac.businesslayer.beans; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.assertNotNull; 8 | 9 | /** 10 | * Created by ovidiu on 2/7/17. 11 | */ 12 | 13 | public class TestCheeseBean { 14 | 15 | private CheeseBeanImpl cheeseBean; 16 | 17 | @Before 18 | public void setUp() { 19 | cheeseBean = new CheeseBeanImpl(); 20 | } 21 | 22 | @After 23 | public void tearDown() { 24 | cheeseBean = null; 25 | } 26 | 27 | @Test 28 | public void getFruit() { 29 | assertNotNull(cheeseBean.getRandomCheese()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /domain-layer/src/test/java/ro/ovidiuconeaac/businesslayer/beans/TestFruitsBean.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeaac.businesslayer.beans; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.assertNotNull; 8 | 9 | /** 10 | * Created by ovidiu on 2/7/17. 11 | */ 12 | 13 | public class TestFruitsBean { 14 | 15 | private FruitsBeanImpl fruitsBean; 16 | 17 | @Before 18 | public void setUp() { 19 | fruitsBean = new FruitsBeanImpl(); 20 | } 21 | 22 | @After 23 | public void tearDown() { 24 | fruitsBean = null; 25 | } 26 | 27 | @Test 28 | public void getFruit() { 29 | assertNotNull(fruitsBean.getRandomFruit()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /domain-layer/src/test/java/ro/ovidiuconeaac/businesslayer/beans/TestSweetsBean.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeaac.businesslayer.beans; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.assertNotNull; 8 | 9 | /** 10 | * Created by ovidiu on 2/7/17. 11 | */ 12 | 13 | public class TestSweetsBean { 14 | 15 | private SweetsBeanImpl sweetsBean; 16 | 17 | @Before 18 | public void setUp() { 19 | sweetsBean = new SweetsBeanImpl(); 20 | } 21 | 22 | @After 23 | public void tearDown() { 24 | sweetsBean = null; 25 | } 26 | 27 | @Test 28 | public void getFruit() { 29 | assertNotNull(sweetsBean.getRandomSweet()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 09 15:16:29 EET 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /models/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /models/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile fileTree(dir: 'libs', include: ['*.jar']) 5 | } 6 | 7 | sourceCompatibility = "1.7" 8 | targetCompatibility = "1.7" 9 | -------------------------------------------------------------------------------- /models/src/main/java/ro/ovidiuconeac/models/Cheese.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.models; 2 | 3 | /** 4 | * Created by ovidiu on 2/7/17. 5 | */ 6 | 7 | public class Cheese extends Food { 8 | 9 | public Cheese(String name) { 10 | this.name = name; 11 | } 12 | 13 | public String getName() { 14 | return name; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /models/src/main/java/ro/ovidiuconeac/models/Food.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.models; 2 | 3 | /** 4 | * Created by ovidiu on 2/7/17. 5 | */ 6 | 7 | public abstract class Food { 8 | 9 | protected String name; 10 | 11 | @Override 12 | public boolean equals(Object o) { 13 | if (this == o) return true; 14 | if (o == null || getClass() != o.getClass()) return false; 15 | 16 | Food food = (Food) o; 17 | 18 | return name != null ? name.equals(food.name) : food.name == null; 19 | 20 | } 21 | 22 | @Override 23 | public int hashCode() { 24 | return name != null ? name.hashCode() : 0; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /models/src/main/java/ro/ovidiuconeac/models/Fruit.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.models; 2 | 3 | /** 4 | * Created by ovidiu on 2/7/17. 5 | */ 6 | 7 | public class Fruit extends Food { 8 | 9 | public Fruit(String name) { 10 | this.name = name; 11 | } 12 | 13 | public String getName() { 14 | return name; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /models/src/main/java/ro/ovidiuconeac/models/Sweet.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.models; 2 | 3 | /** 4 | * Created by ovidiu on 2/7/17. 5 | */ 6 | 7 | public class Sweet extends Food { 8 | 9 | public Sweet(String name) { 10 | this.name = name; 11 | } 12 | 13 | public String getName() { 14 | return name; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /presentation-layer/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /presentation-layer/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "ro.ovidiuconeac.androidcleanarchitecturewithmvp" 8 | minSdkVersion 16 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.1.1' 28 | testCompile 'junit:junit:4.12' 29 | testCompile "org.mockito:mockito-core:1.10.19" 30 | // Modules 31 | compile project(':domain-layer') 32 | // Butterknife 33 | compile 'com.jakewharton:butterknife:8.5.1' 34 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' 35 | } 36 | -------------------------------------------------------------------------------- /presentation-layer/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/ovidiu/Developer/android-sdk-linux/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 | -------------------------------------------------------------------------------- /presentation-layer/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /presentation-layer/src/main/java/ro/ovidiuconeac/presentationlayer/cache/Cache.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.presentationlayer.cache; 2 | 3 | import java.io.Serializable; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.UUID; 7 | 8 | import ro.ovidiuconeac.presentationlayer.features.Presenter; 9 | 10 | /** 11 | * Created by ovidiu on 2/2/17. 12 | * 13 | * The purpose of this class is to provide a caching 14 | * mechanism to persist presenters during activity 15 | * orientation change. 16 | * 17 | * A disadvantage of the caching class is that it 18 | * never deletes the unused presenters. 19 | * 20 | * A solution, e.g. a daemon, must be implemented 21 | * to remove the unused presenters. 22 | */ 23 | public class Cache implements Serializable { 24 | 25 | private static Cache ourInstance; 26 | private Map cache; 27 | 28 | public static Cache getInstance() { 29 | if (ourInstance == null) { 30 | ourInstance = new Cache(); 31 | } 32 | return ourInstance; 33 | } 34 | 35 | private Cache() { 36 | cache = new HashMap<>(); 37 | } 38 | 39 | public void cachePresenterFor(UUID uuid, Presenter presenter) { 40 | cache.put(uuid, presenter); 41 | } 42 | 43 | public Presenter restorePresenterFor(UUID uuid) { 44 | Presenter presenter = cache.get(uuid); 45 | cache.remove(presenter); 46 | return presenter; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /presentation-layer/src/main/java/ro/ovidiuconeac/presentationlayer/common/Util.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.presentationlayer.common; 2 | 3 | /** 4 | * Created by ovidiu on 12/29/16. 5 | * 6 | * A utility class whose purpose it only to simulate 7 | * a network latency. What is does it to delay the 8 | * running thread by the specified number of milliseconds. 9 | */ 10 | 11 | public abstract class Util { 12 | 13 | public static void simulateNetworkLatency(int millis) { 14 | // Pause current thread 15 | try { 16 | Thread.sleep(millis); 17 | } catch (InterruptedException e) { 18 | e.printStackTrace(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /presentation-layer/src/main/java/ro/ovidiuconeac/presentationlayer/features/Presenter.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.presentationlayer.features; 2 | 3 | import java.util.UUID; 4 | 5 | /** 6 | * Created by ovidiu on 2/2/17. 7 | * 8 | * Common presenter interface. 9 | */ 10 | 11 | public interface Presenter { 12 | 13 | /* 14 | The method is used to restore the view 15 | reference in the presenter, after an 16 | orientation change 17 | */ 18 | void setScreen(Screen screen); 19 | 20 | /* 21 | The UUID is used to save and restore 22 | the presenter instance during an 23 | orientation change 24 | */ 25 | UUID getUuid(); 26 | } 27 | -------------------------------------------------------------------------------- /presentation-layer/src/main/java/ro/ovidiuconeac/presentationlayer/features/Screen.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.presentationlayer.features; 2 | 3 | import java.util.UUID; 4 | 5 | /** 6 | * Created by ovidiu on 2/2/17. 7 | * 8 | * Common screen interface. 9 | * 10 | * The purpose of this interface is to facilitate 11 | * the creation of the {@link Presenter} interface's 12 | * {@link Presenter#setScreen(Screen)} method. 13 | */ 14 | 15 | public interface Screen { 16 | void cachePresenter(Presenter presenter); 17 | void restorePresenter(UUID uuid); 18 | } 19 | -------------------------------------------------------------------------------- /presentation-layer/src/main/java/ro/ovidiuconeac/presentationlayer/features/featurex/model/MainUseCases.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.presentationlayer.features.featurex.model; 2 | 3 | import ro.ovidiuconeaac.businesslayer.beans.CheeseBean; 4 | import ro.ovidiuconeaac.businesslayer.beans.CheeseBeanImpl; 5 | import ro.ovidiuconeaac.businesslayer.beans.FruitsBean; 6 | import ro.ovidiuconeaac.businesslayer.beans.FruitsBeanImpl; 7 | import ro.ovidiuconeaac.businesslayer.beans.SweetsBean; 8 | import ro.ovidiuconeaac.businesslayer.beans.SweetsBeanImpl; 9 | import ro.ovidiuconeac.models.Cheese; 10 | import ro.ovidiuconeac.models.Fruit; 11 | import ro.ovidiuconeac.models.Sweet; 12 | 13 | /** 14 | * Created by ovidiu on 2/6/17. 15 | */ 16 | 17 | public class MainUseCases { 18 | 19 | private FruitsBean fruitBean; 20 | private CheeseBean cheeseBean; 21 | private SweetsBean sweetsBean; 22 | 23 | public MainUseCases() { 24 | this.fruitBean = new FruitsBeanImpl(); 25 | this.cheeseBean = new CheeseBeanImpl(); 26 | this.sweetsBean = new SweetsBeanImpl(); 27 | } 28 | 29 | public Fruit getFruit1() { 30 | return fruitBean.getRandomFruit(); 31 | } 32 | 33 | public Fruit getFruit2() { 34 | return fruitBean.getRandomFruit(); 35 | } 36 | 37 | public Cheese getCheese1() { 38 | return cheeseBean.getRandomCheese(); 39 | } 40 | 41 | public Cheese getCheese2() { 42 | return cheeseBean.getRandomCheese(); 43 | } 44 | 45 | public Sweet getSweet1() { 46 | return sweetsBean.getRandomSweet(); 47 | } 48 | 49 | public Sweet getSweet2() { 50 | return sweetsBean.getRandomSweet(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /presentation-layer/src/main/java/ro/ovidiuconeac/presentationlayer/features/featurex/presenter/MainPresenter.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.presentationlayer.features.featurex.presenter; 2 | 3 | import ro.ovidiuconeac.presentationlayer.features.Presenter; 4 | 5 | /** 6 | * Created by ovidiu on 2/6/17. 7 | */ 8 | 9 | public interface MainPresenter extends Presenter { 10 | void requestFruit1(); 11 | void requestFruit2(); 12 | void requestCheese1(); 13 | void requestCheese2(); 14 | void requestSweet1(); 15 | void requestSweet2(); 16 | } 17 | -------------------------------------------------------------------------------- /presentation-layer/src/main/java/ro/ovidiuconeac/presentationlayer/features/featurex/presenter/MainPresenterImpl.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.presentationlayer.features.featurex.presenter; 2 | 3 | import android.os.AsyncTask; 4 | 5 | import java.util.UUID; 6 | 7 | import ro.ovidiuconeac.models.Cheese; 8 | import ro.ovidiuconeac.models.Fruit; 9 | import ro.ovidiuconeac.models.Sweet; 10 | import ro.ovidiuconeac.presentationlayer.common.Util; 11 | import ro.ovidiuconeac.presentationlayer.features.Screen; 12 | import ro.ovidiuconeac.presentationlayer.features.featurex.model.MainUseCases; 13 | import ro.ovidiuconeac.presentationlayer.features.featurex.view.MainView; 14 | 15 | /** 16 | * Created by ovidiu on 2/6/17. 17 | */ 18 | 19 | public class MainPresenterImpl implements MainPresenter { 20 | 21 | private UUID uuid; 22 | private MainView view; 23 | private MainUseCases model; 24 | 25 | public MainPresenterImpl(MainView view) { 26 | this.uuid = UUID.randomUUID(); 27 | this.view = view; 28 | this.model = new MainUseCases(); 29 | } 30 | 31 | @Override 32 | public void requestFruit1() { 33 | new AsyncTask() { 34 | 35 | @Override 36 | protected Fruit doInBackground(Void... params) { 37 | Util.simulateNetworkLatency(4000); 38 | return model.getFruit1(); 39 | } 40 | 41 | @Override 42 | protected void onPostExecute(Fruit fruit) { 43 | super.onPostExecute(fruit); 44 | view.postFruit1(fruit.getName()); 45 | } 46 | }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 47 | } 48 | 49 | @Override 50 | public void requestFruit2() { 51 | new AsyncTask() { 52 | 53 | @Override 54 | protected Fruit doInBackground(Void... params) { 55 | Util.simulateNetworkLatency(4000); 56 | return model.getFruit2(); 57 | } 58 | 59 | @Override 60 | protected void onPostExecute(Fruit fruit) { 61 | super.onPostExecute(fruit); 62 | view.postFruit2(fruit.getName()); 63 | } 64 | }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 65 | } 66 | 67 | @Override 68 | public void requestCheese1() { 69 | new AsyncTask() { 70 | 71 | @Override 72 | protected Cheese doInBackground(Void... params) { 73 | Util.simulateNetworkLatency(4000); 74 | return model.getCheese1(); 75 | } 76 | 77 | @Override 78 | protected void onPostExecute(Cheese cheese) { 79 | super.onPostExecute(cheese); 80 | view.postCheese1(cheese.getName()); 81 | } 82 | }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 83 | } 84 | 85 | @Override 86 | public void requestCheese2() { 87 | new AsyncTask() { 88 | 89 | @Override 90 | protected Cheese doInBackground(Void... params) { 91 | Util.simulateNetworkLatency(4000); 92 | return model.getCheese2(); 93 | } 94 | 95 | @Override 96 | protected void onPostExecute(Cheese cheese) { 97 | super.onPostExecute(cheese); 98 | view.postCheese2(cheese.getName()); 99 | } 100 | }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 101 | } 102 | 103 | @Override 104 | public void requestSweet1() { 105 | new AsyncTask() { 106 | 107 | @Override 108 | protected Sweet doInBackground(Void... params) { 109 | Util.simulateNetworkLatency(4000); 110 | return model.getSweet1(); 111 | } 112 | 113 | @Override 114 | protected void onPostExecute(Sweet sweet) { 115 | super.onPostExecute(sweet); 116 | view.postSweet1(sweet.getName()); 117 | } 118 | }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 119 | } 120 | 121 | @Override 122 | public void requestSweet2() { 123 | new AsyncTask() { 124 | 125 | @Override 126 | protected Sweet doInBackground(Void... params) { 127 | Util.simulateNetworkLatency(4000); 128 | return model.getSweet2(); 129 | } 130 | 131 | @Override 132 | protected void onPostExecute(Sweet sweet) { 133 | super.onPostExecute(sweet); 134 | view.postSweet2(sweet.getName()); 135 | } 136 | }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 137 | } 138 | 139 | @Override 140 | public void setScreen(Screen screen) { 141 | this.view = (MainView) screen; 142 | } 143 | 144 | @Override 145 | public UUID getUuid() { 146 | return uuid; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /presentation-layer/src/main/java/ro/ovidiuconeac/presentationlayer/features/featurex/view/MainActivity.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.presentationlayer.features.featurex.view; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.view.WindowManager; 7 | import android.widget.Button; 8 | import android.widget.EditText; 9 | import android.widget.ProgressBar; 10 | 11 | import java.util.UUID; 12 | 13 | import butterknife.BindView; 14 | import butterknife.ButterKnife; 15 | import butterknife.OnClick; 16 | import ro.ovidiuconeac.presentationlayer.R; 17 | import ro.ovidiuconeac.presentationlayer.cache.Cache; 18 | import ro.ovidiuconeac.presentationlayer.features.Presenter; 19 | import ro.ovidiuconeac.presentationlayer.features.featurex.presenter.MainPresenter; 20 | import ro.ovidiuconeac.presentationlayer.features.featurex.presenter.MainPresenterImpl; 21 | 22 | public class MainActivity extends AppCompatActivity implements MainView { 23 | 24 | // Fruit1 25 | @BindView(R.id.progressBarFruit1) 26 | ProgressBar progressBarFruit1; 27 | @BindView(R.id.fruit1) 28 | EditText fruit1; 29 | @BindView(R.id.get_fruit1) 30 | Button getFruit1; 31 | // Fruit2 32 | @BindView(R.id.progressBarFruit2) 33 | ProgressBar progressBarFruit2; 34 | @BindView(R.id.fruit2) 35 | EditText fruit2; 36 | @BindView(R.id.get_fruit2) 37 | Button getFruit2; 38 | // Cheese1 39 | @BindView(R.id.progressBarCheese1) 40 | ProgressBar progressBarCheese1; 41 | @BindView(R.id.cheese1) 42 | EditText cheese1; 43 | @BindView(R.id.get_cheese1) 44 | Button getCheese1; 45 | // Cheese2 46 | @BindView(R.id.progressBarCheese2) 47 | ProgressBar progressBarCheese2; 48 | @BindView(R.id.cheese2) 49 | EditText cheese2; 50 | @BindView(R.id.get_cheese2) 51 | Button getCheese2; 52 | // Sweet1 53 | @BindView(R.id.progressBarSweet1) 54 | ProgressBar progressBarSweet1; 55 | @BindView(R.id.sweet1) 56 | EditText sweet1; 57 | @BindView(R.id.get_sweet1) 58 | Button getSweet1; 59 | // Sweet2 60 | @BindView(R.id.progressBarSweet2) 61 | ProgressBar progressBarSweet2; 62 | @BindView(R.id.sweet2) 63 | EditText sweet2; 64 | @BindView(R.id.get_sweet2) 65 | Button getSweet2; 66 | 67 | private MainPresenter presenter; 68 | 69 | @Override 70 | protected void onCreate(Bundle savedInstanceState) { 71 | super.onCreate(savedInstanceState); 72 | setContentView(R.layout.activity_main); 73 | ButterKnife.bind(this); 74 | progressBarFruit1.setVisibility(View.INVISIBLE); 75 | progressBarFruit2.setVisibility(View.INVISIBLE); 76 | progressBarCheese1.setVisibility(View.INVISIBLE); 77 | progressBarCheese2.setVisibility(View.INVISIBLE); 78 | progressBarSweet1.setVisibility(View.INVISIBLE); 79 | progressBarSweet2.setVisibility(View.INVISIBLE); 80 | presenter = new MainPresenterImpl(this); 81 | // Hide keyboard 82 | getWindow().setSoftInputMode( 83 | WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN 84 | ); 85 | } 86 | 87 | @Override 88 | protected void onSaveInstanceState(Bundle outState) { 89 | outState.putString("presenter_uuid", presenter.getUuid().toString()); 90 | cachePresenter(presenter); 91 | // Fruit1 92 | outState.putInt("progressBarFruit1", progressBarFruit1.getVisibility()); 93 | outState.putBoolean("fruit1", fruit1.isEnabled()); 94 | outState.putString("fruit1_txt", fruit1.getText().toString()); 95 | outState.putBoolean("getFruit1", getFruit1.isEnabled()); 96 | // Fruit2 97 | outState.putInt("progressBarFruit2", progressBarFruit1.getVisibility()); 98 | outState.putBoolean("fruit2", fruit2.isEnabled()); 99 | outState.putString("fruit2_txt", fruit2.getText().toString()); 100 | outState.putBoolean("getFruit2", getFruit2.isEnabled()); 101 | // Cheese1 102 | outState.putInt("progressBarCheese1", progressBarCheese1.getVisibility()); 103 | outState.putBoolean("cheese1", cheese1.isEnabled()); 104 | outState.putString("cheese1_txt", cheese1.getText().toString()); 105 | outState.putBoolean("getCheese1", getCheese1.isEnabled()); 106 | // Cheese2 107 | outState.putInt("progressBarCheese2", progressBarCheese2.getVisibility()); 108 | outState.putBoolean("cheese2", cheese2.isEnabled()); 109 | outState.putString("cheese2_txt", cheese2.getText().toString()); 110 | outState.putBoolean("getCheese2", getCheese2.isEnabled()); 111 | // Sweet1 112 | outState.putInt("progressBarSweet1", progressBarSweet1.getVisibility()); 113 | outState.putBoolean("sweet1", sweet1.isEnabled()); 114 | outState.putString("sweet1_txt", sweet1.getText().toString()); 115 | outState.putBoolean("getSweet1", getSweet1.isEnabled()); 116 | // Sweet2 117 | outState.putInt("progressBarSweet2", progressBarSweet2.getVisibility()); 118 | outState.putBoolean("sweet2", sweet2.isEnabled()); 119 | outState.putString("sweet2_txt", sweet2.getText().toString()); 120 | outState.putBoolean("getSweet2", getSweet2.isEnabled()); 121 | super.onSaveInstanceState(outState); 122 | } 123 | 124 | @Override 125 | protected void onRestoreInstanceState(Bundle savedInstanceState) { 126 | restorePresenter(UUID.fromString(savedInstanceState.getString("presenter_uuid"))); 127 | // Fruit1 128 | progressBarFruit1.setVisibility(savedInstanceState.getInt("progressBarFruit1") == View.VISIBLE ? View.VISIBLE : View.INVISIBLE); 129 | fruit1.setEnabled(savedInstanceState.getBoolean("fruit1")); 130 | fruit1.setText(savedInstanceState.getString("fruit1_txt")); 131 | getFruit1.setEnabled(savedInstanceState.getBoolean("getFruit1")); 132 | // Fruit2 133 | progressBarFruit2.setVisibility(savedInstanceState.getInt("progressBarFruit2") == View.VISIBLE ? View.VISIBLE : View.INVISIBLE); 134 | fruit2.setEnabled(savedInstanceState.getBoolean("fruit2")); 135 | fruit1.setText(savedInstanceState.getString("fruit2_txt")); 136 | getFruit2.setEnabled(savedInstanceState.getBoolean("getFruit2")); 137 | // Cheese1 138 | progressBarCheese1.setVisibility(savedInstanceState.getInt("progressBarCheese1") == View.VISIBLE ? View.VISIBLE : View.INVISIBLE); 139 | cheese1.setEnabled(savedInstanceState.getBoolean("cheese1")); 140 | cheese1.setText(savedInstanceState.getString("cheese1_txt")); 141 | getCheese1.setEnabled(savedInstanceState.getBoolean("getCheese1")); 142 | // Cheese2 143 | progressBarCheese2.setVisibility(savedInstanceState.getInt("progressBarCheese2") == View.VISIBLE ? View.VISIBLE : View.INVISIBLE); 144 | cheese2.setEnabled(savedInstanceState.getBoolean("cheese2")); 145 | cheese2.setText(savedInstanceState.getString("cheese2_txt")); 146 | getCheese2.setEnabled(savedInstanceState.getBoolean("getCheese2")); 147 | // Sweet1 148 | progressBarSweet1.setVisibility(savedInstanceState.getInt("progressBarSweet1") == View.VISIBLE ? View.VISIBLE : View.INVISIBLE); 149 | sweet1.setEnabled(savedInstanceState.getBoolean("sweet1")); 150 | sweet1.setText(savedInstanceState.getString("sweet1_txt")); 151 | getSweet1.setEnabled(savedInstanceState.getBoolean("getSweet1")); 152 | // Sweet2 153 | progressBarSweet2.setVisibility(savedInstanceState.getInt("progressBarSweet2") == View.VISIBLE ? View.VISIBLE : View.INVISIBLE); 154 | sweet2.setEnabled(savedInstanceState.getBoolean("sweet2")); 155 | sweet2.setText(savedInstanceState.getString("sweet2_txt")); 156 | getSweet2.setEnabled(savedInstanceState.getBoolean("getSweet2")); 157 | super.onRestoreInstanceState(savedInstanceState); 158 | } 159 | 160 | @OnClick(R.id.get_fruit1) 161 | @Override 162 | public void requestFruit1() { 163 | enableUiFruit1(false); 164 | presenter.requestFruit1(); 165 | } 166 | 167 | @Override 168 | public void postFruit1(String fruit) { 169 | enableUiFruit1(true); 170 | fruit1.setText(fruit); 171 | } 172 | 173 | @Override 174 | public void enableUiFruit1(boolean enable) { 175 | if (enable) { 176 | progressBarFruit1.setVisibility(View.INVISIBLE); 177 | } else { 178 | progressBarFruit1.setVisibility(View.VISIBLE); 179 | } 180 | fruit1.setEnabled(enable); 181 | getFruit1.setEnabled(enable); 182 | } 183 | 184 | @OnClick(R.id.get_fruit2) 185 | @Override 186 | public void requestFruit2() { 187 | enableUiFruit2(false); 188 | presenter.requestFruit2(); 189 | } 190 | 191 | @Override 192 | public void postFruit2(String fruit) { 193 | enableUiFruit2(true); 194 | fruit2.setText(fruit); 195 | } 196 | 197 | @Override 198 | public void enableUiFruit2(boolean enable) { 199 | if (enable) { 200 | progressBarFruit2.setVisibility(View.INVISIBLE); 201 | } else { 202 | progressBarFruit2.setVisibility(View.VISIBLE); 203 | } 204 | fruit2.setEnabled(enable); 205 | getFruit2.setEnabled(enable); 206 | } 207 | 208 | @OnClick(R.id.get_cheese1) 209 | @Override 210 | public void requestCheese1() { 211 | enableUiCheese1(false); 212 | presenter.requestCheese1(); 213 | } 214 | 215 | @Override 216 | public void postCheese1(String cheese) { 217 | enableUiCheese1(true); 218 | cheese1.setText(cheese); 219 | } 220 | 221 | @Override 222 | public void enableUiCheese1(boolean enable) { 223 | if (enable) { 224 | progressBarCheese1.setVisibility(View.INVISIBLE); 225 | } else { 226 | progressBarCheese1.setVisibility(View.VISIBLE); 227 | } 228 | cheese1.setEnabled(enable); 229 | getCheese1.setEnabled(enable); 230 | } 231 | 232 | @OnClick(R.id.get_cheese2) 233 | @Override 234 | public void requestCheese2() { 235 | enableUiCheese2(false); 236 | presenter.requestCheese2(); 237 | } 238 | 239 | @Override 240 | public void postCheese2(String cheese) { 241 | enableUiCheese2(true); 242 | cheese2.setText(cheese); 243 | } 244 | 245 | @Override 246 | public void enableUiCheese2(boolean enable) { 247 | if (enable) { 248 | progressBarCheese2.setVisibility(View.INVISIBLE); 249 | } else { 250 | progressBarCheese2.setVisibility(View.VISIBLE); 251 | } 252 | cheese2.setEnabled(enable); 253 | getCheese2.setEnabled(enable); 254 | } 255 | 256 | @OnClick(R.id.get_sweet1) 257 | @Override 258 | public void requestSweet1() { 259 | enableUiSweet1(false); 260 | presenter.requestSweet1(); 261 | } 262 | 263 | @Override 264 | public void postSweet1(String sweet) { 265 | enableUiSweet1(true); 266 | sweet1.setText(sweet); 267 | } 268 | 269 | @Override 270 | public void enableUiSweet1(boolean enable) { 271 | if (enable) { 272 | progressBarSweet1.setVisibility(View.INVISIBLE); 273 | } else { 274 | progressBarSweet1.setVisibility(View.VISIBLE); 275 | } 276 | sweet1.setEnabled(enable); 277 | getSweet1.setEnabled(enable); 278 | } 279 | 280 | @OnClick(R.id.get_sweet2) 281 | @Override 282 | public void requestSweet2() { 283 | enableUiSweet2(false); 284 | presenter.requestSweet2(); 285 | } 286 | 287 | @Override 288 | public void postSweet2(String sweet) { 289 | enableUiSweet2(true); 290 | sweet2.setText(sweet); 291 | } 292 | 293 | @Override 294 | public void enableUiSweet2(boolean enable) { 295 | if (enable) { 296 | progressBarSweet2.setVisibility(View.INVISIBLE); 297 | } else { 298 | progressBarSweet2.setVisibility(View.VISIBLE); 299 | } 300 | sweet2.setEnabled(enable); 301 | getSweet2.setEnabled(enable); 302 | } 303 | 304 | @Override 305 | public void cachePresenter(Presenter presenter) { 306 | Cache.getInstance().cachePresenterFor(presenter.getUuid(), presenter); 307 | } 308 | 309 | @Override 310 | public void restorePresenter(UUID uuid) { 311 | presenter = (MainPresenter) Cache.getInstance().restorePresenterFor(uuid); 312 | presenter.setScreen(this); 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /presentation-layer/src/main/java/ro/ovidiuconeac/presentationlayer/features/featurex/view/MainView.java: -------------------------------------------------------------------------------- 1 | package ro.ovidiuconeac.presentationlayer.features.featurex.view; 2 | 3 | import ro.ovidiuconeac.presentationlayer.features.Screen; 4 | 5 | /** 6 | * Created by ovidiu on 2/6/17. 7 | */ 8 | 9 | public interface MainView extends Screen { 10 | void requestFruit1(); 11 | void postFruit1(String fruit); 12 | void enableUiFruit1(boolean enable); 13 | void requestFruit2(); 14 | void postFruit2(String fruit); 15 | void enableUiFruit2(boolean enable); 16 | void requestCheese1(); 17 | void postCheese1(String cheese); 18 | void enableUiCheese1(boolean enable); 19 | void requestCheese2(); 20 | void postCheese2(String cheese); 21 | void enableUiCheese2(boolean enable); 22 | void requestSweet1(); 23 | void postSweet1(String sweet); 24 | void enableUiSweet1(boolean enable); 25 | void requestSweet2(); 26 | void postSweet2(String sweet); 27 | void enableUiSweet2(boolean enable); 28 | } 29 | -------------------------------------------------------------------------------- /presentation-layer/src/main/res/layout-land/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 36 | 37 | 43 | 44 |