├── .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 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
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 | 
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 | 
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 | 
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 | 
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 | 
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 |
50 |
51 |
52 |
53 |
57 |
58 |
64 |
65 |
71 |
72 |
78 |
79 |
80 |
81 |
85 |
86 |
90 |
91 |
97 |
98 |
104 |
105 |
111 |
112 |
113 |
114 |
118 |
119 |
125 |
126 |
132 |
133 |
139 |
140 |
141 |
142 |
146 |
147 |
151 |
152 |
158 |
159 |
165 |
166 |
172 |
173 |
174 |
175 |
179 |
180 |
186 |
187 |
193 |
194 |
200 |
201 |
202 |
203 |
204 |
--------------------------------------------------------------------------------
/presentation-layer/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
19 |
20 |
24 |
25 |
29 |
30 |
36 |
37 |
43 |
44 |
50 |
51 |
52 |
53 |
57 |
58 |
64 |
65 |
71 |
72 |
78 |
79 |
80 |
81 |
85 |
86 |
90 |
91 |
97 |
98 |
104 |
105 |
111 |
112 |
113 |
114 |
118 |
119 |
125 |
126 |
132 |
133 |
139 |
140 |
141 |
142 |
146 |
147 |
151 |
152 |
158 |
159 |
165 |
166 |
172 |
173 |
174 |
175 |
179 |
180 |
186 |
187 |
193 |
194 |
200 |
201 |
202 |
203 |
204 |
--------------------------------------------------------------------------------
/presentation-layer/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/presentation-layer/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/presentation-layer/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/presentation-layer/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/presentation-layer/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/presentation-layer/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/presentation-layer/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/presentation-layer/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/presentation-layer/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovicon/AndroidCleanArchitectureWithMVP/b6202c23d57625934ef04258334803d734cf653c/presentation-layer/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/presentation-layer/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/presentation-layer/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/presentation-layer/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/presentation-layer/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidCleanArchitectureWithMVP
3 | Fruits
4 | Cheeses
5 | Sweets
6 | Get
7 |
8 |
--------------------------------------------------------------------------------
/presentation-layer/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/presentation-layer/src/test/java/ro/ovidiuconeac/presentationlayer/features/featurex/model/MainUseCasesTest.java:
--------------------------------------------------------------------------------
1 | package ro.ovidiuconeac.presentationlayer.features.featurex.model;
2 |
3 | import org.junit.After;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | import ro.ovidiuconeaac.businesslayer.beans.CheeseBeanImpl;
8 | import ro.ovidiuconeaac.businesslayer.beans.FruitsBeanImpl;
9 | import ro.ovidiuconeaac.businesslayer.beans.SweetsBeanImpl;
10 |
11 | import static org.junit.Assert.assertNotNull;
12 |
13 | /**
14 | * Created by ovidiu on 2/7/17.
15 | */
16 |
17 | public class MainUseCasesTest {
18 |
19 | private FruitsBeanImpl fruitsBean;
20 | private CheeseBeanImpl cheeseBean;
21 | private SweetsBeanImpl sweetsBean;
22 |
23 | @Before
24 | public void setUp() {
25 | fruitsBean = new FruitsBeanImpl();
26 | cheeseBean = new CheeseBeanImpl();
27 | sweetsBean = new SweetsBeanImpl();
28 | }
29 |
30 | @After
31 | public void tearDown() {
32 | fruitsBean = null;
33 | cheeseBean = null;
34 | sweetsBean = null;
35 | }
36 |
37 | @Test
38 | public void getFruit() {
39 | assertNotNull(fruitsBean.getRandomFruit());
40 | }
41 |
42 | @Test
43 | public void getCheese() {
44 | assertNotNull(cheeseBean.getRandomCheese());
45 | }
46 |
47 | @Test
48 | public void getSweets() {
49 | assertNotNull(sweetsBean.getRandomSweet());
50 | }
51 | }
--------------------------------------------------------------------------------
/presentation-layer/src/test/java/ro/ovidiuconeac/presentationlayer/features/featurex/presenter/MainPresenterImplTest.java:
--------------------------------------------------------------------------------
1 | package ro.ovidiuconeac.presentationlayer.features.featurex.presenter;
2 |
3 | import org.junit.After;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | import java.util.UUID;
8 |
9 | import ro.ovidiuconeac.presentationlayer.features.Screen;
10 |
11 | import static org.junit.Assert.assertEquals;
12 | import static org.junit.Assert.assertNotNull;
13 | import static org.mockito.Matchers.any;
14 | import static org.mockito.Mockito.doNothing;
15 | import static org.mockito.Mockito.mock;
16 | import static org.mockito.Mockito.when;
17 |
18 | /**
19 | * Created by www.ovidiuconeac.ro on 3/9/17.
20 | */
21 |
22 | public class MainPresenterImplTest {
23 |
24 | private MainPresenterImpl presenter;
25 | private Screen screen;
26 |
27 | @Before
28 | public void setUp() {
29 | presenter = mock(MainPresenterImpl.class);
30 | screen = mock(Screen.class);
31 | }
32 |
33 | @After
34 | public void tearDown() {
35 | presenter = null;
36 | }
37 |
38 | @Test
39 | public void requestFruit1() {
40 | doNothing().when(presenter).requestFruit1();
41 | presenter.requestFruit1();
42 | }
43 |
44 | @Test
45 | public void requestFruit2() {
46 | doNothing().when(presenter).requestFruit2();
47 | presenter.requestFruit2();
48 | }
49 |
50 | @Test
51 | public void requestCheese1() {
52 | doNothing().when(presenter).requestCheese1();
53 | presenter.requestCheese1();
54 | }
55 |
56 | @Test
57 | public void requestCheese2() {
58 | doNothing().when(presenter).requestCheese2();
59 | presenter.requestCheese2();
60 | }
61 |
62 | @Test
63 | public void requestSweet1() {
64 | doNothing().when(presenter).requestSweet1();
65 | presenter.requestSweet1();
66 | }
67 |
68 | @Test
69 | public void requestSweet2() {
70 | doNothing().when(presenter).requestSweet2();
71 | presenter.requestSweet2();
72 | }
73 |
74 | @Test
75 | public void testSetScreen() {
76 | doNothing().when(presenter).setScreen(any(Screen.class));
77 | presenter.setScreen(screen);
78 | }
79 |
80 | @Test
81 | public void testGetUuid() {
82 | UUID uuid = UUID.randomUUID();
83 | when(presenter.getUuid()).thenReturn(uuid);
84 | UUID result = presenter.getUuid();
85 | assertNotNull(result);
86 | assertEquals(uuid, result);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':presentation-layer', ':domain-layer', ':data-layer', ':models'
2 |
--------------------------------------------------------------------------------