Repository: tinmegali/simple-mvp
Branch: master
Commit: df726db3a14d
Files: 42
Total size: 78.3 KB
Directory structure:
gitextract__r8j11ak/
├── AndroidMVP/
│ ├── .idea/
│ │ └── runConfigurations.xml
│ ├── app/
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src/
│ │ ├── androidTest/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── tinmegali/
│ │ │ └── androidmvp/
│ │ │ └── ApplicationTest.java
│ │ ├── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── tinmegali/
│ │ │ │ └── androidmvp/
│ │ │ │ └── main/
│ │ │ │ ├── MVP_MainActivity.java
│ │ │ │ ├── model/
│ │ │ │ │ └── MainModel.java
│ │ │ │ ├── presenter/
│ │ │ │ │ └── MainPresenter.java
│ │ │ │ └── view/
│ │ │ │ └── MainActivity.java
│ │ │ └── res/
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml
│ │ │ │ └── content_main.xml
│ │ │ ├── menu/
│ │ │ │ └── menu_main.xml
│ │ │ ├── values/
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ ├── values-v21/
│ │ │ │ └── styles.xml
│ │ │ └── values-w820dp/
│ │ │ └── dimens.xml
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── tinmegali/
│ │ └── androidmvp/
│ │ ├── TestModel.java
│ │ └── TestPresenter.java
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ ├── mvp/
│ │ ├── .gitignore
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src/
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── tinmegali/
│ │ │ └── mvp/
│ │ │ ├── mvp/
│ │ │ │ ├── ActivityView.java
│ │ │ │ ├── ContextView.java
│ │ │ │ ├── GenericMVPActivity.java
│ │ │ │ ├── GenericMVPFragment.java
│ │ │ │ ├── GenericModel.java
│ │ │ │ ├── GenericPresenter.java
│ │ │ │ ├── ModelOps.java
│ │ │ │ ├── PresenterOps.java
│ │ │ │ ├── StateMaintainer.java
│ │ │ │ └── package-info.java
│ │ │ └── util/
│ │ │ └── ActivityKeyBoardDetector.java
│ │ └── res/
│ │ └── values/
│ │ ├── AndroidManifest.xml
│ │ └── strings.xml
│ └── settings.gradle
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: AndroidMVP/.idea/runConfigurations.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>
================================================
FILE: AndroidMVP/app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.tinmegali.androidmvp"
minSdkVersion 11
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.2.0'
compile 'com.android.support:design:23.2.0'
compile project(":mvp")
// compile 'com.tinmegali.mvp:mvp:0.0.5'
// Unit testing dependencies
testCompile 'junit:junit:4.12'
// Set this dependency if you want to use Mockito
testCompile 'org.mockito:mockito-core:1.10.19'
// Set this dependency if you want to use Hamcrest matching
testCompile 'org.hamcrest:hamcrest-library:1.1'
testCompile "org.robolectric:robolectric:3.0"
}
================================================
FILE: AndroidMVP/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/tinmegali/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: AndroidMVP/app/src/androidTest/java/com/tinmegali/androidmvp/ApplicationTest.java
================================================
package com.tinmegali.androidmvp;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: AndroidMVP/app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tinmegali.androidmvp">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".main.view.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
================================================
FILE: AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java
================================================
package com.tinmegali.androidmvp.main;
import com.tinmegali.mvp.mvp.ActivityView;
import com.tinmegali.mvp.mvp.ModelOps;
import com.tinmegali.mvp.mvp.PresenterOps;
/**
* <p>
* Interface that holds all operations available to MVP layers.
* Controls the communication process between each layer
* </p>
*
* <strong>Using</strong>
* <code>interface RequiredViewOps extends ActivityView</code>
* with VIEW methods to be accessed by PRESENTER
*
* <code>interface ProvidedPresenterOps extends PresenterOps<RequiredViewOps></code>
* Operations offered to VIEW to communicate with PRESENTER
*
* <code>interface RequiredPresenterOps</code>
* with Required PRESENTER methods available to MODEL
*
* <code>interface ProvidedModelOps extends ModelOps<RequiredPresenterOps></code>
* Operations offered to MODEL to communicate with PRESENTER
*
* Created by: Tin Megali on 25/02/16. <br/>
* Project: AndroidMVP </br>
* --------------------------------------------------- <br />
* <a href="http://www.tinmegali.com">tinmegali.com</a> <br/>
* <a href="http://www.github.com/tinmegali>github</a> <br />
* ---------------------------------------------------
* <p>
* Based on
* <a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
* framework MVP</a> developed by
* <a href="https://github.com/douglascraigschmidt">
* Dr. Douglas Schmidth</a>
* </p>
*
*/
public interface MVP_MainActivity {
/**
* Required VIEW methods available to PRESENTER
* PRESENTER to VIEW
*/
interface RequiredViewOps extends ActivityView {
}
/**
* Operations offered to VIEW to communicate with PRESENTER
* VIEW to PRESENTER
*/
interface ProvidedPresenterOps extends PresenterOps<RequiredViewOps> {
boolean clickSaveName(String nameTxt);
boolean clickClearName();
}
/**
* Required PRESENTER methods available to MODEL
* MODEL to PRESENTER
*/
interface RequiredPresenterOps {
boolean onNameSaved(String nameTxt);
boolean onNameCleared();
}
/**
* Operations offered to MODEL to communicate with PRESENTER
* PRESENTER to MODEL
*/
interface ProvidedModelOps extends ModelOps<RequiredPresenterOps> {
boolean saveName(String nameTxt);
boolean clearName();
}
}
================================================
FILE: AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/model/MainModel.java
================================================
package com.tinmegali.androidmvp.main.model;
import android.os.AsyncTask;
import com.tinmegali.androidmvp.main.MVP_MainActivity;
import com.tinmegali.mvp.mvp.GenericModel;
/**
* <p>
* Layer MODEL from Model View Presenter (MVP) pattern. <br/>
* Responsible to deal with all business logic and data in general. <br/>
* </p>
*
* Created by Tin Megali on 25/02/16. <br/>
* Project: AndroidMVP </br>
* --------------------------------------------------- <br />
* <a href="http://www.tinmegali.com">tinmegali.com</a> <br/>
* <a href="http://www.github.com/tinmegali>github</a> <br />
* ---------------------------------------------------
* <p>
* Based on
* <a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
* framework MVP</a> developed by
* <a href="https://github.com/douglascraigschmidt">
* Dr. Douglas Schmidth</a>
* </p>
*/
public class MainModel extends GenericModel<MVP_MainActivity.RequiredPresenterOps>
implements MVP_MainActivity.ProvidedModelOps {
/**
* Method that recovers a reference to the PRESENTER
* <ul>
* <li>You must ALWAYS call {@link super#onCreate(Object)} here </li>
* </ul>
* @param presenterOps Presenter interface
*/
@Override
public void onCreate(MVP_MainActivity.RequiredPresenterOps presenterOps) {
super.onCreate(presenterOps);
}
/**
* Called by layer PRESENTER when VIEW pass for a reconstruction/destruction. <br/>
* Usefull for kill/stop activities that could be running on the background
* Threads
* @param isChangingConfiguration Informs that a change is occurring on the configuration
*/
@Override
public void onDestroy(boolean isChangingConfiguration) {
}
String mName;
@Override
public boolean clearName() {
mName = null;
return getPresenter().onNameCleared();
}
@Override
public boolean saveName(final String nameTxt) {
new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
mName = nameTxt;
try {
Thread.sleep(2000);
return mName;
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(String name) {
if ( name != null)
getPresenter().onNameSaved(name);
}
}.execute();
return true;
}
// just for tests
public String getName(){
return mName;
}
}
================================================
FILE: AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/presenter/MainPresenter.java
================================================
package com.tinmegali.androidmvp.main.presenter;
import com.tinmegali.androidmvp.main.MVP_MainActivity;
import com.tinmegali.androidmvp.main.model.MainModel;
import com.tinmegali.mvp.mvp.ContextView;
import com.tinmegali.mvp.mvp.GenericMVPActivity;
import com.tinmegali.mvp.mvp.GenericPresenter;
/**
* <p>
* Layer PRESENTER from Model View Presenter (MVP) Pattern. <br/>
* Mediates the comunication between layers VIEW and MODEL.
* </p>
* <p>
* Layer Presenter no padrão Model View Presenter (MVP). <br/>
*
* Created by: Tin Megali on 25/02/16. <br/>
* Project: AndroidMVP </br>
* --------------------------------------------------- <br />
* <a href="http://www.tinmegali.com">tinmegali.com</a> <br/>
* <a href="http://www.github.com/tinmegali>github</a> <br />
* ---------------------------------------------------
* <p>
* Based on
* <a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
* framework MVP</a> developed by
* <a href="https://github.com/douglascraigschmidt">
* Dr. Douglas Schmidth</a>
* </p>
*/
public class MainPresenter
extends GenericPresenter<MVP_MainActivity.RequiredPresenterOps,
MVP_MainActivity.ProvidedModelOps, MVP_MainActivity.RequiredViewOps,
MainModel>
implements
MVP_MainActivity.RequiredPresenterOps,
MVP_MainActivity.ProvidedPresenterOps
{
/**
* Operation called during VIEW creation in
* {@link com.tinmegali.mvp.mvp.GenericMVPActivity#onCreate(Class, Object)} </br>
* Responsible to initialize MODEL.
* <ul>
* <li>
* Always call {@link GenericPresenter#onCreate(Class, Object)} to
* initialize the object
* </li>
* <li>
* Always call {@link GenericPresenter#setView(ContextView)} to save a
* <code>RequiredViewOps</code> reference
* </li>
* </ul>
* @param view The current VIEW instance
*/
@Override
public void onCreate(MVP_MainActivity.RequiredViewOps view) {
super.onCreate(MainModel.class, this);
// super.onCreate(<Model.class>, <RequiredPresenterOps>);
setView( view );
}
/**
* Operation called by VIEW after its reconstruction.
*<ul>
* <li>
* Always call {@link GenericPresenter#setView(ContextView)} to
* save the new instance of <code>RequiredViewOps</code>
* </li>
*</ul>
* @param view The current VIEW instance
*/
@Override
public void onConfigurationChanged(MVP_MainActivity.RequiredViewOps view) {
setView(view);
}
/**
* Helper method to inform Presenter that a <code>onBackPressed</code> event occurred
* Called by {@link GenericMVPActivity}
*/
@Override
public void onBackPressed() {
}
@Override
public boolean clickClearName() {
return clearName();
}
// For test purposes
public boolean clearName(){
return getModel().clearName();
}
@Override
public boolean clickSaveName(String nameTxt) {
return saveName(nameTxt);
}
// For test purposes
public boolean saveName(String txt){
return getModel().saveName(txt);
}
@Override
public boolean onNameSaved(String nameTxt) {
return nameTxt != null;
}
@Override
public boolean onNameCleared() {
return true;
}
}
================================================
FILE: AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/view/MainActivity.java
================================================
package com.tinmegali.androidmvp.main.view;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.Toolbar;
import android.view.View;
import com.tinmegali.androidmvp.R;
import com.tinmegali.androidmvp.main.MVP_MainActivity;
import com.tinmegali.androidmvp.main.presenter.MainPresenter;
import com.tinmegali.mvp.mvp.GenericMVPActivity;
/**
* <p>
* VIEW layer of MVP pattern.
* </p><p>
* Created by: Tin Megali on 25/02/16. <br/>
* Project: AndroidMVP </br>
* --------------------------------------------------- <br />
* <a href="http://www.tinmegali.com">tinmegali.com</a> <br/>
* <a href="http://www.github.com/tinmegali>github</a> <br />
* ---------------------------------------------------
* <p>
* Based on
* <a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
* framework MVP</a> developed by
* <a href="https://github.com/douglascraigschmidt">
* Dr. Douglas Schmidth</a>
* </p>
*/
public class MainActivity
extends
GenericMVPActivity<MVP_MainActivity.RequiredViewOps,
MVP_MainActivity.ProvidedPresenterOps,
MainPresenter>
implements
MVP_MainActivity.RequiredViewOps{
/**
* Method that initialized MVP objects
* {@link super#onCreate(Class, Object)} should always be called
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.onCreate(MainPresenter.class,this);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
}
================================================
FILE: AndroidMVP/app/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".main.view.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
================================================
FILE: AndroidMVP/app/src/main/res/layout/content_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".main.view.MainActivity"
tools:showIn="@layout/activity_main">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>
================================================
FILE: AndroidMVP/app/src/main/res/menu/menu_main.xml
================================================
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".main.view.MainActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never" />
</menu>
================================================
FILE: AndroidMVP/app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
================================================
FILE: AndroidMVP/app/src/main/res/values/dimens.xml
================================================
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="fab_margin">16dp</dimen>
</resources>
================================================
FILE: AndroidMVP/app/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">AndroidMVP</string>
<string name="action_settings">Settings</string>
</resources>
================================================
FILE: AndroidMVP/app/src/main/res/values/styles.xml
================================================
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>
================================================
FILE: AndroidMVP/app/src/main/res/values-v21/styles.xml
================================================
<resources>>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
================================================
FILE: AndroidMVP/app/src/main/res/values-w820dp/dimens.xml
================================================
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>
================================================
FILE: AndroidMVP/app/src/test/java/com/tinmegali/androidmvp/TestModel.java
================================================
package com.tinmegali.androidmvp;
import com.tinmegali.androidmvp.main.model.MainModel;
import com.tinmegali.androidmvp.main.presenter.MainPresenter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
*
* A simple Test case to illustrate how to Test Model
* ---------------------------------------------------
* Created by Tin Megali on 14/03/16.
* Project: AndroidMVP
* ---------------------------------------------------
* <a href="http://www.tinmegali.com">tinmegali.com</a>
* <a href="http://www.github.com/tinmegali>github</a>
* ---------------------------------------------------
*/
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21, manifest = "/src/main/AndroidManifest.xml")
public class TestModel {
private MainPresenter mPresenter;
private MainModel mModel;
@Before
public void setup(){
mModel = new MainModel();
mPresenter = mock(MainPresenter.class);
mModel.onCreate(mPresenter);
}
@Test
public void testRealModel(){
String txt = "name";
mModel.saveName(txt);
assertEquals(mModel.getName(), txt);
mModel.clearName();
assertEquals(mModel.getName(), null);
}
}
================================================
FILE: AndroidMVP/app/src/test/java/com/tinmegali/androidmvp/TestPresenter.java
================================================
package com.tinmegali.androidmvp;
import com.tinmegali.androidmvp.main.MVP_MainActivity;
import com.tinmegali.androidmvp.main.model.MainModel;
import com.tinmegali.androidmvp.main.presenter.MainPresenter;
import com.tinmegali.androidmvp.main.view.MainActivity;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.InjectMocks;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
/**
* A simple Test case to illustrate how to Test Presenter
*
* ---------------------------------------------------
* Created by Tin Megali on 14/03/16.
* Project: AndroidMVP
* ---------------------------------------------------
* <a href="http://www.tinmegali.com">tinmegali.com</a>
* <a href="http://www.github.com/tinmegali>github</a>
* ---------------------------------------------------
*/
@RunWith(JUnit4.class)
@Config(constants = BuildConfig.class, sdk = 21, manifest = "/src/main/AndroidManifest.xml")
public class TestPresenter {
private MainModel mModel;
private MainPresenter mPresenter, spyPresenter;
private MainActivity mView;
@Before
@SuppressWarnings("unchecked")
public void setup() {
mPresenter = new MainPresenter();
mView = mock(MainActivity.class);
mModel = mock(MainModel.class);
mPresenter.setView(mView);
mPresenter.testWithModel(mModel);
spyPresenter = spy(mPresenter);
doReturn(mModel).when(spyPresenter).getModel();
}
@Test
public void testRealPresenter(){
String txt = "text";
doReturn(true).when(mModel).clearName();
doReturn(true).when(mModel).saveName(anyString());
assertEquals(mPresenter.clickSaveName(txt), true);
assertEquals(mPresenter.clearName(), true);
}
@Test
public void testSpyPresenter() {
String txt = "text";
spyPresenter.clickSaveName(txt);
verify(spyPresenter).saveName(anyString());
verify(mModel).saveName(anyString());
spyPresenter.clickClearName();
verify(spyPresenter).clearName();
}
}
================================================
FILE: AndroidMVP/build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.5.0'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2'
classpath "com.github.dcendents:android-maven-gradle-plugin:1.3"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
================================================
FILE: AndroidMVP/gradle/wrapper/gradle-wrapper.properties
================================================
#Wed Oct 21 11:34:03 PDT 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
================================================
FILE: AndroidMVP/gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: AndroidMVP/mvp/.gitignore
================================================
/build
================================================
FILE: AndroidMVP/mvp/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'com.jfrog.bintray'
apply plugin: 'com.github.dcendents.android-maven'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
minSdkVersion 11
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:23.2.0'
compile 'com.android.support:design:23.2.0'
// Unit testing dependencies
testCompile 'junit:junit:4.12'
// Set this dependency if you want to use Mockito
testCompile 'org.mockito:mockito-core:1.10.19'
// Set this dependency if you want to use Hamcrest matching
testCompile 'org.hamcrest:hamcrest-library:1.1'
testCompile "org.robolectric:robolectric:3.0"
}
group = 'com.tinmegali.mvp' // Change this to match your package name
version = '0.0.7' // Change this to match your version number
task generateSourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier 'sources'
}
task generateJavaDocs(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath()
.join(File.pathSeparator))
failOnError false
}
task generateJavaDocsJar(type: Jar, dependsOn: generateJavaDocs) {
from generateJavaDocs.destinationDir
classifier 'javadoc'
}
bintray {
user = BINRAY_USER
key = BINRAY_KEY
pkg {
repo = 'maven'
name = BINRAY_NAME
version {
name = 'simple-mvp'
desc = 'A simple MVP Framework without any external libraries.'
vcsTag = '0.0.7'
}
licenses = ['Apache-2.0']
vcsUrl = 'https://github.com/tinmegali/simple-mvp'
websiteUrl = 'https://github.com/tinmegali/simple-mvp'
}
configurations = ['archives']
}
artifacts {
archives generateJavaDocsJar
archives generateSourcesJar
}
================================================
FILE: AndroidMVP/mvp/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/tinmegali/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: AndroidMVP/mvp/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tinmegali.mvp">
<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true">
</application>
</manifest>
================================================
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/ActivityView.java
================================================
package com.tinmegali.mvp.mvp;
import android.view.View;
/**
* <p>
* Interface to be implemented by the Activity,
* contains some basic common feedback operations
* </p>
* <p>
* Created by Tin Megali on 24/02/16. <br>
* Project: AndroidMVP <br>
*
* <a href="http://www.tinmegali.com">www.tinmegali.com</a>
*
* @see <a href="https://github.com/tinmegali/simple-mvp">Project's Git</a> <br>
* @see <a href="https://github.com/tinmegali/simple-mvp/tree/master/AndroidMVP/app">Sample Application</a>
* @see <a href="https://github.com/tinmegali/simple-mvp/blob/master/AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java">
* Sample MVP interface
* </a>
*/
public interface ActivityView extends ContextView {
/**
* Shows a toast in the Activity with a short time
* @param msg Message to show
*/
void onShowToast(String msg);
/**
* Shows a {@link android.widget.Toast} in the Activity with custom time
* @param msg Message to show
* @param duration Time Length
* {@link android.widget.Toast#LENGTH_SHORT}
* {@link android.widget.Toast#LENGTH_LONG}
*/
void onShowToast(String msg, int duration);
/**
* Shows a <code>Snackbar</code> in Activity with a short time
* @param msg Snackbar message
* @param parentView Snackbar parent view
*/
void onShowSnackbar(String msg, View parentView);
/**
* Shows a <code>Snackbar</code> in Activity with custom time
* @param msg Snackbar message
* @param parentView Snackbar parent view
* @param duration Time Length
* <code>Snackbar#LENGTH_SHORT</code>
* <code>Snackbar#LENGTH_LONG</code>
* <code>Snackbar#LENGTH_INDEFINITE</code>
*/
void onShowSnackbar(String msg, View parentView, int duration);
/**
* Shows a given <code>Snackbar</code>
* Useful for snacks with custom actions.
* @param snackbar The Snackbar to show
*/
void onShowSnackbar(android.support.design.widget.Snackbar snackbar);
}
================================================
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/ContextView.java
================================================
package com.tinmegali.mvp.mvp;
import android.content.Context;
/**
* <p>Interface that grants access to Contexts</p>
* <p>
* Created by Tin Megali on 24/02/16. <br>
* Project: AndroidMVP <br>
*
* <a href="http://www.tinmegali.com">www.tinmegali.com</a>
* </p>
* --------------------------------------------------- <br>
* <p>
* Based on <a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
* framework MVP</a> developed by
* <a href="https://github.com/douglascraigschmidt">
* Dr. Douglas Schmidth</a>
* </p>
*
* @see <a href="https://github.com/tinmegali/simple-mvp">Project's Git</a> <br>
* @see <a href="https://github.com/tinmegali/simple-mvp/tree/master/AndroidMVP/app">Sample Application</a>
* @see <a href="https://github.com/tinmegali/simple-mvp/blob/master/AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java">
* Sample MVP interface
* </a>
*
*/
public interface ContextView {
/**
* Access to application {@link Context}
* @return application context
*/
Context getApplicationContext();
/**
* Access to current activity {@link Context}
* @return activity context
*/
Context getActivityContext();
}
================================================
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/GenericMVPActivity.java
================================================
package com.tinmegali.mvp.mvp;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.util.Log;
import android.view.View;
import com.tinmegali.mvp.util.ActivityKeyBoardDetector;
/**
* <p>
* Generic Abstract Activity.
* Works as a VIEW layer in the MVP pattern. <br>
* Responsible to initialize the PRESENTER and to maintain
* it synchronized with Activity lifecycle changes. <br>
*
* <strong>IMPORTANT: View Object should implement <code> RequiredViewOps </code></strong>
* </p>
*
* <p>
* Created by Tin Megali on 24/02/16. <br>
* Project: AndroidMVP <br>
*
* <a href="http://www.tinmegali.com">www.tinmegali.com</a>
* </p>
* --------------------------------------------------- <br>
* <p>
* Based on <a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
* framework MVP</a> developed by
* <a href="https://github.com/douglascraigschmidt">
* Dr. Douglas Schmidth</a>
* </p>
*
* @see <a href="https://github.com/tinmegali/simple-mvp">Project's Git</a> <br>
* @see <a href="https://github.com/tinmegali/simple-mvp/tree/master/AndroidMVP/app">Sample Application</a>
* @see <a href="https://github.com/tinmegali/simple-mvp/blob/master/AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java">
* Sample MVP interface
* </a>
*
* @param <RequiredViewOps> Interface with available
* VIEW methods available to PRESENTER
* @param <ProvidedPresenterOps> Interface with available
* PRESENTER methods available to VIEW
* @param <PresenterType> PRESENTER Object to be instantiated by the VIEW
*/
public abstract class GenericMVPActivity
<RequiredViewOps,
ProvidedPresenterOps,
PresenterType extends PresenterOps<RequiredViewOps>>
extends ActivityKeyBoardDetector
implements ActivityView {
protected final String TAG = getClass().getSimpleName();
// Responsible to maintain objects state between config changes
protected final StateMaintainer mStateMaintainer =
new StateMaintainer( this.getFragmentManager(), TAG );
// PRESENTER reference
private PresenterType mPresenterInstance;
/**
* Hook method to initialize PRESENTER. Needs to be called in
* {@link Activity#onCreate(Bundle)}.
*
* @param opsType PRESENTER Object class
* @param view Interface with VIEW reference to PRESENTER
*/
public void onCreate(Class<PresenterType> opsType, RequiredViewOps view ) {
try {
if ( mStateMaintainer.firstTimeIn() ) {
Log.d(TAG, "onCreate() calling for the first time.");
initialize(opsType, view);
} else {
Log.d(TAG, "onCreate() calling more then once.");
reinitialize(opsType, view);
}
} catch ( InstantiationException | IllegalAccessException e ) {
Log.d(TAG, "onCreate() " + e );
throw new RuntimeException( e );
}
}
/**
* Gets PRESENTER layer
* @return PRESENTER instance with operation availabe to VIEW
*/
@SuppressWarnings("unchecked")
public ProvidedPresenterOps getPresenter() { return (ProvidedPresenterOps) mPresenterInstance; }
/**
* @return State Maintainer instance
*/
public StateMaintainer getStateMaintainer() { return mStateMaintainer; }
/**
* @return Activity Context
*/
@Override
public Context getActivityContext() {
return this;
}
/**
* @return Application Context
*/
@Override
public Context getApplicationContext() {
return super.getApplicationContext();
}
/**
* Initialize PRESENTER layer.
* Creates a new instance of the PRESENTER,
* saves in the StateMaintainer and call {@link GenericPresenter#onCreate(Object)}
* hook method.
*
* @param opsType PRESENTER Object Class type
* @param view Current VIEW instance
*/
private void initialize( Class<PresenterType> opsType, RequiredViewOps view )
throws InstantiationException, IllegalAccessException{
mPresenterInstance = opsType.newInstance();
mStateMaintainer.put( opsType.getSimpleName(), mPresenterInstance );
mPresenterInstance.onCreate(view);
}
/**
* Recovers the PRESENTER and informs its instance that a
* configuration change occurred, passing a VIEW reference
* one more time.
* Case the PRESENTER has been lost, another instance is created.
*
* @param opsType PRESENTER Object Class type
* @param view Current VIEW instance
*/
private void reinitialize( Class<PresenterType> opsType, RequiredViewOps view)
throws InstantiationException, IllegalAccessException {
mPresenterInstance = mStateMaintainer.get( opsType.getSimpleName() );
if ( mPresenterInstance == null ) {
Log.w(TAG, "reinitialize: recreating presenter");
initialize(opsType, view );
} else {
mPresenterInstance.onConfigurationChanged(view);
}
}
/**
* Show a Snackbar. Helper method to be called by Presenter
* @param msg Snackbar message
* @param parentView Snackbar parent view
*/
public void onShowSnackbar(String msg, View parentView) {
showSnackbar(msg, parentView);
}
/**
* Show a Toast. Helper method to be called by Presenter
* @param msg Message to show
*/
public void onShowToast(String msg) {
showToast(msg);
}
/**
* Show a Toast. Helper method to be called by Presenter
* @param msg Message to show
* @param duration Time Length
* {@link android.widget.Toast#LENGTH_SHORT}
*/
public void onShowToast(String msg, int duration) {
showToast(msg, duration);
}
/**
* Show a Snackbar. Helper method to be called by Presenter
* @param msg Snackbar message
* @param parentView Snackbar parent view
* @param duration Time Length
* <code>Snackbar#LENGTH_SHORT</code>
* <code>Snackbar#LENGTH_LONG</code>
*/
public void onShowSnackbar(String msg, View parentView, int duration) {
showSnackbar(msg, parentView, duration);
}
/**
* Show a Snackbar. Helper method to be called by Presenter
* @param snackbar The Snackbar to show
*/
public void onShowSnackbar(Snackbar snackbar) {
showSnackbar(snackbar);
}
}
================================================
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/GenericMVPFragment.java
================================================
package com.tinmegali.mvp.mvp;
import android.content.Context;
import android.util.Log;
import java.lang.ref.WeakReference;
/**
* <p>
* Generic Abstract Fragment.
* Works as a VIEW layer in the MVP pattern. <br>
* Responsible to initialize the PRESENTER and to maintain
* it synchronized with Activity lifecycle changes. <br>
* <strong>IMPORTANT: View Object should implement <code>RequiredViewOps</code></strong>
* </p>
* <p>
* Created by Tin Megali on 24/02/16. <br>
* Project: AndroidMVP <br>
*
* <a href="http://www.tinmegali.com">www.tinmegali.com</a>
* </p>
*
* @see <a href="https://github.com/tinmegali/simple-mvp">Project's Git</a> <br>
* @see <a href="https://github.com/tinmegali/simple-mvp/tree/master/AndroidMVP/app">Sample Application</a>
* @see <a href="https://github.com/tinmegali/simple-mvp/blob/master/AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java">
* Sample MVP interface
* </a>
*
* @param <RequiredActivityOps> Interface that define operation to be executed
* on the Activity
* @param <RequiredViewOps> Interface with available
* VIEW methods available to PRESENTER
* @param <ProvidedPresenterOps> Interface with available
* PRESENTER methods available to VIEW
* @param <PresenterType> PRESENTER Object to be instantiated by the VIEW
*/
public abstract class GenericMVPFragment<
RequiredActivityOps,
RequiredViewOps,
ProvidedPresenterOps,
PresenterType extends PresenterOps<RequiredViewOps>>
extends android.support.v4.app.Fragment
implements ContextView {
protected final String TAG = getClass().getSimpleName();
// PRESENTER reference
private PresenterType mPresenterInstance;
// Responsible to maintain objects state between config changes
protected StateMaintainer mStateMaintainer;
// Activity operations available
protected WeakReference<RequiredActivityOps> mActivity;
/**
* Subscribes onAttach to get a reference from current Activity
* and its available operations to Fragment
* @param context Activity context
*/
@SuppressWarnings("unchecked")
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mActivity = new WeakReference<>( (RequiredActivityOps) getActivity() );
} catch (ClassCastException e) {
throw new ClassCastException("Activity must implement <RequiredActivityOps>" );
}
}
/**
* Hook method to initialize PRESENTER. Needs to be called in
* <code>Fragment#onCreate(Bundle)</code>
*
* @param opsType PRESENTER Object class
* @param view Interface with VIEW reference to PRESENTER
*/
public void onCreate(Class<PresenterType> opsType, RequiredViewOps view ) {
if ( mStateMaintainer == null)
mStateMaintainer =
new StateMaintainer(getActivity().getFragmentManager(), TAG + "_retainer" );
try {
if ( mStateMaintainer.firstTimeIn() ) {
Log.d(TAG, "First time calling onCreate()");
initialize(opsType, view);
} else {
Log.d(TAG, "Second (or subsequent) time calling onCreate()");
reinitialize(opsType, view);
}
} catch ( java.lang.InstantiationException | IllegalAccessException e ) {
Log.d(TAG, "onCreate() " + e );
throw new RuntimeException( e );
}
}
/**
* Gets PRESENTER layer
* @return PRESENTER instance with operation availabe to VIEW
*/
@SuppressWarnings("unchecked")
public ProvidedPresenterOps getPresenter() { return (ProvidedPresenterOps) mPresenterInstance; }
/**
* @return State Maintainer instance
*/
public StateMaintainer getStateMaintainer() { return mStateMaintainer; }
/**
* Initialize PRESENTER layer.
* Creates a new instance of the PRESENTER,
* saves in the StateMaintainer and call {@link GenericPresenter#onCreate(Object)}
* hook method.
*
* @param opsType PRESENTER Object Class type
* @param view Current VIEW instance
*/
private void initialize( Class<PresenterType> opsType, RequiredViewOps view )
throws java.lang.InstantiationException, IllegalAccessException{
mPresenterInstance = opsType.newInstance();
mStateMaintainer.put(opsType.getSimpleName(), mPresenterInstance);
mPresenterInstance.onCreate( view );
}
/**
* Recovers the PRESENTER and informs its instance that a
* configuration change occurred, passing a VIEW reference
* one more time.
* Case the PRESENTER has been lost, another instance is created.
*
* @param opsType PRESENTER Object Class type
* @param view Current VIEW instance
*/
private void reinitialize( Class<PresenterType> opsType, RequiredViewOps view)
throws java.lang.InstantiationException, IllegalAccessException {
mPresenterInstance = mStateMaintainer.get( opsType.getSimpleName() );
if ( mPresenterInstance == null ) {
initialize( opsType, view );
} else {
mPresenterInstance.onConfigurationChanged(view);
}
}
@SuppressWarnings("unchecked")
@Override
public void onDestroy() {
super.onDestroy();
((PresenterOps<RequiredViewOps>)getPresenter())
.onDestroy(getActivity().isChangingConfigurations());
}
@SuppressWarnings("unchecked")
@Override
public void onPause() {
super.onPause();
((PresenterOps<RequiredViewOps>)getPresenter())
.onDestroy(getActivity().isChangingConfigurations());
}
/**
* @return Activity Context
*/
@Override
public Context getActivityContext() {
return getActivity();
}
/**
* @return Application Context
*/
@Override
public Context getApplicationContext() {
return getActivity().getApplicationContext();
}
}
================================================
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/GenericModel.java
================================================
package com.tinmegali.mvp.mvp;
import android.util.Log;
/**
* <p>
* Abstract class of MODEL layer on MVP pattern.
* Should be extended by any MODEL object.
* </p>
* <p>
* Created by Tin Megali on 24/02/16. <br>
* Project: AndroidMVP <br>
*
* <a href="http://www.tinmegali.com">www.tinmegali.com</a>
* </p>
* --------------------------------------------------- <br>
* <p>
* Based on <a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
* framework MVP</a> developed by
* <a href="https://github.com/douglascraigschmidt">
* Dr. Douglas Schmidth</a>
* </p>
*
* @see <a href="https://github.com/tinmegali/simple-mvp">Project's Git</a> <br>
* @see <a href="https://github.com/tinmegali/simple-mvp/tree/master/AndroidMVP/app">Sample Application</a>
* @see <a href="https://github.com/tinmegali/simple-mvp/blob/master/AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java">
* Sample MVP interface
* </a>
*
* @param <RequiredPresenterOps> Interface with method given to MODEL
* to access the Presenter. Should act
* as a callback to data operations
*/
public abstract class GenericModel<RequiredPresenterOps> implements ModelOps<RequiredPresenterOps> {
private String TAG = getClass().getSimpleName();
// PRESENTER operations available to MODEL
private RequiredPresenterOps mPresenter;
/**
* Initialize object. Called by {@link GenericPresenter#initialize(Class, Object)}
* Saves the PRESENTER reference.
*
* @param presenterOps reference passed by GenericPresenter
* with available operations in PRESENTER layer
*/
@Override
public void onCreate(RequiredPresenterOps presenterOps) {
// Log.d(TAG, "onCreate() Model" );
mPresenter = presenterOps;
}
/**
* Hook method that receives destruction and change configurations events.
* Shut down methods should be implemented here.
* Called from {@link GenericPresenter#onDestroy(boolean)}
*
* @param isChangingConfiguration Informs if Activity is being destroyed
* or changing its configuration
* true: configuration is changing
* false: a Activity is being destroyed
*/
@Override
public void onDestroy(boolean isChangingConfiguration) {
// Log.d(TAG, "onDestrou(isChangingConfiguration:"+isChangingConfiguration+")");
}
/**
* Recovers the PRESENTER layer reference
* @return Operations available on PRESENTER
*/
public RequiredPresenterOps getPresenter() { return mPresenter; }
}
================================================
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/GenericPresenter.java
================================================
package com.tinmegali.mvp.mvp;
import android.content.Context;
import android.util.Log;
import java.lang.ref.WeakReference;
/**
*
* <p>A Model View Presenter Library using plain and simple interfaces.</p>
*
* <p>
* Abstract class of PRESENTER layer on MVP pattern. <br>
* Should be extended by any PRESENTER object
* <strong>IMPORTANT: PRESENTER pbject should implement
* <code>RequiredPresenterOps</code> and <code>ProvidedModelOps</code></strong>
* <p>
* <strong>IMPORTANT: View Object should implement <code> RequiredViewOps </code></strong>
* </p>
*
* <p>
* Created by Tin Megali on 24/02/16. <br>
* Project: AndroidMVP <br>
*
* <a href="http://www.tinmegali.com">www.tinmegali.com</a>
* </p>
* --------------------------------------------------- <br>
* <p>
* Based on <a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
* framework MVP</a> developed by
* <a href="https://github.com/douglascraigschmidt">
* Dr. Douglas Schmidth</a>
* </p>
*
* @see <a href="https://github.com/tinmegali/simple-mvp">Project's Git</a> <br>
* @see <a href="https://github.com/tinmegali/simple-mvp/tree/master/AndroidMVP/app">Sample Application</a>
* @see <a href="https://github.com/tinmegali/simple-mvp/blob/master/AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java">
* Sample MVP interface
* </a>
*
*
* @param <RequiredPresenterOps>Interface with available PRESENTER
* operation to the MODEL layer
*
* @param <ProvidedModelOps> Interface with available MODEL
* operations available to PRESENTER
*
* @param <RequiredViewOps> Interface with available VIEW
* operations available to PRESENTER
*
* @param <ModelType> MODEL object to be instantiated by
* the PRESENTER
*/
public abstract class GenericPresenter
<RequiredPresenterOps,
ProvidedModelOps, RequiredViewOps extends ContextView,
ModelType extends ModelOps<RequiredPresenterOps> >
implements PresenterOps<RequiredViewOps>
{
protected final String TAG = getClass().getSimpleName();
// Reference to the destruction kind that is occurring in VIEW
private boolean mConfigurationChangeOccurred;
// Informs if VIEW is active
private boolean mIsRunning;
// Reference to VIEW operations
private WeakReference<RequiredViewOps> mView;
// MODEL object to be instantiated
private ModelType mOpsInstance;
/**
* Method that NEEDS TO BE CALLED by the PRESENTER object
* in the {@link PresenterOps#onCreate(Object)} method.
* Initialize MODEL and PRESENTER.
*
* @param opsType Class of the MODEL object
* @param presenter Interface with provided PRESENTER operations
* available to MODEL
*/
public void onCreate( Class<ModelType> opsType, RequiredPresenterOps presenter ) {
mIsRunning = true;
mConfigurationChangeOccurred = false;
try {
// initialized MODEL
initialize( opsType, presenter );
} catch ( InstantiationException | IllegalAccessException e ) {
Log.d(TAG, "handleConfiguration " + e);
throw new RuntimeException( e );
}
}
/**
* Define a VIEW reference available to PRESENTER.
* Should be called i {@link PresenterOps#onCreate(Object)}
*
* @param view VIEW operations available to PRESENTER
*/
public void setView(RequiredViewOps view) {
mView = new WeakReference<>(view);
}
/**
* Recovers the VIEW reference, returning NULL if layers isn't available.
* @return VIEW reference or NULL
*/
public RequiredViewOps getView() {
if ( mIsRunning && !mConfigurationChangeOccurred && mView != null) {
return mView.get();
} else {
Log.w(TAG, "View unavailable.");
return null;
}
}
/**
* Recover the application Context
* @return Application Context
*/
public Context getApplicationContext() {
return mView.get().getApplicationContext();
}
/**
* Recover the Activity Context
* @return Activity Context
*/
public Context getActivityContext() {
return mView.get().getActivityContext();
}
/**
* Hook method informing of destruction of the VIEW.
* Should be subscribed by the PRESENTER object and called by itself
* as a super method.
*
* @param isChangingConfiguration true: VIEW changing configuration
* false: VIEW being destroyed
*/
public void onDestroy(boolean isChangingConfiguration) {
mIsRunning = isChangingConfiguration;
mConfigurationChangeOccurred = isChangingConfiguration;
mOpsInstance.onDestroy(isChangingConfiguration);
}
/**
* Informs actual VIEW state
* @return true: VIEW is active
* false: VIEW is destroyed
*/
public boolean isViewRunning() { return mIsRunning; }
/**
* Informs if occurred a configuration change
* @return true: a configuration change occured
*/
public boolean configurationsOccurred() { return mConfigurationChangeOccurred; }
/**
* Initialized the MODEL object, creating a new MODEL instance
*
* @param opsType MODEL object Class
* @param presenter Interface given to MODEL with available
* PRESENTER operations
* MODEL -> PRESENTER
* @throws InstantiationException
* @throws IllegalAccessException
*/
private void initialize( Class<ModelType> opsType, RequiredPresenterOps presenter )
throws InstantiationException, IllegalAccessException {
mOpsInstance = opsType.newInstance();
mOpsInstance.onCreate( presenter );
}
/**
* Returns available MODEL methods
* @return a MODEL object instance
*/
@SuppressWarnings("unchecked")
public ProvidedModelOps getModel() { return (ProvidedModelOps) mOpsInstance; }
/**
* Added for test purposes. Sets the given Model
* without the need to cal {@link #onCreate(Class, Object)}.
* IMPORTANT: Use ONLY FOR TESTS.
* @param model Model instance
*/
public void testWithModel(ModelType model) {
mOpsInstance = model;
}
}
================================================
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/ModelOps.java
================================================
package com.tinmegali.mvp.mvp;
/**
* <p>
* Interface implemented by {@link GenericModel}. <br>
* Contains
* initializing and destruction methods called by VIEW layer in
* all MVP objects. Garantes the correct synchronization of MVP
* with Activity lifecycle.
* <p>
* <strong>IMPORTANT: View Object should implement <code> RequiredViewOps </code></strong>
* </p>
*
* <p>
* Created by Tin Megali on 24/02/16. <br>
* Project: AndroidMVP <br>
*
* <a href="http://www.tinmegali.com">www.tinmegali.com</a>
* </p>
* --------------------------------------------------- <br>
* <p>
* Based on <a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
* framework MVP</a> developed by
* <a href="https://github.com/douglascraigschmidt">
* Dr. Douglas Schmidth</a>
* </p>
*
* @see <a href="https://github.com/tinmegali/simple-mvp">Project's Git</a> <br>
* @see <a href="https://github.com/tinmegali/simple-mvp/tree/master/AndroidMVP/app">Sample Application</a>
* @see <a href="https://github.com/tinmegali/simple-mvp/blob/master/AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java">
* Sample MVP interface
* </a>
*
*
* @param <RequiredPresenterOps> PRESENTER operations available to MODEL
* Acts as a callback to data operations.
*/
public interface ModelOps<RequiredPresenterOps> {
/**
* Initialization method. Receives the PRESENTER reference.
* Called by {@link GenericPresenter#initialize(Class, Object)}
*
* @param presenterOps PRESENTER reference, containing
* operations available to MODEL
*/
void onCreate(RequiredPresenterOps presenterOps);
/**
* Hook method called when the VIEW is being destroyed or
* having its configuration changed.
* Responsible to maintain MVP synchronized with Activity lifecycle.
* Called by {@link GenericPresenter#onDestroy(boolean)}
*
* @param isChangingConfiguration true: changing configuration
* false: being destroyed
*/
void onDestroy(boolean isChangingConfiguration);
}
================================================
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/PresenterOps.java
================================================
package com.tinmegali.mvp.mvp;
/**
* <p>
* Interface implemented by {@link GenericPresenter}. Contains
* initializing and destruction methods called by VIEW layer in
* the PRESENTER. Garantes the correct synchronization of MVP
* with Activity lifecycle.
* </p>
* <p>
* <strong>IMPORTANT: View Object should implement <code> RequiredViewOps </code></strong>
* </p>
*
* <p>
* Created by Tin Megali on 24/02/16. <br>
* Project: AndroidMVP <br>
*
* <a href="http://www.tinmegali.com">www.tinmegali.com</a>
* </p>
* --------------------------------------------------- <br>
* <p>
* Based on <a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
* framework MVP</a> developed by
* <a href="https://github.com/douglascraigschmidt">
* Dr. Douglas Schmidth</a>
* </p>
* @see <a href="https://github.com/tinmegali/simple-mvp">Project's Git</a> <br>
* @see <a href="https://github.com/tinmegali/simple-mvp/tree/master/AndroidMVP/app">Sample Application</a>
* @see <a href="https://github.com/tinmegali/simple-mvp/blob/master/AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java">
* Sample MVP interface
* </a>
*
* @param <RequiredViewOps> VIEW layer reference. Interface containing
* operations available to PRESENTER
*
*/
public interface PresenterOps<RequiredViewOps> extends ContextView {
/**
* Initialization method.
* Called by initialization methods in VIEW, like
* {@link GenericMVPActivity#initialize(Class, Object)}
* {@link GenericMVPFragment#initialize(Class, Object)}.
* Pass the VIEW reference with available operations to PRESENTER.
* @param view Current VIEW instance reference,
* with operations available
*/
void onCreate(RequiredViewOps view);
/**
* Hook method called every time the VIEW change its configuration.
* Called by reinitialization methods in VIEW, like
* {@link GenericMVPActivity#reinitialize(Class, Object)}
* {@link GenericMVPFragment#reinitialize(Class, Object)}
* Responsible to send a new reference to the VIEW layer with operations
* available to PRESENTER.
*
* @param view New VIEW instance reference
*/
void onConfigurationChanged(RequiredViewOps view);
/**
* Hook method called when the VIEW is being destroyed or
* having its configuration changed.
* Responsible to maintain MVP synchronized with Activity lifecycle.
* Called by onDestroy methods of the VIEW layer, like:
* {@link GenericMVPActivity#onDestroy()}
* {@link GenericMVPFragment#onDestroy()}
*
* @param isChangingConfiguration true: configuration changing
* false: being destroyed
*/
void onDestroy(boolean isChangingConfiguration);
/**
* OnBack pressed Event, called by the VIEW.
*/
void onBackPressed();
}
================================================
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/StateMaintainer.java
================================================
package com.tinmegali.mvp.mvp;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.util.HashMap;
/**
* todo english comments
* <p>Retains and maintain object's state between configuration changes
* in Activitys and Fragments.
* </p>
* <p>
* <strong>IMPORTANT: View Object should implement <code> RequiredViewOps </code></strong>
* </p>
* <p>
* Created by Tin Megali on 24/02/16. <br>
* Project: AndroidMVP <br>
*
* <a href="http://www.tinmegali.com">www.tinmegali.com</a>
* </p>
* --------------------------------------------------- <br>
* <p>
* Based on <a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
* framework MVP</a> developed by
* <a href="https://github.com/douglascraigschmidt">
* Dr. Douglas Schmidth</a>
* </p>
*
* @see <a href="https://github.com/tinmegali/simple-mvp">Project's Git</a> <br>
* @see <a href="https://github.com/tinmegali/simple-mvp/tree/master/AndroidMVP/app">Sample Application</a>
* @see <a href="https://github.com/tinmegali/simple-mvp/blob/master/AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java">
* Sample MVP interface
* </a>
*/
public class StateMaintainer {
protected final String TAG = getClass().getSimpleName();
private final String mStateMaintenerTag;
private final WeakReference<FragmentManager> mFragmentManager;
private StateMngFragment mStateMaintainerFrag;
private boolean mIsRecreating;
/**
* Construtor
* @param fragmentManager repassa uma referência do FragmentManager
* @param stateMaintainerTAG a TAG utilizada para inserir o fragmento responsável
* por manter os objetos "vivos"
*/
public StateMaintainer(FragmentManager fragmentManager, String stateMaintainerTAG) {
mFragmentManager = new WeakReference<>(fragmentManager);
mStateMaintenerTag = stateMaintainerTAG;
}
/**
* cria o fragmento responsável por armazenar o objetos
* @return true: criou o framentos e rodou pela primeira vez
* false: o objeto já foi criado, portanto é apenas recuperado
*/
public boolean firstTimeIn() {
try {
// Recuperando referência
mStateMaintainerFrag = (StateMngFragment)
mFragmentManager.get().findFragmentByTag(mStateMaintenerTag);
// Criando novo RetainedFragment
if (mStateMaintainerFrag == null) {
Log.d(TAG, "Criando novo RetainedFragment " + mStateMaintenerTag);
mStateMaintainerFrag = new StateMngFragment();
mFragmentManager.get().beginTransaction()
.add(mStateMaintainerFrag, mStateMaintenerTag).commit();
mIsRecreating = false;
return true;
} else {
Log.d(TAG, "Retornando retained fragment existente " + mStateMaintenerTag);
mIsRecreating = true;
return false;
}
} catch (NullPointerException e) {
Log.w(TAG, "Erro firstTimeIn()");
return false;
}
}
/**
* Return <strong>true</strong> it the current Activity was recreated at least one time
* @return If the Activity was recreated
*/
public boolean wasRecreated() { return mIsRecreating; }
/**
* Insere objeto a serem presenrvados durante mudanças de configuração
* @param key TAG de referência para recuperação do objeto
* @param obj Objeto a ser mantido
*/
public void put(String key, Object obj) {
mStateMaintainerFrag.put(key, obj);
}
/**
* Insere objeto a serem presenrvados durante mudanças de configuração.
* Utiliza a classe do Objeto como referência futura.
* Só deve ser utilizado somente uma vez por classe, caso contrário haverá
* possíveis conflitos na recuperação dos dados
* @param obj Objeto a ser mantido
*/
public void put(Object obj) {
put(obj.getClass().getName(), obj);
}
/**
* Recupera o objeto salvo
* @param key Chave de referência do obj
* @param <T> tipo genérico de retorno
* @return Objeto armazenado
*/
@SuppressWarnings("unchecked")
public <T> T get(String key) {
return mStateMaintainerFrag.get(key);
}
/**
* Verifica a existência de um objeto com a chave fornecida
* @param key Chave para verificação
* @return true: obj existe
* false: obj insexistente
*/
public boolean hasKey(String key) {
return mStateMaintainerFrag.get(key) != null;
}
/**
* Armazena e administra os objetos que devem ser preservados
* durante mudanças de configuração.
* É instanciado somente uma vez e utiliza um
* <code>HashMap</code> para salvar os objetos e suas
* chaves de referência.
*/
public static class StateMngFragment extends Fragment {
private HashMap<String, Object> mData = new HashMap<>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Garante que o Fragmento será preservado
// durante mudanças de configuração
setRetainInstance(true);
}
/**
* Insere objetos no hashmap
* @param key Chave de referência
* @param obj Objeto a ser salvo
*/
public void put(String key, Object obj) {
mData.put(key, obj);
}
/**
* Insere objeto utilizando o nome da classe como referência
* @param object Objeto a ser salvo
*/
public void put(Object object) {
put(object.getClass().getName(), object);
}
/**
* Recupera objeto salvo no hashmap
* @param key Chave de referência
* @param <T> Classe
* @return Objeto salvo
*/
@SuppressWarnings("unchecked")
public <T> T get(String key) {
return (T) mData.get(key);
}
}
}
================================================
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/package-info.java
================================================
/**
* <p>
* github.com/tinmegali/simple-mvp
* </p>
*
* Provides the necessary classes to implement
* a Model View Presenter architecture pattern.
* <p>
* Each layer of MVP pattern should extends
* its generic object and implement the correct
* interfaces to communicate with the other Layers. <br>
* <strong> Check sample MVP interface
* <a href="https://github.com/tinmegali/simple-mvp/blob/master/AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java">
* sample interface
* </a>
* </strong>
* <ul>
* <li><strong>VIEW</strong>
* <ul>
* <li>
* extends {@link com.tinmegali.mvp.mvp.GenericMVPActivity} or
* {@link com.tinmegali.mvp.mvp.GenericMVPFragment}
* </li>
* <li>
* implements <code>RequiredViewOps</code>: a interface that you create
* containing VIEW operations
* to be accessed by the PRESENTER
* </li>
* </ul>
*
* </li>
* <li>
* <strong>MODEL</strong>
* <ul>
* <li>
* extends {@link com.tinmegali.mvp.mvp.GenericModel}
* </li>
* <li>
* implements <code>ProvidedModelOps</code>: a interface that you create
* containing MODEL operations
* to be accessed by the PRESENTER
* </li>
*
* </ul>
*
* </li>
* <li>
* <strong>PRESENTER</strong>
* <ul>
* <li>
* extends {@link com.tinmegali.mvp.mvp.GenericPresenter}
* </li>
* <li>
* implements <code>RequiredPresenterOps</code>: a interface that you create
* containing PRESENTER operations
* to be accessed by the VIEW
* </li>
* <li>
* implements <code>ProvidedPresenterOps</code>: a interface that you create
* containing PRESENTER operations
* to be accessed by the MODEL
* </li>
* </ul>
* </li>
* </ul>
*
* @see <a href="https://github.com/tinmegali/simple-mvp">Project's Git</a> <br>
* @see <a href="https://github.com/tinmegali/simple-mvp/tree/master/AndroidMVP/app">Sample Application</a>
* @see <a href="https://github.com/tinmegali/simple-mvp/blob/master/AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java">
* Sample MVP interface
* </a>
*
* <p>
* Copyright 2016 Tin Megali
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://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.
* </p>
* <p>
* Created by Tin Megali on 24/02/16. <br>
* Project: AndroidMVP <br>
*
* <a href="http://www.tinmegali.com">www.tinmegali.com</a>
* </p>
* --------------------------------------------------- <br>
* <p>
* Based on <a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
* framework MVP</a> developed by
* <a href="https://github.com/douglascraigschmidt">
* Dr. Douglas Schmidth</a>
* </p>
*/
package com.tinmegali.mvp.mvp;
================================================
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/util/ActivityKeyBoardDetector.java
================================================
package com.tinmegali.mvp.util;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.Toast;
/**
* Created by Tin Megali on 24/02/16. <br>
* Project: AndroidMVP <br>
*
* <a href="http://www.tinmegali.com">www.tinmegali.com</a> <br>
* <a href="https://github.com/tinmegali/Android-Model-View-Presenter-MVP">Project's Git</a> <br>
* --------------------------------------------------- <br>
* <br><br>
* Object to add to an Activity the capability to detect the presence
* of the SoftKeyboard. <br>
*
*/
public abstract class ActivityKeyBoardDetector extends AppCompatActivity {
private final String TAG = ActivityKeyBoardDetector.class.getSimpleName();
@Override
protected void onDestroy() {
// Remove LayoutChange listener
stopDetectRootViewChanges();
super.onDestroy();
}
/**
* Shows a toast in the Activity with a short time
* @param msg Message to show
*/
protected void showToast(String msg) {
Toast.makeText(
getApplicationContext(), msg, Toast.LENGTH_SHORT
).show();
}
/**
* Shows a <code>android.widget.Toast</code> in the Activity with custom time
* @param msg Message to show
* @param duration Time Length
* <code>android.widget.Toast#LENGTH_SHORT</code>
* <code>@link android.widget.Toast#LENGTH_LONG</code>
*/
protected void showToast(String msg, final int duration) {
Toast.makeText(
getApplicationContext(), msg, Toast.LENGTH_SHORT
).show();
}
/**
* Shows a <code>Snackbar</code> in Activity with a short time
* @param msg Snackbar message
* @param view Snackbar parent view
*/
protected void showSnackbar(String msg, View view){
android.support.design.widget.Snackbar.make(view, msg, android.support.design.widget.Snackbar.LENGTH_SHORT).show();
}
/**
* Shows a <code>Snackbar</code> in Activity with custom time
* @param msg Snackbar message
* @param view Snackbar parent view
* @param duration Time Length
* <code>Snackbar#LENGTH_SHORT</code>
* <code>Snackbar#LENGTH_LONG</code>
* <code>Snackbar#LENGTH_INDEFINITE</code>
*/
protected void showSnackbar(String msg, View view, int duration){
android.support.design.widget.Snackbar.make( view, msg, duration).show();
}
/**
* Shows a given <code>Snackbar</code>
* Useful for snacks with custom actions.
* @param snackbar The Snackbar to show
*/
protected void showSnackbar(android.support.design.widget.Snackbar snackbar){
snackbar.show();
}
/**
* Detecting Layout Root changes
* <ul>
* <li>{@link #startDetectRootViewChanges(View)} must be called on {@link #onCreate(Bundle)}</li>
* <li>Detect if SoftKeyboard is On/Off
* {@link #onKeyBoardOn()} {@link #onKeyBoardOff()}</li>
* <li>Register event for layout changes
* {@link #onRootLayoutChanged(int)}</li>
* </ul>
*/
private View mActivityRootView;
private ViewTreeObserver.OnGlobalLayoutListener mLayoutListener;
/**
* Create a new Global Layout Listener
*/
private ViewTreeObserver.OnGlobalLayoutListener createLayoutListener() {
Log.d(TAG, "createLayoutListener");
final int KEYBOARD_GREATER_THAN = 125;
return new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect rect = new Rect();
mActivityRootView.getWindowVisibleDisplayFrame(rect);
int heightDiff = mActivityRootView.getHeight() - (rect.bottom - rect.top);
if (heightDiff>0) onRootLayoutChanged(heightDiff);
if (heightDiff > KEYBOARD_GREATER_THAN) { // if more than 100 pixels, its probably a keyboard...
Log.d(TAG, "onGlobalLayout(). Probabaly a keyBoard. > heightDiff[" + heightDiff + "]");
onKeyBoardOn();
mIsKeyboardOn = true;
} else {
if (mIsKeyboardOn) {
onKeyBoardOff();
mIsKeyboardOn = false;
}
}
}
};
}
/**
* Start to detect changed on Root layout height
* - Register a Root view
* - Create and starts a Global Layout Listener
* - Register root view layout events
* {@link #onKeyBoardOn()}
* {@link #onKeyBoardOff()}
* {@link #onRootLayoutChanged(int)}
* @param activityRootView Root layout to be monitored
*/
protected void startDetectRootViewChanges(final View activityRootView) {
Log.d(TAG, "startDetectRootViewChanges()");
mActivityRootView = activityRootView;
mIsKeyboardOn = false;
mLayoutListener = createLayoutListener();
mActivityRootView.getViewTreeObserver().addOnGlobalLayoutListener(mLayoutListener);
}
/**
* Remove Global Layout change listener
*/
protected void stopDetectRootViewChanges() {
Log.d(TAG, "stopDetectRootViewChanges()");
if ( mActivityRootView != null )
mActivityRootView.getViewTreeObserver().removeOnGlobalLayoutListener(mLayoutListener);
}
private Boolean mIsKeyboardOn;
/**
* Controls if SoftKeyBoard is on/off
* - May return null if {@link #startDetectRootViewChanges(View)} wasn't called
* @return true if SoftKeyboard is showing on screen
*/
protected Boolean isKeyboardOn() {
Log.d(TAG, "isKeyboardOn()");
return mIsKeyboardOn;
}
/**
* Called on SoftKeyBoard is ON
* - Depends on {@link #startDetectRootViewChanges(View)}
* - It must be called more than one time
*/
protected void onKeyBoardOn() {
Log.d(TAG, "onKeyBoardOn()");
}
/**
* Called on SoftKeyBoard is OFF
* - Depends on {@link #startDetectRootViewChanges(View)}
* - It must be called more than one time
*/
protected synchronized void onKeyBoardOff() {
Log.d(TAG, "onKeyBoardOff()");
}
/**
* Called when Root Layout view changes
* - Depends on {@link #startDetectRootViewChanges(View)}
* @param heightDiff Height Difference between current
* Root View an available Windo size
*/
protected void onRootLayoutChanged(int heightDiff) {
Log.d(TAG, "onRootLayoutChanged(heightDiff[" + heightDiff + "])");
}
}
================================================
FILE: AndroidMVP/mvp/src/main/res/values/AndroidManifest.xml
================================================
<manifest
package="com.tinmegali.mvp">
</manifest>
================================================
FILE: AndroidMVP/mvp/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">MVP</string>
</resources>
================================================
FILE: AndroidMVP/settings.gradle
================================================
include ':app', ':mvp'
================================================
FILE: README.md
================================================
<h1>Android-Model-View-Presenter-MVP</h1>
<p>
A Model View Presenter Library using plain and simple interfaces,
based on concept from
<a href="https://github.com/douglascraigschmidt/POSA-15/tree/master/ex/AcronymExpander/src/vandy/mooc">
Dr. Douglas Schmidt</a>
</p>
<p>
<strong>Attention: </strong>
<code>simple-mvp</code> is still an <strong>experimental</strong> library created for education purposes. <br/>
Some methods and operations may change until its maturity.
</p>
<p>
Created by <a href="http://www.tinmegali.com">Tin Megali</a>
</p>
<h3>Learn more</h3>
<ul>
<li>
<a href="http://wp.me/p7gH7l-34">
Step by step guide
</a>
</li>
<li>
<a href="http://www.tinmegali.com/en/model-view-presenter-android-part-1/">
Model View Presenter Tutorial series</a> <br/>
<em>Tutoriais também disponíveis em português</em>
</li>
<!--
<li>
<a href="http://tinmegali.com/my-libs/simple-mvp/javadoc/">
Using MVP library
</a>
</li>
-->
<li>
<a href="http://tinmegali.com/my-libs/simple-mvp/javadoc/">
JavaDoc
</a>
</li>
</ul>
<h3>Quick install</h3>
<ol>
<li>
add <em>build.gradle</em> <pre>compile 'com.tinmegali.mvp:mvp:0.0.7'</pre>
</li>
<li>
Create interfaces to communicate between MVP layers
<ul>
<li>
<code>interface RequiredViewOps extends ActivityView</code>
<br/>with VIEW methods to be accessed by PRESENTER
</li>
<li>
<code>interface ProvidedPresenterOps extends PresenterOps<RequiredViewOps></code>
<br/>Operations offered to VIEW to communicate with PRESENTER
</li>
<li>
<code>interface RequiredPresenterOps</code>
<br/>with Required PRESENTER methods available to MODEL
</li>
<li>
<code>interface ProvidedModelOps extends ModelOps<RequiredPresenterOps></code>
<br/>Operations offered to MODEL to communicate with PRESENTER
</li>
</ul>
</li>
<li>
Implement MVP objects extending its generics
<ul>
<li> MODEL from Model View Presenter (MVP) pattern. </br>
<code>
class MODEL
extends GenericModel<MVP_MainActivity.RequiredPresenterOps>
implements MVP_MainActivity.ProvidedModelOps
</code>
</li>
<li>VIEW layer of MVP pattern <br/>
<code>
class VIEW_Activity extends GenericMVPActivity<MVP_MainActivity.RequiredViewOps,
MVP_MainActivity.ProvidedPresenterOps,
MainPresenter>
implements MVP_MainActivity.RequiredViewOps
</code> <br />
<em>Could also extend <code>GenericMVPFragment</code></em>
</li>
<li>PRESENTER from Model View Presenter (MVP) Pattern. <br/>
<code>
class MainPresenter extends GenericPresenter<MVP_MainActivity.RequiredPresenterOps,
MVP_MainActivity.ProvidedModelOps, MVP_MainActivity.RequiredViewOps, MainModel>
implements
MVP_MainActivity.RequiredPresenterOps,
MVP_MainActivity.ProvidedPresenterOps
</code>
</li>
</ul>
</li>
</ol>
<h2>Instalação rápida</h2>
<ul>
<li>
Crie as interfaces de comunicação entre os módulos View, Presenter e Model
<ol>
<li>
interface <code>RequiredViewOps</code> fornece métodos para <code>Presenter</code>
comunicar com <code>View</code>. É necessário extender <code>ActivityView</code>
</li>
<li>
interface <code>ProvidedPresenterOps</code> fornece operações oferecidas
ao layer View para comunicação com Presenter.
É preciso extender <code>PresenterOps<RequiredViewOps></code>
</li>
<li>
interface <code>RequiredPresenterOps</code> operações oferecidas
pelo layer Presenter para comunicações com Model
</li>
<li>
interface <code>ProvidedModelOps</code> operações oferecidos pelo
layer Model para comunicações com Presenter.
É preciso extender <code>ModelOps<RequiredPresenterOps></code>
</li>
</ol>
</li>
<li>
Crie a classe <code>Model</code> extendendo <code>GenericModel<RequiredPresenterOps></code>
e implementando <code>ProvidedModelOps</code>
ex: {@link com.tinmegali.androidmvp.main.model.MainModel}
</li>
<li>
Crie a classe <code>Presenter</code> extendendo <code>GenericPresenter</code>,
implementando <code>RequiredPresenterOps</code> e <code>ProvidedPresenterOps</code>.
exemplo: {@link com.tinmegali.androidmvp.main.presenter.MainPresenter}
</li>
<li>
Crie a classe <code>View</code> GenericMVPActivity ou GenericMVPFragment e
implementando <code>RequiredViewOps</code>
Exemplo: {@link com.tinmegali.androidmvp.main.view.MainActivity}
</li>.
</ul>
<h2>Android MVP Class Diagram</h2>
<img src="http://www.tinmegali.com/wp-content/uploads/2016/02/mvp-class-diagram.jpg" />
gitextract__r8j11ak/ ├── AndroidMVP/ │ ├── .idea/ │ │ └── runConfigurations.xml │ ├── app/ │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src/ │ │ ├── androidTest/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── tinmegali/ │ │ │ └── androidmvp/ │ │ │ └── ApplicationTest.java │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── tinmegali/ │ │ │ │ └── androidmvp/ │ │ │ │ └── main/ │ │ │ │ ├── MVP_MainActivity.java │ │ │ │ ├── model/ │ │ │ │ │ └── MainModel.java │ │ │ │ ├── presenter/ │ │ │ │ │ └── MainPresenter.java │ │ │ │ └── view/ │ │ │ │ └── MainActivity.java │ │ │ └── res/ │ │ │ ├── layout/ │ │ │ │ ├── activity_main.xml │ │ │ │ └── content_main.xml │ │ │ ├── menu/ │ │ │ │ └── menu_main.xml │ │ │ ├── values/ │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── values-v21/ │ │ │ │ └── styles.xml │ │ │ └── values-w820dp/ │ │ │ └── dimens.xml │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── tinmegali/ │ │ └── androidmvp/ │ │ ├── TestModel.java │ │ └── TestPresenter.java │ ├── build.gradle │ ├── gradle/ │ │ └── wrapper/ │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ ├── mvp/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── tinmegali/ │ │ │ └── mvp/ │ │ │ ├── mvp/ │ │ │ │ ├── ActivityView.java │ │ │ │ ├── ContextView.java │ │ │ │ ├── GenericMVPActivity.java │ │ │ │ ├── GenericMVPFragment.java │ │ │ │ ├── GenericModel.java │ │ │ │ ├── GenericPresenter.java │ │ │ │ ├── ModelOps.java │ │ │ │ ├── PresenterOps.java │ │ │ │ ├── StateMaintainer.java │ │ │ │ └── package-info.java │ │ │ └── util/ │ │ │ └── ActivityKeyBoardDetector.java │ │ └── res/ │ │ └── values/ │ │ ├── AndroidManifest.xml │ │ └── strings.xml │ └── settings.gradle └── README.md
SYMBOL INDEX (122 symbols across 17 files)
FILE: AndroidMVP/app/src/androidTest/java/com/tinmegali/androidmvp/ApplicationTest.java
class ApplicationTest (line 9) | public class ApplicationTest extends ApplicationTestCase<Application> {
method ApplicationTest (line 10) | public ApplicationTest() {
FILE: AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java
type MVP_MainActivity (line 42) | public interface MVP_MainActivity {
type RequiredViewOps (line 48) | interface RequiredViewOps extends ActivityView {
type ProvidedPresenterOps (line 56) | interface ProvidedPresenterOps extends PresenterOps<RequiredViewOps> {
method clickSaveName (line 57) | boolean clickSaveName(String nameTxt);
method clickClearName (line 58) | boolean clickClearName();
type RequiredPresenterOps (line 65) | interface RequiredPresenterOps {
method onNameSaved (line 66) | boolean onNameSaved(String nameTxt);
method onNameCleared (line 67) | boolean onNameCleared();
type ProvidedModelOps (line 74) | interface ProvidedModelOps extends ModelOps<RequiredPresenterOps> {
method saveName (line 75) | boolean saveName(String nameTxt);
method clearName (line 76) | boolean clearName();
FILE: AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/model/MainModel.java
class MainModel (line 29) | public class MainModel extends GenericModel<MVP_MainActivity.RequiredPre...
method onCreate (line 39) | @Override
method onDestroy (line 50) | @Override
method clearName (line 57) | @Override
method saveName (line 63) | @Override
method getName (line 88) | public String getName(){
FILE: AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/presenter/MainPresenter.java
class MainPresenter (line 33) | public class MainPresenter
method onCreate (line 58) | @Override
method onConfigurationChanged (line 75) | @Override
method onBackPressed (line 85) | @Override
method clickClearName (line 91) | @Override
method clearName (line 97) | public boolean clearName(){
method clickSaveName (line 101) | @Override
method saveName (line 107) | public boolean saveName(String txt){
method onNameSaved (line 111) | @Override
method onNameCleared (line 116) | @Override
FILE: AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/view/MainActivity.java
class MainActivity (line 32) | public class MainActivity
method onCreate (line 44) | @Override
FILE: AndroidMVP/app/src/test/java/com/tinmegali/androidmvp/TestModel.java
class TestModel (line 28) | @RunWith(RobolectricGradleTestRunner.class)
method setup (line 35) | @Before
method testRealModel (line 42) | @Test
FILE: AndroidMVP/app/src/test/java/com/tinmegali/androidmvp/TestPresenter.java
class TestPresenter (line 30) | @RunWith(JUnit4.class)
method setup (line 38) | @Before
method testRealPresenter (line 53) | @Test
method testSpyPresenter (line 63) | @Test
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/ActivityView.java
type ActivityView (line 23) | public interface ActivityView extends ContextView {
method onShowToast (line 29) | void onShowToast(String msg);
method onShowToast (line 38) | void onShowToast(String msg, int duration);
method onShowSnackbar (line 45) | void onShowSnackbar(String msg, View parentView);
method onShowSnackbar (line 56) | void onShowSnackbar(String msg, View parentView, int duration);
method onShowSnackbar (line 64) | void onShowSnackbar(android.support.design.widget.Snackbar snackbar);
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/ContextView.java
type ContextView (line 29) | public interface ContextView {
method getApplicationContext (line 35) | Context getApplicationContext();
method getActivityContext (line 41) | Context getActivityContext();
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/GenericMVPActivity.java
class GenericMVPActivity (line 47) | public abstract class GenericMVPActivity
method onCreate (line 70) | public void onCreate(Class<PresenterType> opsType, RequiredViewOps vie...
method getPresenter (line 89) | @SuppressWarnings("unchecked")
method getStateMaintainer (line 95) | public StateMaintainer getStateMaintainer() { return mStateMaintainer; }
method getActivityContext (line 100) | @Override
method getApplicationContext (line 108) | @Override
method initialize (line 123) | private void initialize( Class<PresenterType> opsType, RequiredViewOps...
method reinitialize (line 142) | private void reinitialize( Class<PresenterType> opsType, RequiredViewO...
method onShowSnackbar (line 159) | public void onShowSnackbar(String msg, View parentView) {
method onShowToast (line 167) | public void onShowToast(String msg) {
method onShowToast (line 177) | public void onShowToast(String msg, int duration) {
method onShowSnackbar (line 189) | public void onShowSnackbar(String msg, View parentView, int duration) {
method onShowSnackbar (line 197) | public void onShowSnackbar(Snackbar snackbar) {
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/GenericMVPFragment.java
class GenericMVPFragment (line 37) | public abstract class GenericMVPFragment<
method onAttach (line 61) | @SuppressWarnings("unchecked")
method onCreate (line 79) | public void onCreate(Class<PresenterType> opsType, RequiredViewOps vie...
method getPresenter (line 101) | @SuppressWarnings("unchecked")
method getStateMaintainer (line 107) | public StateMaintainer getStateMaintainer() { return mStateMaintainer; }
method initialize (line 119) | private void initialize( Class<PresenterType> opsType, RequiredViewOps...
method reinitialize (line 138) | private void reinitialize( Class<PresenterType> opsType, RequiredViewO...
method onDestroy (line 149) | @SuppressWarnings("unchecked")
method onPause (line 157) | @SuppressWarnings("unchecked")
method getActivityContext (line 168) | @Override
method getApplicationContext (line 176) | @Override
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/GenericModel.java
class GenericModel (line 35) | public abstract class GenericModel<RequiredPresenterOps> implements Mode...
method onCreate (line 49) | @Override
method onDestroy (line 65) | @Override
method getPresenter (line 74) | public RequiredPresenterOps getPresenter() { return mPresenter; }
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/GenericPresenter.java
class GenericPresenter (line 55) | public abstract class GenericPresenter
method onCreate (line 86) | public void onCreate( Class<ModelType> opsType, RequiredPresenterOps p...
method setView (line 104) | public void setView(RequiredViewOps view) {
method getView (line 112) | public RequiredViewOps getView() {
method getApplicationContext (line 125) | public Context getApplicationContext() {
method getActivityContext (line 133) | public Context getActivityContext() {
method onDestroy (line 145) | public void onDestroy(boolean isChangingConfiguration) {
method isViewRunning (line 157) | public boolean isViewRunning() { return mIsRunning; }
method configurationsOccurred (line 163) | public boolean configurationsOccurred() { return mConfigurationChangeO...
method initialize (line 176) | private void initialize( Class<ModelType> opsType, RequiredPresenterOp...
method getModel (line 187) | @SuppressWarnings("unchecked")
method testWithModel (line 196) | public void testWithModel(ModelType model) {
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/ModelOps.java
type ModelOps (line 38) | public interface ModelOps<RequiredPresenterOps> {
method onCreate (line 47) | void onCreate(RequiredPresenterOps presenterOps);
method onDestroy (line 58) | void onDestroy(boolean isChangingConfiguration);
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/PresenterOps.java
type PresenterOps (line 37) | public interface PresenterOps<RequiredViewOps> extends ContextView {
method onCreate (line 48) | void onCreate(RequiredViewOps view);
method onConfigurationChanged (line 60) | void onConfigurationChanged(RequiredViewOps view);
method onDestroy (line 73) | void onDestroy(boolean isChangingConfiguration);
method onBackPressed (line 78) | void onBackPressed();
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/StateMaintainer.java
class StateMaintainer (line 39) | public class StateMaintainer {
method StateMaintainer (line 54) | public StateMaintainer(FragmentManager fragmentManager, String stateMa...
method firstTimeIn (line 64) | public boolean firstTimeIn() {
method wasRecreated (line 93) | public boolean wasRecreated() { return mIsRecreating; }
method put (line 101) | public void put(String key, Object obj) {
method put (line 112) | public void put(Object obj) {
method get (line 123) | @SuppressWarnings("unchecked")
method hasKey (line 135) | public boolean hasKey(String key) {
class StateMngFragment (line 147) | public static class StateMngFragment extends Fragment {
method onCreate (line 150) | @Override
method put (line 163) | public void put(String key, Object obj) {
method put (line 171) | public void put(Object object) {
method get (line 181) | @SuppressWarnings("unchecked")
FILE: AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/util/ActivityKeyBoardDetector.java
class ActivityKeyBoardDetector (line 23) | public abstract class ActivityKeyBoardDetector extends AppCompatActivity {
method onDestroy (line 27) | @Override
method showToast (line 38) | protected void showToast(String msg) {
method showToast (line 51) | protected void showToast(String msg, final int duration) {
method showSnackbar (line 62) | protected void showSnackbar(String msg, View view){
method showSnackbar (line 76) | protected void showSnackbar(String msg, View view, int duration){
method showSnackbar (line 86) | protected void showSnackbar(android.support.design.widget.Snackbar sna...
method createLayoutListener (line 107) | private ViewTreeObserver.OnGlobalLayoutListener createLayoutListener() {
method startDetectRootViewChanges (line 143) | protected void startDetectRootViewChanges(final View activityRootView) {
method stopDetectRootViewChanges (line 156) | protected void stopDetectRootViewChanges() {
method isKeyboardOn (line 169) | protected Boolean isKeyboardOn() {
method onKeyBoardOn (line 179) | protected void onKeyBoardOn() {
method onKeyBoardOff (line 187) | protected synchronized void onKeyBoardOff() {
method onRootLayoutChanged (line 196) | protected void onRootLayoutChanged(int heightDiff) {
Condensed preview — 42 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (87K chars).
[
{
"path": "AndroidMVP/.idea/runConfigurations.xml",
"chars": 564,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"RunConfigurationProducerService\">\n <o"
},
{
"path": "AndroidMVP/app/build.gradle",
"chars": 1091,
"preview": "apply plugin: 'com.android.application'\n\nandroid {\n compileSdkVersion 23\n buildToolsVersion \"23.0.2\"\n\n defaultC"
},
{
"path": "AndroidMVP/app/proguard-rules.pro",
"chars": 667,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
},
{
"path": "AndroidMVP/app/src/androidTest/java/com/tinmegali/androidmvp/ApplicationTest.java",
"chars": 355,
"preview": "package com.tinmegali.androidmvp;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a h"
},
{
"path": "AndroidMVP/app/src/main/AndroidManifest.xml",
"chars": 788,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/MVP_MainActivity.java",
"chars": 2375,
"preview": "package com.tinmegali.androidmvp.main;\n\nimport com.tinmegali.mvp.mvp.ActivityView;\nimport com.tinmegali.mvp.mvp.ModelOps"
},
{
"path": "AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/model/MainModel.java",
"chars": 2711,
"preview": "package com.tinmegali.androidmvp.main.model;\n\nimport android.os.AsyncTask;\n\nimport com.tinmegali.androidmvp.main.MVP_Mai"
},
{
"path": "AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/presenter/MainPresenter.java",
"chars": 3503,
"preview": "package com.tinmegali.androidmvp.main.presenter;\n\nimport com.tinmegali.androidmvp.main.MVP_MainActivity;\nimport com.tinm"
},
{
"path": "AndroidMVP/app/src/main/java/com/tinmegali/androidmvp/main/view/MainActivity.java",
"chars": 2188,
"preview": "package com.tinmegali.androidmvp.main.view;\n\nimport android.os.Bundle;\nimport android.support.design.widget.FloatingActi"
},
{
"path": "AndroidMVP/app/src/main/res/layout/activity_main.xml",
"chars": 1388,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.design.widget.CoordinatorLayout xmlns:android=\"http://schemas.an"
},
{
"path": "AndroidMVP/app/src/main/res/layout/content_main.xml",
"chars": 852,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xm"
},
{
"path": "AndroidMVP/app/src/main/res/menu/menu_main.xml",
"chars": 399,
"preview": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:app=\"http://schemas.android.com/apk/res-auto\""
},
{
"path": "AndroidMVP/app/src/main/res/values/colors.xml",
"chars": 208,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"colorPrimary\">#3F51B5</color>\n <color name=\"color"
},
{
"path": "AndroidMVP/app/src/main/res/values/dimens.xml",
"chars": 253,
"preview": "<resources>\n <!-- Default screen margins, per the Android Design guidelines. -->\n <dimen name=\"activity_horizontal"
},
{
"path": "AndroidMVP/app/src/main/res/values/strings.xml",
"chars": 126,
"preview": "<resources>\n <string name=\"app_name\">AndroidMVP</string>\n <string name=\"action_settings\">Settings</string>\n</resou"
},
{
"path": "AndroidMVP/app/src/main/res/values/styles.xml",
"chars": 708,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
},
{
"path": "AndroidMVP/app/src/main/res/values-v21/styles.xml",
"chars": 328,
"preview": "<resources>>\n\n <style name=\"AppTheme.NoActionBar\">\n <item name=\"windowActionBar\">false</item>\n <item na"
},
{
"path": "AndroidMVP/app/src/main/res/values-w820dp/dimens.xml",
"chars": 358,
"preview": "<resources>\n <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n (such as s"
},
{
"path": "AndroidMVP/app/src/test/java/com/tinmegali/androidmvp/TestModel.java",
"chars": 1461,
"preview": "package com.tinmegali.androidmvp;\n\nimport com.tinmegali.androidmvp.main.model.MainModel;\nimport com.tinmegali.androidmvp"
},
{
"path": "AndroidMVP/app/src/test/java/com/tinmegali/androidmvp/TestPresenter.java",
"chars": 2234,
"preview": "package com.tinmegali.androidmvp;\n\nimport com.tinmegali.androidmvp.main.MVP_MainActivity;\nimport com.tinmegali.androidmv"
},
{
"path": "AndroidMVP/build.gradle",
"chars": 580,
"preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n r"
},
{
"path": "AndroidMVP/gradle/wrapper/gradle-wrapper.properties",
"chars": 230,
"preview": "#Wed Oct 21 11:34:03 PDT 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "AndroidMVP/gradle.properties",
"chars": 855,
"preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
},
{
"path": "AndroidMVP/mvp/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "AndroidMVP/mvp/build.gradle",
"chars": 2199,
"preview": "apply plugin: 'com.android.library'\napply plugin: 'com.jfrog.bintray'\napply plugin: 'com.github.dcendents.android-maven'"
},
{
"path": "AndroidMVP/mvp/proguard-rules.pro",
"chars": 667,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
},
{
"path": "AndroidMVP/mvp/src/main/AndroidManifest.xml",
"chars": 265,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.tinmegali.mvp\">\n\n <application\n"
},
{
"path": "AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/ActivityView.java",
"chars": 2189,
"preview": "package com.tinmegali.mvp.mvp;\n\nimport android.view.View;\n\n/**\n * <p>\n * Interface to be implemented by the Activity,\n *"
},
{
"path": "AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/ContextView.java",
"chars": 1281,
"preview": "package com.tinmegali.mvp.mvp;\n\nimport android.content.Context;\n\n/**\n * <p>Interface that grants access to Contexts</p>\n"
},
{
"path": "AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/GenericMVPActivity.java",
"chars": 6770,
"preview": "package com.tinmegali.mvp.mvp;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.os.Bundle;\ni"
},
{
"path": "AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/GenericMVPFragment.java",
"chars": 6177,
"preview": "package com.tinmegali.mvp.mvp;\n\nimport android.content.Context;\nimport android.util.Log;\n\nimport java.lang.ref.WeakRefer"
},
{
"path": "AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/GenericModel.java",
"chars": 2813,
"preview": "package com.tinmegali.mvp.mvp;\n\nimport android.util.Log;\n\n/**\n * <p>\n * Abstract class of MODEL layer on MVP pattern"
},
{
"path": "AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/GenericPresenter.java",
"chars": 6543,
"preview": "package com.tinmegali.mvp.mvp;\n\nimport android.content.Context;\nimport android.util.Log;\n\nimport java.lang.ref.WeakRefer"
},
{
"path": "AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/ModelOps.java",
"chars": 2209,
"preview": "package com.tinmegali.mvp.mvp;\n\n/**\n * <p>\n * Interface implemented by {@link GenericModel}. <br>\n * Contains\n * initial"
},
{
"path": "AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/PresenterOps.java",
"chars": 2996,
"preview": "package com.tinmegali.mvp.mvp;\n\n/**\n * <p>\n * Interface implemented by {@link GenericPresenter}. Contains\n * initializin"
},
{
"path": "AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/StateMaintainer.java",
"chars": 6291,
"preview": "package com.tinmegali.mvp.mvp;\n\nimport android.app.Fragment;\nimport android.app.FragmentManager;\nimport android.os.Bundl"
},
{
"path": "AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/mvp/package-info.java",
"chars": 3521,
"preview": "/**\n * <p>\n * github.com/tinmegali/simple-mvp\n * </p>\n *\n * Provides the necessary classes to implement\n * a Model V"
},
{
"path": "AndroidMVP/mvp/src/main/java/com/tinmegali/mvp/util/ActivityKeyBoardDetector.java",
"chars": 6846,
"preview": "package com.tinmegali.mvp.util;\n\nimport android.graphics.Rect;\nimport android.os.Bundle;\nimport android.support.v7.app.A"
},
{
"path": "AndroidMVP/mvp/src/main/res/values/AndroidManifest.xml",
"chars": 56,
"preview": "<manifest\n package=\"com.tinmegali.mvp\">\n\n\n</manifest>"
},
{
"path": "AndroidMVP/mvp/src/main/res/values/strings.xml",
"chars": 66,
"preview": "<resources>\n <string name=\"app_name\">MVP</string>\n</resources>\n"
},
{
"path": "AndroidMVP/settings.gradle",
"chars": 23,
"preview": "include ':app', ':mvp'\n"
},
{
"path": "README.md",
"chars": 5022,
"preview": "<h1>Android-Model-View-Presenter-MVP</h1>\n\n<p>\nA Model View Presenter Library using plain and simple interfaces,\nbased o"
}
]
About this extraction
This page contains the full source code of the tinmegali/simple-mvp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 42 files (78.3 KB), approximately 20.8k tokens, and a symbol index with 122 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.