Skip to content

Commit

Permalink
Merge pull request #6 from andrzejchm/release/0.1.1
Browse files Browse the repository at this point in the history
Release/0.1.1 -> master
  • Loading branch information
andrzejchm committed Jun 23, 2016
2 parents 7d35b76 + dcee350 commit ba5b3b7
Show file tree
Hide file tree
Showing 34 changed files with 1,056 additions and 501 deletions.
63 changes: 56 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,58 @@
DroidMVP
========
# DroidMVP
[![Release](https://jitpack.io/v/andrzejchm/DroidMVP.svg)](https://jitpack.io/#andrzejchm/DroidMVP)[![CircleCI](https://circleci.com/gh/andrzejchm/DroidMVP/tree/develop.svg?style=svg)](https://circleci.com/gh/andrzejchm/DroidMVP/tree/develop)

TODO:
##About
DroidMVP is a small Android library to help you incorporate the [**MVP pattern**](http:https://antonioleiva.com/mvp-android/) along with [**Passive View**](http:https://martinfowler.com/eaaDev/PassiveScreen.html) and [**Presentation Model**](http:https://martinfowler.com/eaaDev/PresentationModel.html) (yes, those can be combined together :) ) within your Android project.

##Explanation
<p align="center">
<img align="cetnter" src="mvp-diagram.png" alt="mvp diagram" />
</p>

**Pasive View**

> A screen and components with all application specific behavior extracted into a controller so that the widgets have their state controlled entirely by controller. - **Martin Fowler**
--
**Presentation Model**

> Represent the state and behavior of the presentation independently of the GUI controls used in the interface - **Martin Fowler**
--

#####Model and Presenter
In our case a controller will be our presenter, which stores the view state within the Presentation Model. All the state manipulation happens within the Model class itself, but it is the Presenter who initiates those modifications.

#####View
Our passive view is the activity or fragment, which will be treated as a widgets' (like TextView, ImageView etc.) container with the ability to present different states driven by the presenter. All user interaction should be routed to the presenter.

##Setup

Add it in your root `build.gradle` at the end of repositories:
```groovy
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
```


Add the dependency to your app's `build.gradle`
```groovy
dependencies {
compile 'com.github.andrzejchm:DroidMVP:0.1.0'
}
```

##Composition over inheritance
If by any chance you cannot extend from `DroidMVPActivity` or `DroidMVPFragment` you can always use the `DroidMVPViewDelegate`. Just make sure to bind it with your activity's or fragment's lifecycle the same way the `DroidMVPFragment` or `DroidMVPActivity` does it.

##Dependency Injection
This library makes it easy to use it with dependency injection frameworks like [Dagger](http:https://google.github.io/dagger/). To see how it could be done, check out the [**Sample project**](/sample), specifically the `BaseFragment` or `BaseActivity`

##Sample Project
A small android app which uses Dependency Injection along with **DroidMVP** can be found
[**here**](/sample)

* fill up this readme
* make the androidSample implement DroidMVP library
* add sample for MVPDelegate
* add sample for Dagger injection of presenter
13 changes: 6 additions & 7 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ dependencies:
## Customize test commands
test:
override:
- case $CIRCLE_NODE_INDEX in 0) ./gradlew check -PpreDexLibraries=false ;; 1) ./gradlew :androidsample:assembleDebug :androidsample:assembleAndroidTest -PpreDexLibraries=false ;; esac:
parallel: true
- ./gradlew check -PpreDexLibraries=false
post:
- mkdir -p $CIRCLE_ARTIFACTS/androidsample/reports
- mkdir -p $CIRCLE_ARTIFACTS/androidsample/test-results
- cp -r androidsample/build/test-results/ $CIRCLE_ARTIFACTS/androidsample/test-results
- cp -r androidsample/build/reports/ $CIRCLE_ARTIFACTS/androidsample/reports
- cp -r androidsample/build/outputs/ $CIRCLE_ARTIFACTS/androidsample/outputs
- mkdir -p $CIRCLE_ARTIFACTS/sample/reports
- mkdir -p $CIRCLE_ARTIFACTS/sample/test-results
- cp -r sample/build/test-results/ $CIRCLE_ARTIFACTS/sample/test-results
- cp -r sample/build/reports/ $CIRCLE_ARTIFACTS/sample/reports
- cp -r sample/build/outputs/ $CIRCLE_ARTIFACTS/sample/outputs
43 changes: 22 additions & 21 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,32 @@ apply from: '../quality_tools/findbugs.gradle'


android {
compileSdkVersion 24
buildToolsVersion "24"
compileSdkVersion 24
buildToolsVersion "24"

defaultConfig {
minSdkVersion 10
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
defaultConfig {
minSdkVersion 10
targetSdkVersion 24
versionCode 1
versionName "0.1.1"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

lintOptions {
warningsAsErrors true
abortOnError true
disable 'InvalidPackage'
}
lintOptions {
warningsAsErrors true
abortOnError true
disable 'InvalidPackage'
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.0.0'
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.0.0'
compile 'com.google.code.findbugs:annotations:2.0.3'
}
90 changes: 90 additions & 0 deletions library/src/main/java/io/appflate/droidmvp/DroidMVPActivity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (C) 2016 Appflate.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http:https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.appflate.droidmvp;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import java.io.Serializable;

public abstract class DroidMVPActivity<M extends Serializable, V extends DroidMVPView, P extends DroidMVPPresenter<V, M>>
extends AppCompatActivity implements DroidMVPView {

private DroidMVPViewDelegate<M, V, P> mvpDelegate = new DroidMVPViewDelegate<M, V, P>() {
@NonNull @Override protected P createPresenter() {
return DroidMVPActivity.this.createPresenter();
}

@NonNull @Override protected M createPresentationModel() {
return DroidMVPActivity.this.createPresentationModel();
}
};

@Override protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
performFieldInjection();
mvpDelegate.onCreate(this, savedInstanceState);
}

@Override protected void onStop() {
super.onStop();
mvpDelegate.onStop();
}

@Override protected void onDestroy() {
super.onDestroy();
mvpDelegate.onDestroy();
}

@Override protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mvpDelegate.onSaveInstanceState(outState);
}

@Override protected void onStart() {
super.onStart();
mvpDelegate.onStart((V) this);
}

/**
* Used for performing field injection trough various dependency injection frameworks like
* Dagger. The injection is performed just before the #createPresenter() or
* #createPresentationModel() methods are called, so you can
* have your presenter and/or Presentation Model being injected by Dagger.
*/
protected abstract void performFieldInjection();

/**
* Used for creating the presenter instance, called in #onCreate(Bundle) method.
* @return an instance of your Presenter.
*/
@NonNull protected abstract P createPresenter();

/**
* Used to create the Presentation Model that will be attached to your presenter in #onAttach()
* method of your presenter.
*
* NOTE: this will be called only if there is no Presentation Model persisted in your
* savedInstanceState!
*
* You can retrieve the arguments from your Intent's extra and pass it
* to your Presentation's model constructor.
* @return Presentation Model instance used by your Presenter.
*/
@NonNull protected abstract M createPresentationModel();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,30 @@
* limitations under the License.
*/

package io.appflate.droidmvp.base;
package io.appflate.droidmvp;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import java.io.Serializable;

public abstract class DroidMVPFragment<M extends Serializable, V extends DroidMVPView, P extends DroidMVPPresenter<V>>
public abstract class DroidMVPFragment<M extends Serializable, V extends DroidMVPView, P extends DroidMVPPresenter<V, M>>
extends Fragment implements DroidMVPView {

private DroidMVPDelegate<M, V, P> mvpDelegate = new DroidMVPDelegate<M, V, P>() {
@Override protected P createPresenter(M presentationModel) {
return DroidMVPFragment.this.createPresenter(presentationModel);
private DroidMVPViewDelegate<M, V, P> mvpDelegate = new DroidMVPViewDelegate<M, V, P>() {
@NonNull @Override protected P createPresenter() {
return DroidMVPFragment.this.createPresenter();
}

@Override protected M createPresentationModel() {
@NonNull @Override protected M createPresentationModel() {
return DroidMVPFragment.this.createPresentationModel();
}
};

@Override public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
performFieldInection();
mvpDelegate.onCreate(this, savedInstanceState);
}

Expand All @@ -59,7 +61,31 @@ public abstract class DroidMVPFragment<M extends Serializable, V extends DroidMV
mvpDelegate.onDestroy();
}

protected abstract P createPresenter(M presentationModel);
/**
* Used for performing field injection trough various dependency injection frameworks like
* Dagger. The injection is performed just before the #createPresenter() or
* #createPresentationModel() methods are called, so you can
* have your presenter and/or Presentation Model being injected by Dagger.
*/
protected abstract void performFieldInection();

protected abstract M createPresentationModel();
/**
* Used for creating the presenter instance, called in #onCreate(Bundle) method.
*
* @return an instance of your Presenter.
*/
@NonNull protected abstract P createPresenter();

/**
* Used to create the Presentation Model that will be attached to your presenter in #onAttach()
* method of your presenter.
*
* NOTE: this will be called only if there is no Presentation Model persisted in your
* savedInstanceState!
*
* You can retrieve the arguments from #getArguments() method of your
* fragment and pass it to your Presentation's model constructor.
* @return Presentation Model instance used by your Presenter.
*/
@NonNull protected abstract M createPresentationModel();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@
* limitations under the License.
*/

package io.appflate.droidmvp.base;
package io.appflate.droidmvp;

import java.io.Serializable;

/**
* Every presenter in the app must either implement this interface or extend SimpleDroidMVPPresenter
* indicating the DroidMVPView type that wants to be attached with.
*/
public interface DroidMVPPresenter<V extends DroidMVPView> {
public interface DroidMVPPresenter<V extends DroidMVPView, M extends Serializable> {

void attachView(V mvpView, M presentationModel);

void attachView(V mvpView);

void detachView();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
* limitations under the License.
*/

package io.appflate.droidmvp.base;
package io.appflate.droidmvp;

/**
* Base interface that any class that wants to act as a View in the MVP (Model View DroidMVPPresenter)
* Base interface that any class that wants to act as a View in the MVP (Model-View-Presenter)
* pattern must implement. Generally this interface will be extended by a more specific interface
* that then usually will be implemented by an Activity or Fragment.
*/
Expand Down
Loading

0 comments on commit ba5b3b7

Please sign in to comment.