Skip to content

Commit

Permalink
Merge pull request #2 from andrzejchm/feature/circleci-setup
Browse files Browse the repository at this point in the history
general refactoring + adding sample with dagger
  • Loading branch information
andrzejchm committed Jun 22, 2016
2 parents ce4d1b9 + d68eac8 commit a1473f7
Show file tree
Hide file tree
Showing 29 changed files with 760 additions and 325 deletions.
48 changes: 41 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,43 @@
DroidMVP
========
# 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.

* fill up this readme
* make the androidSample implement DroidMVP library
* add sample for MVPDelegate
* add sample for Dagger injection of presenter
##Explanation

**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 widget container with the ability to present different states driven by the presenter. All user interaction should be routed to the presenter.

##Setup

setup goes here

##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**]()

##TODO:

* add to jitpack
* add to android arsenal
44 changes: 22 additions & 22 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +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 "1.0"
}
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 'com.google.code.findbugs:annotations:2.0.3'
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'
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,27 @@
package io.appflate.droidmvp.base;

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>>
public abstract class DroidMVPActivity<M extends Serializable, V extends DroidMVPView, P extends DroidMVPPresenter<V, M>>
extends AppCompatActivity implements DroidMVPView {

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

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

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

Expand All @@ -56,10 +58,12 @@ public abstract class DroidMVPActivity< M extends Serializable, V extends DroidM

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

protected abstract P createPresenter(M presentationModel);
protected abstract void performFieldInjection();

protected abstract M createPresentationModel();
@NonNull protected abstract P createPresenter(M presentationModel);

@NonNull protected abstract M createPresentationModel();
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,27 @@
package io.appflate.droidmvp.base;

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) {
private DroidMVPViewDelegate<M, V, P> mvpDelegate = new DroidMVPViewDelegate<M, V, P>() {
@NonNull @Override protected P createPresenter(M presentationModel) {
return DroidMVPFragment.this.createPresenter(presentationModel);
}

@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,9 @@ public abstract class DroidMVPFragment<M extends Serializable, V extends DroidMV
mvpDelegate.onDestroy();
}

protected abstract P createPresenter(M presentationModel);
protected abstract void performFieldInection();

protected abstract M createPresentationModel();
@NonNull protected abstract P createPresenter(M presentationModel);

@NonNull protected abstract M createPresentationModel();
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@

package io.appflate.droidmvp.base;

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 @@ -17,7 +17,7 @@
package io.appflate.droidmvp.base;

/**
* 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
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,28 @@
/**
* Created by andrzejchm on 16/05/16.
*/
public abstract class DroidMVPDelegate<M extends Serializable, V extends DroidMVPView, P extends DroidMVPPresenter<V>> {
public abstract class DroidMVPViewDelegate<M extends Serializable, V extends DroidMVPView, P extends DroidMVPPresenter<V, M>> {

private P presenter;
private M presentationModel;

private String presentationModelKey;

public void onCreate(DroidMVPView mvpView, @Nullable Bundle savedInstanceState) {
presentationModel = restorePresentationModel(getClass(), savedInstanceState);
presentationModelKey = mvpView.getClass().getCanonicalName() + "$PresentationModel";
presentationModel = restorePresentationModel(getClass(), savedInstanceState);
if (presentationModel == null) {
presentationModel = createPresentationModel();
}
this.presenter = createPresenter(presentationModel);
}

@SuppressFBWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR")
protected void onStop() {
@SuppressFBWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR") protected void onStop() {
checkPresenter();
presenter.detachView();
}

@SuppressFBWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR")
protected void onDestroy() {
@SuppressFBWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR") protected void onDestroy() {
checkPresenter();
presenter.onDestroy();
}
Expand All @@ -60,7 +58,8 @@ protected void onSaveInstanceState(Bundle outState) {
@SuppressFBWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR")
protected void onStart(V mvpView) {
checkPresenter();
presenter.attachView(mvpView);
checkPresentationModel();
presenter.attachView(mvpView, presentationModel);
}

public P getPresenter() {
Expand All @@ -74,7 +73,14 @@ public P getPresenter() {
private void checkPresenter() {
if (presenter == null) {
throw new IllegalStateException(
"call onCreate in DroidMVPDelegate, because presenter is missing");
"call onCreate in DroidMVPViewDelegate, because presenter is missing");
}
}

private void checkPresentationModel() {
if (presentationModel == null) {
throw new IllegalStateException(
"seems like you forgot to create presentationModel in #createPresentationModel() method, or call the #onCreate() of this delegate");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@
* can be accessed from the child classes by calling getMvpView().
*/
public abstract class SimpleDroidMVPPresenter<V extends DroidMVPView, M extends Serializable>
implements DroidMVPPresenter<V> {
implements DroidMVPPresenter<V,M> {

private M presentationModel;
private V mvpView;

public SimpleDroidMVPPresenter(M presentationModel) {
this.presentationModel = presentationModel;
}

@Override public void attachView(V mvpView) {
@Override public void attachView(V mvpView, M presentationModel) {
this.mvpView = mvpView;
this.presentationModel = presentationModel;
}

public M getPresentationModel() {
Expand Down
2 changes: 1 addition & 1 deletion sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity android:name="io.appflate.droidvmp.androidsample.ui.activities.ReposActivity">
<activity android:name="io.appflate.droidvmp.androidsample.ui.activities.RepositoriesActivity">
</activity>
</application>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@

package io.appflate.droidvmp.androidsample.di;

import io.appflate.droidvmp.androidsample.ui.fragments.RepositoriesFragment;
import javax.inject.Singleton;

import dagger.Component;
import io.appflate.droidvmp.androidsample.ui.activities.MainActivity;
import io.appflate.droidvmp.androidsample.ui.activities.ReposActivity;
import io.appflate.droidvmp.androidsample.ui.activities.RepositoriesActivity;
import io.appflate.droidvmp.androidsample.domain.GithubApi;

/**
Expand All @@ -32,5 +33,6 @@ public interface AppComponent {
GithubApi getRestService();

void inject(MainActivity mainActivity);
void inject(ReposActivity reposActivity);
void inject(RepositoriesActivity repositoriesActivity);
void inject(RepositoriesFragment repositoriesFragment);
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ public interface GithubApi {
Call<User> getUserProfile(@Path("username") String username);

@GET("users/{username}/repos")
Call<List<Repository>> getUserRepos(@Path("username") String username);
Call<List<Repository>> getUserRepositories(@Path("username") String username);

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,27 @@
package io.appflate.droidvmp.androidsample.model;

import com.google.gson.annotations.SerializedName;
import java.io.Serializable;

/**
* Created by andrzejchm on 23/04/16.
*/
public class Owner {
@SerializedName("login") public String login;
@SerializedName("id") public int id;
@SerializedName("avatar_url") public String avatarUrl;
@SerializedName("gravatar_id") public String gravatarId;
@SerializedName("url") public String url;
@SerializedName("html_url") public String htmlUrl;
@SerializedName("followers_url") public String followersUrl;
@SerializedName("following_url") public String followingUrl;
@SerializedName("gists_url") public String gistsUrl;
@SerializedName("starred_url") public String starredUrl;
@SerializedName("subscriptions_url") public String subscriptionsUrl;
@SerializedName("organizations_url") public String organizationsUrl;
@SerializedName("repos_url") public String reposUrl;
@SerializedName("events_url") public String eventsUrl;
@SerializedName("received_events_url") public String receivedEventsUrl;
@SerializedName("type") public String type;
@SerializedName("site_admin") public boolean siteAdmin;
public class Owner implements Serializable {
@SerializedName("login") public String login;
@SerializedName("id") public int id;
@SerializedName("avatar_url") public String avatarUrl;
@SerializedName("gravatar_id") public String gravatarId;
@SerializedName("url") public String url;
@SerializedName("html_url") public String htmlUrl;
@SerializedName("followers_url") public String followersUrl;
@SerializedName("following_url") public String followingUrl;
@SerializedName("gists_url") public String gistsUrl;
@SerializedName("starred_url") public String starredUrl;
@SerializedName("subscriptions_url") public String subscriptionsUrl;
@SerializedName("organizations_url") public String organizationsUrl;
@SerializedName("repos_url") public String reposUrl;
@SerializedName("events_url") public String eventsUrl;
@SerializedName("received_events_url") public String receivedEventsUrl;
@SerializedName("type") public String type;
@SerializedName("site_admin") public boolean siteAdmin;
}
Loading

0 comments on commit a1473f7

Please sign in to comment.