diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0301369
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,64 @@
+# Built application files
+/*/build/
+
+# Crashlytics configuations
+com_crashlytics_export_strings.xml
+
+# Signing files
+.signing/
+
+# User-specific configurations
+.idea/
+*.iml
+
+# OS-specific files
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+
+# Built application files
+*.apk
+*.ap_
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# Intellij
+.idea/libraries
+
+# Keystore files
+*.jks
+
+# Temp files
+*~
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..b2b8732
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2016 Mateusz Koślacz
+
+ 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.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..591c392
--- /dev/null
+++ b/README.md
@@ -0,0 +1,51 @@
+# Moviper
+
+### A [Mosby](https://github.com/sockeqwe/mosby) based [VIPER](https://www.objc.io/issues/13-architecture/viper/) library for Android
+
+## Why Moviper?
+
+You got tired because of fact that your Activities and Fragments were becoming god classes, so you have migrated to MVP. Now you're tired of your god-class presenters and you just want to stop continuously wondering if you should pass a context to your presenters and make them platform-dependent and harder to mock and test, or maybe you should let your view-activity manage the system connected work. That's why you're here. My Android VIPER interpretation allows you keep your code clean, neat, and more [SRP](https://en.wikipedia.org/wiki/Single_responsibility_principle) with minimal effort.
+
+### OK, but for every screen I have to create so many files!
+
+To avoid manually creating all VIPER class files and managing their dependencies every time you want to create a new screen I created a generator that does all of necessary work for you. You can find it [here](https://github.com/mkoslacz/MoviperTemplateGenerator).
+
+### Great! But I'm used to go with Mosby. What about all of its goodies?
+
+You are able to use all of the Mosby's MVP Views with Moviper. MvpFragment, MvpLceActivity, ViewStateFragment etc. are fully compatibile with Moviper presenters.
+
+## Dependency
+
+I haven't yet deployed the library to any repo, so for now you will have to do a `git clone` and manually link the library to your project like [this](http://stackoverflow.com/a/31366602/3898686). I will deploy the library very soon.
+
+## Getting started
+
+Just create a VIPER files set using [Moviper Template Generator](https://github.com/mkoslacz/MoviperTemplateGenerator), fill up the contract, generate missing methods using Android Studio autofix
+and implement them. Most probably you will want to check out a sample module provided in this repository to see how to use Moviper.
+
+## Examples
+
+Just check out the sample module in this repo.
+
+## Credits
+
+This library is built on top of a great [Mosby](https://github.com/sockeqwe/mosby) library by [Hannes Dorfmann](http://hannesdorfmann.com/). Check out the [project website](http://hannesdorfmann.com/mosby/).
+
+I've also used a Lucas Urbas Interactor implementation idea presented in his [Search viper architecture example](https://github.com/lurbas/Search).
+
+## License
+```
+Copyright 2016 Mateusz Koślacz
+
+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.
+```
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..3eebc9b
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,26 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.1.3'
+ classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
+ classpath 'me.tatarka:gradle-retrolambda:3.2.5'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..4b601f2
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,19 @@
+# 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
+org.gradle.jvmargs=-Xmx4096m
+
+# 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
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..f573d78
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Aug 16 17:56:20 CEST 2016
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/moviper/.gitignore b/moviper/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/moviper/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/moviper/build.gradle b/moviper/build.gradle
new file mode 100644
index 0000000..85e9bf6
--- /dev/null
+++ b/moviper/build.gradle
@@ -0,0 +1,27 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 24
+ buildToolsVersion "24.0.1"
+
+ defaultConfig {
+ minSdkVersion 16
+ targetSdkVersion 24
+ 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:24.2.0'
+ compile 'com.hannesdorfmann.mosby:mvp:2.0.1'
+ compile 'com.hannesdorfmann.mosby:viewstate:2.0.1'
+}
diff --git a/moviper/proguard-rules.pro b/moviper/proguard-rules.pro
new file mode 100644
index 0000000..c9d9908
--- /dev/null
+++ b/moviper/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/mateuszkoslacz/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 *;
+#}
diff --git a/moviper/src/androidTest/java/com/mateuszkoslacz/moviper/ApplicationTest.java b/moviper/src/androidTest/java/com/mateuszkoslacz/moviper/ApplicationTest.java
new file mode 100644
index 0000000..f895e3b
--- /dev/null
+++ b/moviper/src/androidTest/java/com/mateuszkoslacz/moviper/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.mateuszkoslacz.moviper;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/moviper/src/main/AndroidManifest.xml b/moviper/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b0a6f24
--- /dev/null
+++ b/moviper/src/main/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/base/interactor/BaseInteractor.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/base/interactor/BaseInteractor.java
new file mode 100644
index 0000000..210f66c
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/base/interactor/BaseInteractor.java
@@ -0,0 +1,47 @@
+package com.mateuszkoslacz.moviper.base.interactor;
+
+
+import android.support.annotation.Nullable;
+
+import com.mateuszkoslacz.moviper.iface.interactor.MoviperInteractor;
+import com.mateuszkoslacz.moviper.iface.presenter.interactor.MoviperPresenterForInteractor;
+import com.mateuszkoslacz.moviper.util.WeakReferenceUtils;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Created by lucas.urbas on 29/08/15.
+ *
+ * Adapted and modified by mateuszkoslacz on 08.08.2016.
+ *
+ * This is a universal base presenter class for mentioned set of concepts.
+ * (see {@link MvpBasePresenter})
+ *
+ * You can use any Mosby View with this class (activity views:
+ * {@link com.hannesdorfmann.mosby.mvp.MvpActivity},
+ * {@link com.hannesdorfmann.mosby.mvp.lce.MvpLceActivity},
+ * {@link com.hannesdorfmann.mosby.mvp.viewstate.MvpViewStateActivity},
+ * {@link com.hannesdorfmann.mosby.mvp.viewstate.lce.MvpLceViewStateActivity},
+ * and fragment ones:
+ * {@link com.hannesdorfmann.mosby.mvp.MvpFragment},
+ * {@link com.hannesdorfmann.mosby.mvp.lce.MvpLceFragment},
+ * {@link com.hannesdorfmann.mosby.mvp.viewstate.MvpViewStateFragment},
+ * {@link com.hannesdorfmann.mosby.mvp.viewstate.lce.MvpLceViewStateFragment})
+ */
+//TODO migrate to MvpNullObjectPresenter base class?
+public abstract class WipeBasePresenter
+
+ extends MvpBasePresenter
+ implements MvpPresenter,
+ MoviperPresenterForInteractor {
+
+ @Nullable
+ private InteractorType interactor;
+
+ public WipeBasePresenter() {
+ super();
+ this.interactor = createInteractor();
+ }
+
+ @Override
+ public boolean isInteractorAttached() {
+ return interactor != null;
+ }
+
+ @Override
+ public void attachView(ViewType view) {
+ super.attachView(view);
+ assert interactor != null;
+ //noinspection unchecked
+ interactor.attachPresenter(this);
+ }
+
+ @Override
+ public void detachView(boolean retainInstance) {
+ super.detachView(retainInstance);
+ assert interactor != null;
+ interactor.detachPresenter();
+ interactor = null;
+ }
+
+ @Nullable
+ @Override
+ public InteractorType getInteractor() {
+ return interactor;
+ }
+
+
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/base/routing/ActivityBaseRouting.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/base/routing/ActivityBaseRouting.java
new file mode 100644
index 0000000..b1c3400
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/base/routing/ActivityBaseRouting.java
@@ -0,0 +1,66 @@
+package com.mateuszkoslacz.moviper.base.routing;
+
+import android.app.Activity;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.mateuszkoslacz.moviper.iface.presenter.routing.MoviperPresenterForRouting;
+import com.mateuszkoslacz.moviper.iface.routing.MoviperRouting;
+import com.mateuszkoslacz.moviper.util.WeakReferenceUtils;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Created by mateuszkoslacz on 08.08.2016.
+ *
+ * Activity version of base Routing class.
+ * (see {@link MoviperRouting} and {@link MoviperPresenterForRouting})
+ */
+public abstract class ActivityBaseRouting
+ // I prefer readability rather than conventions
+ implements MoviperRouting {
+
+ @Nullable
+ protected WeakReference activity;
+
+ @Nullable
+ private WeakReference presenter;
+
+ public ActivityBaseRouting(@NonNull Activity activity) {
+ this.activity = new WeakReference<>(activity);
+ }
+
+ @Override
+ public boolean isActivityAttached() {
+ return WeakReferenceUtils.isAttached(activity);
+ }
+
+ @Nullable
+ @Override
+ public Activity getActivity() {
+ return WeakReferenceUtils.get(activity);
+ }
+
+ @Override
+ public void attachPresenter(PresenterType presenter) {
+ this.presenter = new WeakReference<>(presenter);
+ }
+
+ @Nullable
+ @Override
+ public PresenterType getPresenter() {
+ return WeakReferenceUtils.get(presenter);
+ }
+
+ @Override
+ public boolean isPresenterAttached() {
+ return WeakReferenceUtils.isAttached(presenter);
+ }
+
+ @Override
+ public void detachPresenter() {
+ WeakReferenceUtils.detach(presenter);
+ WeakReferenceUtils.detach(activity);
+ }
+
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/base/routing/ActivityBaseViewHelperRouting.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/base/routing/ActivityBaseViewHelperRouting.java
new file mode 100644
index 0000000..895d9ea
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/base/routing/ActivityBaseViewHelperRouting.java
@@ -0,0 +1,40 @@
+package com.mateuszkoslacz.moviper.base.routing;
+
+import android.app.Activity;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.mateuszkoslacz.moviper.iface.presenter.routing.MoviperPresenterForRouting;
+import com.mateuszkoslacz.moviper.iface.routing.MoviperViewHelperRouting;
+import com.mateuszkoslacz.moviper.iface.viewhelper.MoviperViewHelper;
+import com.mateuszkoslacz.moviper.util.WeakReferenceUtils;
+
+/**
+ * Created by mateuszkoslacz on 08.08.2016.
+ *
+ * Activity version of base Routing class with ViewHelper. (see {@link MoviperViewHelperRouting},
+ * {@link MoviperViewHelper} and {@link MoviperPresenterForRouting})
+ */
+public abstract class ActivityBaseViewHelperRouting
+
+ extends ActivityBaseRouting
+ implements MoviperViewHelperRouting {
+
+ public ActivityBaseViewHelperRouting(@NonNull Activity activity) {
+ super(activity);
+ }
+
+ @Nullable
+ @Override
+ public ViewHelperType getViewHelper() {
+ //noinspection unchecked
+ return (ViewHelperType) WeakReferenceUtils.get(activity);
+ }
+
+ @Override
+ public boolean isViewHelperAttached() {
+ return isActivityAttached();
+ }
+
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/base/routing/FragmentBaseRouting.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/base/routing/FragmentBaseRouting.java
new file mode 100644
index 0000000..7a4aeaf
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/base/routing/FragmentBaseRouting.java
@@ -0,0 +1,23 @@
+package com.mateuszkoslacz.moviper.base.routing;
+
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+
+import com.mateuszkoslacz.moviper.iface.presenter.routing.MoviperPresenterForRouting;
+import com.mateuszkoslacz.moviper.iface.routing.MoviperRouting;
+
+/**
+ * Created by mateuszkoslacz on 08.08.2016.
+ *
+ * Fragment version of base Routing class.
+ * (see {@link MoviperRouting} and {@link MoviperPresenterForRouting})
+ */
+public abstract class FragmentBaseRouting
+ // I prefer readability rather than conventions
+ extends ActivityBaseRouting
+ implements MoviperRouting {
+
+ public FragmentBaseRouting(@NonNull Fragment fragment) {
+ super(fragment.getActivity());
+ }
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/base/routing/FragmentBaseViewHelperRouting.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/base/routing/FragmentBaseViewHelperRouting.java
new file mode 100644
index 0000000..7896d00
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/base/routing/FragmentBaseViewHelperRouting.java
@@ -0,0 +1,51 @@
+package com.mateuszkoslacz.moviper.base.routing;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+
+import com.mateuszkoslacz.moviper.iface.presenter.routing.MoviperPresenterForRouting;
+import com.mateuszkoslacz.moviper.iface.routing.MoviperViewHelperRouting;
+import com.mateuszkoslacz.moviper.iface.viewhelper.MoviperViewHelper;
+import com.mateuszkoslacz.moviper.util.WeakReferenceUtils;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Created by mateuszkoslacz on 08.08.2016.
+ *
+ * Fragment version of base Routing class with ViewHelper. (see {@link MoviperViewHelperRouting},
+ * {@link MoviperViewHelper} and {@link MoviperPresenterForRouting})
+ */
+public abstract class FragmentBaseViewHelperRouting
+
+ extends ActivityBaseViewHelperRouting
+ implements MoviperViewHelperRouting {
+
+ @Nullable
+ private WeakReference fragment;
+
+ public FragmentBaseViewHelperRouting(@NonNull Fragment fragment) {
+ super(fragment.getActivity());
+ this.fragment = new WeakReference<>(fragment);
+ }
+
+ @Override
+ public boolean isViewHelperAttached() {
+ return WeakReferenceUtils.isAttached(fragment);
+ }
+
+ @Nullable
+ @Override
+ public ViewHelperType getViewHelper() {
+ //noinspection unchecked
+ return ((ViewHelperType) WeakReferenceUtils.get(fragment));
+ }
+
+ @Override
+ public void detachPresenter() {
+ super.detachPresenter();
+ WeakReferenceUtils.detach(fragment);
+ }
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/interactor/MoviperInteractor.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/interactor/MoviperInteractor.java
new file mode 100644
index 0000000..b20415c
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/interactor/MoviperInteractor.java
@@ -0,0 +1,33 @@
+package com.mateuszkoslacz.moviper.iface.interactor;
+
+import android.support.annotation.Nullable;
+
+import com.mateuszkoslacz.moviper.iface.presenter.interactor.MoviperPresenterForInteractor;
+
+/**
+ * Created by lucas.urbas on 29/08/15.
+ *
+ * Adapted and modified by mateuszkoslacz on 08.08.2016.
+ *
+ * It's responsible of manipulating data on behalf of Presenter (see Mosby's
+ * {@link com.hannesdorfmann.mosby.mvp.MvpPresenter}). It queries an API, DB etc.
+ */
+// I prefer readability rather than conventions
+public interface MoviperInteractor {
+
+ /**
+ * Remember to call {@link #isPresenterAttached()} before getting the Presenter to avoid
+ * {@link NullPointerException}s.
+ *
+ * @return attached {@link MoviperPresenterForInteractor} subclass instance or
+ * null if it's detached (View got destroyed)
+ */
+ @Nullable
+ PresenterType getPresenter();
+
+ boolean isPresenterAttached();
+
+ void attachPresenter(PresenterType presenter);
+
+ void detachPresenter();
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/presenter/interactor/MoviperPresenterForInteractor.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/presenter/interactor/MoviperPresenterForInteractor.java
new file mode 100644
index 0000000..b56ca06
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/presenter/interactor/MoviperPresenterForInteractor.java
@@ -0,0 +1,41 @@
+package com.mateuszkoslacz.moviper.iface.presenter.interactor;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.mateuszkoslacz.moviper.iface.interactor.MoviperInteractor;
+
+/**
+ * Created by lucas.urbas on 29/08/15.
+ *
+ * Adapted and modified by mateuszkoslacz on 08.08.2016.
+ *
+ * It defines presenter interface which Interactor ({@link MoviperInteractor}) needs to communicate
+ * with Presenter ({@link com.hannesdorfmann.mosby.mvp.MvpBasePresenter} for proper
+ * execution of given tasks. There are mostly callbacks for displaying data from interactor,
+ * interactor's error messages etc.
+ */
+// I prefer readability rather than conventions
+public interface MoviperPresenterForInteractor {
+
+ boolean isInteractorAttached();
+
+ /**
+ * Remember to call {@link #isInteractorAttached()} before getting the Interactor to avoid
+ * {@link NullPointerException}s.
+ *
+ * @return attached {@link MoviperInteractor} subclass instance or
+ * null if it's detached (View got destroyed)
+ */
+ @Nullable
+ InteractorType getInteractor();
+
+ /**
+ * Instantiate a Interactor instance here.
+ *
+ * @return The {@link MoviperInteractor} for this view
+ */
+ @NonNull
+ InteractorType createInteractor();
+
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/presenter/routing/MoviperActivityPresenterForRouting.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/presenter/routing/MoviperActivityPresenterForRouting.java
new file mode 100644
index 0000000..1c9d238
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/presenter/routing/MoviperActivityPresenterForRouting.java
@@ -0,0 +1,25 @@
+package com.mateuszkoslacz.moviper.iface.presenter.routing;
+
+import android.app.Activity;
+import android.support.annotation.NonNull;
+
+import com.mateuszkoslacz.moviper.iface.routing.MoviperRouting;
+
+/**
+ * Created by mateuszkoslacz on 08.08.2016.
+ *
+ * MoviperPresenterForRouting version for Activity presenters.
+ * (see {@link MoviperPresenterForRouting})
+ */
+// I prefer readability rather than conventions
+public interface MoviperActivityPresenterForRouting
+ extends MoviperPresenterForRouting {
+
+ /**
+ * Instantiate a Routing instance here.
+ *
+ * @return The {@link MoviperRouting} for this view
+ */
+ @NonNull
+ RoutingType createRouting(@NonNull Activity activity);
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/presenter/routing/MoviperFragmentPresenterForRouting.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/presenter/routing/MoviperFragmentPresenterForRouting.java
new file mode 100644
index 0000000..b88f92b
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/presenter/routing/MoviperFragmentPresenterForRouting.java
@@ -0,0 +1,25 @@
+package com.mateuszkoslacz.moviper.iface.presenter.routing;
+
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+
+import com.mateuszkoslacz.moviper.iface.routing.MoviperRouting;
+
+/**
+ * Created by mateuszkoslacz on 08.08.2016.
+ *
+ * MoviperPresenterForRouting version for Fragment presenters.
+ * (see {@link MoviperPresenterForRouting})
+ */
+// I prefer readability rather than conventions
+public interface MoviperFragmentPresenterForRouting
+ extends MoviperPresenterForRouting {
+
+ /**
+ * Instantiate a Routing instance here.
+ *
+ * @return The {@link MoviperRouting} for this view
+ */
+ @NonNull
+ RoutingType createRouting(@NonNull Fragment fragment);
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/presenter/routing/MoviperPresenterForRouting.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/presenter/routing/MoviperPresenterForRouting.java
new file mode 100644
index 0000000..4b870b6
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/presenter/routing/MoviperPresenterForRouting.java
@@ -0,0 +1,29 @@
+package com.mateuszkoslacz.moviper.iface.presenter.routing;
+
+import android.support.annotation.Nullable;
+
+import com.mateuszkoslacz.moviper.iface.routing.MoviperRouting;
+
+/**
+ * Created by mateuszkoslacz on 08.08.2016.
+ *
+ * It defines presenter interface which Routing ({@link MoviperRouting}) needs to communicate with
+ * Presenter ({@link com.hannesdorfmann.mosby.mvp.MvpBasePresenter}) for proper
+ * execution of given tasks. There are mostly callbacks for creating alarms,
+ * routing's error messages etc.
+ */
+// I prefer readability rather than conventions
+public interface MoviperPresenterForRouting {
+
+ boolean isRoutingAttached();
+
+ /**
+ * Remember to call {@link #isRoutingAttached()} before getting the Routing to avoid
+ * {@link NullPointerException}s.
+ *
+ * @return attached {@link MoviperRouting} subclass instance or
+ * null if it's detached (View got destroyed)
+ */
+ @Nullable
+ RoutingType getRouting();
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/routing/MoviperRouting.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/routing/MoviperRouting.java
new file mode 100644
index 0000000..e9cc249
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/routing/MoviperRouting.java
@@ -0,0 +1,52 @@
+package com.mateuszkoslacz.moviper.iface.routing;
+
+import android.app.Activity;
+import android.support.annotation.Nullable;
+
+import com.mateuszkoslacz.moviper.iface.presenter.routing.MoviperPresenterForRouting;
+
+/**
+ * Created by mateuszkoslacz on 08.08.2016.
+ *
+ * It's responsible for performing all platform-specific tasks in behalf of presenter, ie. starting
+ * a new Activity, a new Service, scheduling alarms etc.
+ *
+ * It's also responsible of UI changes outside of given view, ie. Fragment presenter uses this
+ * routing for switching Fragments in parent Activity.
+ *
+ * In complex use cases you will probably want to include here separate classes for handling alarms
+ * scheduling, Services creating etc. for better separation of concepts.
+ *
+ * If you are looking for solution providing Android Views to use in Android Transaction with shared
+ * views, see {@link MoviperViewHelperRouting}
+ */
+// I prefer readability rather than conventions
+public interface MoviperRouting {
+
+ /**
+ * Remember to call {@link #isActivityAttached()} before getting the Activity to avoid
+ * {@link NullPointerException}s.
+ *
+ * @return attached Activity instance or null if it's detached (View got destroyed)
+ */
+ @Nullable
+ Activity getActivity();
+
+ boolean isActivityAttached();
+
+ /**
+ * Remember to call {@link #isPresenterAttached()} ()} before getting the Presenter to avoid
+ * {@link NullPointerException}s.
+ *
+ * @return attached Moviper {@link com.hannesdorfmann.mosby.mvp.MvpBasePresenter} subclass
+ * instance or null if it's detached (View got destroyed)
+ */
+ @Nullable
+ PresenterType getPresenter();
+
+ boolean isPresenterAttached();
+
+ void attachPresenter(PresenterType presenter);
+
+ void detachPresenter();
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/routing/MoviperViewHelperRouting.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/routing/MoviperViewHelperRouting.java
new file mode 100644
index 0000000..f80511d
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/routing/MoviperViewHelperRouting.java
@@ -0,0 +1,32 @@
+package com.mateuszkoslacz.moviper.iface.routing;
+
+import android.support.annotation.Nullable;
+
+import com.mateuszkoslacz.moviper.iface.presenter.routing.MoviperPresenterForRouting;
+import com.mateuszkoslacz.moviper.iface.viewhelper.MoviperViewHelper;
+
+/**
+ * Created by mateuszkoslacz on 08.08.2016.
+ *
+ * It's a Routing ({@link MoviperRouting}) that also provides a ViewHelper
+ * (see {@link MoviperViewHelper}), which is responsible for providing
+ * Android Views (ImageView, TextView etc.) to allow performing Android Transitions with shared Views.
+ *
+ */
+public interface MoviperViewHelperRouting
+
+ extends MoviperRouting {
+
+ /**
+ * Remember to call {@link #isViewHelperAttached()} before getting the ViewHelper to avoid
+ * {@link NullPointerException}s.
+ *
+ * @return attached {@link MoviperViewHelper} or null if it's detached (View got destroyed)
+ */
+ @Nullable
+ ViewHelperType getViewHelper();
+
+ boolean isViewHelperAttached();
+
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/viewhelper/MoviperViewHelper.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/viewhelper/MoviperViewHelper.java
new file mode 100644
index 0000000..91fe52c
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/iface/viewhelper/MoviperViewHelper.java
@@ -0,0 +1,13 @@
+package com.mateuszkoslacz.moviper.iface.viewhelper;
+
+/**
+ * Created by mateuszkoslacz on 09.08.2016.
+ *
+ * It's used for getting Android Views (ImageView, TextView etc.) for Routing (see
+ * {@link com.mateuszkoslacz.moviper.iface.routing.MoviperRouting}) to allow it perform
+ * Android Transitions with shared Views.
+ *
+ * You should NOT manipulate views given by ViewHelper in any way!
+ */
+public interface MoviperViewHelper {
+}
diff --git a/moviper/src/main/java/com/mateuszkoslacz/moviper/util/WeakReferenceUtils.java b/moviper/src/main/java/com/mateuszkoslacz/moviper/util/WeakReferenceUtils.java
new file mode 100644
index 0000000..52f6b99
--- /dev/null
+++ b/moviper/src/main/java/com/mateuszkoslacz/moviper/util/WeakReferenceUtils.java
@@ -0,0 +1,27 @@
+package com.mateuszkoslacz.moviper.util;
+
+import android.support.annotation.Nullable;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Created by mateuszkoslacz on 21.08.2016.
+ */
+public class WeakReferenceUtils {
+
+ @Nullable
+ public static T get(@Nullable WeakReference weakReferenceToGetFrom) {
+ return weakReferenceToGetFrom == null ? null : weakReferenceToGetFrom.get();
+ }
+
+ public static void detach(@Nullable WeakReference weakReferenceToClear) {
+ if (weakReferenceToClear != null) {
+ weakReferenceToClear.clear();
+ weakReferenceToClear = null;
+ }
+ }
+
+ public static boolean isAttached(@Nullable WeakReference weakReferenceToCheck) {
+ return weakReferenceToCheck != null && weakReferenceToCheck.get() != null;
+ }
+}
diff --git a/moviper/src/main/res/values/strings.xml b/moviper/src/main/res/values/strings.xml
new file mode 100644
index 0000000..5f1c25c
--- /dev/null
+++ b/moviper/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Moviper
+
diff --git a/moviper/src/test/java/com/mateuszkoslacz/moviper/ExampleUnitTest.java b/moviper/src/test/java/com/mateuszkoslacz/moviper/ExampleUnitTest.java
new file mode 100644
index 0000000..cfde71b
--- /dev/null
+++ b/moviper/src/test/java/com/mateuszkoslacz/moviper/ExampleUnitTest.java
@@ -0,0 +1,15 @@
+package com.mateuszkoslacz.moviper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/sample/.gitignore b/sample/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/sample/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/sample/build.gradle b/sample/build.gradle
new file mode 100644
index 0000000..9c16c11
--- /dev/null
+++ b/sample/build.gradle
@@ -0,0 +1,65 @@
+apply plugin: 'com.android.application'
+apply plugin: 'me.tatarka.retrolambda'
+apply plugin: 'android-apt'
+
+
+android {
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ compileSdkVersion 24
+ buildToolsVersion "24.0.1"
+
+ defaultConfig {
+ applicationId "com.mateuszkoslacz.moviper.sample"
+ minSdkVersion 21
+ targetSdkVersion 24
+ versionCode 1
+ versionName "1.0"
+ vectorDrawables.useSupportLibrary = true
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ fileTree(dir: 'proguard-rules', include: ['*.pro']).each { File file ->
+ proguardFile file
+ }
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ debug {
+ minifyEnabled false
+ fileTree(dir: 'proguard-rules', include: ['*.pro']).each { File file ->
+ proguardFile file
+ }
+ 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:24.2.0'
+ compile 'com.android.support:design:24.2.0'
+ compile 'com.android.support:support-v4:24.2.0'
+
+ compile 'com.google.android.gms:play-services-location:9.4.0'
+
+ debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
+ releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
+ testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
+
+ compile 'com.jakewharton:butterknife:8.2.1'
+
+ retrolambdaConfig 'net.orfjackal.retrolambda:retrolambda:2.3.0'
+
+ compile 'com.parse:parse-android:1.13.1'
+
+ apt 'com.jakewharton:butterknife-compiler:8.2.1'
+
+ compile project(path: ':moviper')
+}
diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro
new file mode 100644
index 0000000..c9d9908
--- /dev/null
+++ b/sample/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/mateuszkoslacz/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 *;
+#}
diff --git a/sample/proguard-rules/proguard-butterknife-7.pro b/sample/proguard-rules/proguard-butterknife-7.pro
new file mode 100644
index 0000000..a04d9bc
--- /dev/null
+++ b/sample/proguard-rules/proguard-butterknife-7.pro
@@ -0,0 +1,13 @@
+# ButterKnife 7
+
+-keep class butterknife.** { *; }
+-dontwarn butterknife.internal.**
+-keep class **$$ViewBinder { *; }
+
+-keepclasseswithmembernames class * {
+ @butterknife.* ;
+}
+
+-keepclasseswithmembernames class * {
+ @butterknife.* ;
+}
\ No newline at end of file
diff --git a/sample/proguard-rules/proguard-support-design.pro b/sample/proguard-rules/proguard-support-design.pro
new file mode 100644
index 0000000..8883f26
--- /dev/null
+++ b/sample/proguard-rules/proguard-support-design.pro
@@ -0,0 +1,4 @@
+-dontwarn android.support.design.**
+-keep class android.support.design.** { *; }
+-keep interface android.support.design.** { *; }
+-keep public class android.support.design.R$* { *; }
\ No newline at end of file
diff --git a/sample/proguard-rules/proguard-support-v7-appcompat.pro b/sample/proguard-rules/proguard-support-v7-appcompat.pro
new file mode 100644
index 0000000..65068d0
--- /dev/null
+++ b/sample/proguard-rules/proguard-support-v7-appcompat.pro
@@ -0,0 +1,7 @@
+-keep public class android.support.v7.widget.** { *; }
+-keep public class android.support.v7.internal.widget.** { *; }
+-keep public class android.support.v7.internal.view.menu.** { *; }
+
+-keep public class * extends android.support.v4.view.ActionProvider {
+ public (android.content.Context);
+}
diff --git a/sample/proguard-rules/proguard-support-v7-cardview.pro b/sample/proguard-rules/proguard-support-v7-cardview.pro
new file mode 100644
index 0000000..e605293
--- /dev/null
+++ b/sample/proguard-rules/proguard-support-v7-cardview.pro
@@ -0,0 +1,2 @@
+# http://stackoverflow.com/questions/29679177/cardview-shadow-not-appearing-in-lollipop-after-obfuscate-with-proguard/29698051
+-keep class android.support.v7.widget.RoundRectDrawable { *; }
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1c970e1
--- /dev/null
+++ b/sample/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/SampleApp.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/SampleApp.java
new file mode 100644
index 0000000..fc76229
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/SampleApp.java
@@ -0,0 +1,19 @@
+package com.mateuszkoslacz.moviper.sample;
+
+import android.app.Application;
+
+import com.mateuszkoslacz.moviper.sample.data.parse.util.ParseInitializator;
+import com.squareup.leakcanary.LeakCanary;
+
+/**
+ * Created by mateuszkoslacz on 12.08.2016.
+ */
+public class SampleApp extends Application {
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ ParseInitializator.init(this);
+ LeakCanary.install(this); // to ensure that Moviper does not introduce any leaks
+ }
+}
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/config/ParseConsts.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/config/ParseConsts.java
new file mode 100644
index 0000000..723386c
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/config/ParseConsts.java
@@ -0,0 +1,16 @@
+package com.mateuszkoslacz.moviper.sample.config;
+
+/**
+ * Created by mateuszkoslacz on 12.08.2016.
+ *
+ * If you want to see how logging in and stuff works in action you have to run your own
+ * parse local instance described here and change constants in
+ * this class to point there. This sample should be probably migrated to some mocked up db-based
+ * local logging in, but actually it's not needed to understand concepts of viper architecture.
+ */
+public class ParseConsts {
+
+ public static final String SERVER_URL = "http://your.parse.server.com";
+ public static final String PARSE_PATH = "/parse";
+ public static final String APP_ID = "myAppId";
+}
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/data/bundle/LocationPoint.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/data/bundle/LocationPoint.java
new file mode 100644
index 0000000..f4bf33a
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/data/bundle/LocationPoint.java
@@ -0,0 +1,58 @@
+package com.mateuszkoslacz.moviper.sample.data.bundle;
+
+/**
+ * Created by mateuszkoslacz on 12.08.2016.
+ *
+ * To be used in the business logic layer to separate it from view and data layer specific objects.
+ */
+public class LocationPoint {
+
+ private final double latitude;
+ private final double longitude;
+ private final boolean unknown;
+
+ /**
+ * A null object constructor.
+ */
+ public LocationPoint() {
+ unknown = true;
+ latitude = Double.MIN_VALUE;
+ longitude = Double.MIN_VALUE;
+ }
+
+ public LocationPoint(double latitudeDegrees, double longitudeDegrees) {
+ unknown = false;
+ this.latitude = latitudeDegrees;
+ this.longitude = longitudeDegrees;
+ }
+
+ public boolean isUnknown() {
+ return unknown;
+ }
+
+ /**
+ * It will throw a RuntimeException if location is unknown. Always check
+ * LocationPoint#isUnknown() before invoking this method.
+ *
+ * @return
+ */
+ public double getLatitude() {
+ if (isUnknown()) throw new RuntimeException("cannot get latitude, location is unknown! " +
+ "Make sure that you have checked LocationPoint#isUnknown() " +
+ "before trying to get latitude");
+ return latitude;
+ }
+
+ /**
+ * It will throw a RuntimeException if location is unknown. Always check
+ * LocationPoint#isUnknown() before invoking this method.
+ *
+ * @return
+ */
+ public double getLongitude() {
+ if (isUnknown()) throw new RuntimeException("cannot get longitude, location is unknown! " +
+ "Make sure that you have checked LocationPoint#isUnknown() " +
+ "before trying to get longitude");
+ return longitude;
+ }
+}
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/data/bundle/RegisterBundle.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/data/bundle/RegisterBundle.java
new file mode 100644
index 0000000..0db5000
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/data/bundle/RegisterBundle.java
@@ -0,0 +1,69 @@
+package com.mateuszkoslacz.moviper.sample.data.bundle;
+
+
+import com.mateuszkoslacz.moviper.sample.util.StringUtils;
+
+/**
+ * Created by mateuszkoslacz on 12.08.2016.
+ *
+ * Created to avoid passing 3 arguments to register methods.
+ */
+public class RegisterBundle {
+
+ private final String login;
+ private final String email;
+ private final String password;
+
+ private RegisterBundle(Builder builder) {
+ login = builder.login;
+ email = builder.email;
+ password = builder.password;
+ }
+
+ public String getLogin() {
+ return login;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public boolean isComplete() {
+ return !StringUtils.isNullOrEmpty(login) &&
+ !StringUtils.isNullOrEmpty(email) &&
+ !StringUtils.isNullOrEmpty(password);
+ }
+
+ public static final class Builder {
+ private String login;
+ private String email;
+ private String password;
+
+ public Builder() {
+ }
+
+ public Builder withLogin(String login) {
+ this.login = login;
+ return this;
+ }
+
+ public Builder withEmail(String email) {
+ this.email = email;
+ return this;
+ }
+
+ public Builder withPassword(String password) {
+ this.password = password;
+ return this;
+ }
+
+ public RegisterBundle build() {
+ return new RegisterBundle(this);
+ }
+ }
+
+}
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/data/parse/User.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/data/parse/User.java
new file mode 100644
index 0000000..8a6d027
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/data/parse/User.java
@@ -0,0 +1,77 @@
+package com.mateuszkoslacz.moviper.sample.data.parse;
+
+import com.mateuszkoslacz.moviper.sample.util.StringUtils;
+import com.parse.ParseClassName;
+import com.parse.ParseGeoPoint;
+import com.parse.ParseUser;
+
+/**
+ * Created by mateuszkoslacz on 11.08.2016.
+ */
+@ParseClassName(User.PARSE_CLASS_NAME)
+public class User extends ParseUser {
+
+ public static final String PARSE_CLASS_NAME = "_User";
+
+ public static final String C_LOCALIZATION_PGP = "currentPosition";
+
+ // just for reference
+ private static ParseGeoPoint localization;
+
+ public ParseGeoPoint getLocalization() {
+ return getParseGeoPoint(C_LOCALIZATION_PGP);
+ }
+
+ public void setLocalization(ParseGeoPoint localization) {
+ put(C_LOCALIZATION_PGP, localization);
+ }
+
+ public static class Builder {
+ private String login;
+ private String email;
+ private String password;
+ private ParseGeoPoint localization;
+
+
+ public Builder withLogin(String login) {
+ this.login = login;
+ return this;
+ }
+
+ public Builder withEmail(String email) {
+ this.email = email;
+ return this;
+ }
+
+ public Builder withPassword(String password) {
+ this.password = password;
+ return this;
+ }
+
+ public Builder withLocalization(ParseGeoPoint localization) {
+ this.localization = localization;
+ return this;
+ }
+
+ public User build() {
+ interruptEarlyIfNoEssentialDataProvided();
+ User user = ParseUser.create(User.class);
+ user.setUsername(login);
+ user.setEmail(email);
+ user.setPassword(password);
+ if (localization != null) user.setLocalization(localization);
+ return user;
+ }
+
+ private void interruptEarlyIfNoEssentialDataProvided() {
+ if (StringUtils.isNullOrEmpty(login) ||
+ StringUtils.isNullOrEmpty(password) ||
+ StringUtils.isNullOrEmpty(email)) {
+ throw new RuntimeException(
+ String.format("No essential data provided! " +
+ "login: %s, password: %s, email: %s",
+ login, password, email));
+ }
+ }
+ }
+}
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/data/parse/util/ParseInitializator.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/data/parse/util/ParseInitializator.java
new file mode 100644
index 0000000..f5c15e8
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/data/parse/util/ParseInitializator.java
@@ -0,0 +1,33 @@
+package com.mateuszkoslacz.moviper.sample.data.parse.util;
+
+import android.app.Application;
+
+import com.mateuszkoslacz.moviper.sample.config.ParseConsts;
+import com.mateuszkoslacz.moviper.sample.data.parse.User;
+import com.parse.Parse;
+import com.parse.ParseObject;
+
+/**
+ * Created by mateuszkoslacz on 12.08.2016.
+ */
+public class ParseInitializator {
+
+ public static void init(Application app) {
+ registerSubclasses();
+ initializeParse(app);
+ }
+
+ /**
+ * Remember to register all of your subclasses here.
+ */
+ private static void registerSubclasses() {
+ ParseObject.registerSubclass(User.class);
+ }
+
+ private static void initializeParse(Application app) {
+ Parse.initialize(new Parse.Configuration.Builder(app)
+ .applicationId(ParseConsts.APP_ID)
+ .server(ParseConsts.SERVER_URL + ParseConsts.PARSE_PATH)
+ .build());
+ }
+}
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/LocationUtils.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/LocationUtils.java
new file mode 100644
index 0000000..4fee2f0
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/LocationUtils.java
@@ -0,0 +1,24 @@
+package com.mateuszkoslacz.moviper.sample.util;
+
+import com.mateuszkoslacz.moviper.sample.data.bundle.LocationPoint;
+
+/**
+ * Created by mateuszkoslacz on 14.08.2016.
+ */
+public interface LocationUtils {
+
+ int PERMISSION_REQUEST_ACCES_FINE_LOCATION = 123;
+
+ void subscribeToGetLocalization();
+
+ // stuff caused by Android need of registering permission result listener being
+ // a fragment or activity
+ void onRequestLocalizationPermissionsResult(boolean granted);
+
+ interface LocationRequestsCallback {
+
+ void onLocalizationAquired(LocationPoint location);
+
+ void onLocalizationAquireFailed(String message);
+ }
+}
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/LocationUtilsImpl.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/LocationUtilsImpl.java
new file mode 100644
index 0000000..803d29d
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/LocationUtilsImpl.java
@@ -0,0 +1,138 @@
+package com.mateuszkoslacz.moviper.sample.util;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.location.Location;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.ActivityCompat;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.location.LocationServices;
+import com.mateuszkoslacz.moviper.sample.data.bundle.LocationPoint;
+
+/**
+ * Created by mateuszkoslacz on 14.08.2016.
+ */
+public class LocationUtilsImpl
+ implements
+ LocationUtils,
+ GoogleApiClient.ConnectionCallbacks,
+ GoogleApiClient.OnConnectionFailedListener {
+
+ private Activity mActivity;
+ private GoogleApiClient mGoogleApiClient;
+ private LocationRequestsCallback mCallback;
+
+ public LocationUtilsImpl(Activity mActivity, LocationRequestsCallback mCallback) {
+ this.mActivity = mActivity;
+ this.mCallback = mCallback;
+ }
+
+ @Override
+ public void subscribeToGetLocalization() {
+ // TODO migrate to rx approach
+ if (isActivityAttached()) {
+ if (mGoogleApiClient == null) {
+ mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
+ .addConnectionCallbacks(this)
+ .addOnConnectionFailedListener(this)
+ .addApi(LocationServices.API)
+ .build();
+ }
+ mGoogleApiClient.connect();
+ }
+ // will call onConnected, onConnectionSuspended, or onConnectionFailed
+ }
+
+ @Override
+ public void onRequestLocalizationPermissionsResult(boolean granted) {
+ if (isCallbackAttached()) {
+ if (granted) getCallback().onLocalizationAquired(getLastLocation());
+ else getCallback().onLocalizationAquireFailed("We need the localization permission!");
+ }
+ }
+
+ @Override
+ public void onConnected(@Nullable Bundle bundle) {
+ if (weHavePermissionToLocationAcces()) {
+ if (isCallbackAttached()) getCallback().onLocalizationAquired(getLastLocation());
+// mGoogleApiClient.disconnect(); // TODO: 15.08.2016 should we uncomment this?
+ } else {
+ if (weShouldShowWhyWeNeedUserLocation()) {
+ // Show an expanation to the user *asynchronously* -- don't block
+ // this thread waiting for the user's response! After the user
+ // sees the explanation, try again to request the permission.
+ }
+ // else // wrap it onto else block if you are showing the explanation
+ requestLocationPermission();
+ }
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+ //whatever
+ }
+
+ @Override
+ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
+ if (isCallbackAttached())
+ getCallback().onLocalizationAquireFailed(
+ StringUtils.isNullOrEmpty(connectionResult.getErrorMessage()) ?
+ String.format("Google Play Services Error: %d. Make sure that you have " +
+ "Google Play Services installed and up-to-date.",
+ connectionResult.getErrorCode()) :
+ connectionResult.getErrorMessage());
+ }
+
+ private boolean weHavePermissionToLocationAcces() {
+ return ActivityCompat.checkSelfPermission(getActivity(),
+ Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private void requestLocationPermission() {
+ //TODO for now we use activity to handle permission, in future use maybe
+ // https://github.com/bignerdranch/permission-manager to move the logic to routing
+ ActivityCompat.requestPermissions(getActivity(),
+ new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
+ PERMISSION_REQUEST_ACCES_FINE_LOCATION);
+ }
+
+ private boolean weShouldShowWhyWeNeedUserLocation() {
+ return ActivityCompat.shouldShowRequestPermissionRationale(
+ getActivity(), Manifest.permission.ACCESS_FINE_LOCATION);
+ }
+
+ @NonNull
+ private LocationPoint getLastLocation() {
+ //noinspection MissingPermission to be checked in upper level
+ Location lastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
+ LocationPoint location;
+ if (lastLocation != null) {
+ location = new LocationPoint(lastLocation.getLatitude(), lastLocation.getLongitude());
+ } else {
+ location = new LocationPoint();
+ }
+ return location;
+ }
+
+ public LocationRequestsCallback getCallback() {
+ return mCallback;
+ }
+
+ private boolean isCallbackAttached() {
+ return mCallback != null;
+ }
+
+ private Activity getActivity() {
+ return mActivity;
+ }
+
+ private boolean isActivityAttached() {
+ return mActivity != null;
+ }
+
+}
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/StringUtils.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/StringUtils.java
new file mode 100644
index 0000000..4a79009
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/StringUtils.java
@@ -0,0 +1,13 @@
+package com.mateuszkoslacz.moviper.sample.util;
+
+/**
+ * Created by mateuszkoslacz on 12.08.2016.
+ *
+ * Created instead of Android TextUtils class to have presentation-layer Java-only
+ */
+public class StringUtils {
+
+ public static boolean isNullOrEmpty(String string) {
+ return string == null || string.trim().isEmpty();
+ }
+}
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/UiUtils.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/UiUtils.java
new file mode 100644
index 0000000..935fe23
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/UiUtils.java
@@ -0,0 +1,21 @@
+package com.mateuszkoslacz.moviper.sample.util;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+
+/**
+ * Created by mateuszkoslacz on 21.08.2016.
+ */
+public class UiUtils {
+
+ public static void hideSoftKeyboard(Activity activity) {
+ View view = activity.getCurrentFocus();
+ if (view != null) {
+ InputMethodManager imm =
+ (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ }
+ }
+}
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/mapper/LocationPointToParseGeoPointMapper.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/mapper/LocationPointToParseGeoPointMapper.java
new file mode 100644
index 0000000..3a4bc83
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/mapper/LocationPointToParseGeoPointMapper.java
@@ -0,0 +1,15 @@
+package com.mateuszkoslacz.moviper.sample.util.mapper;
+
+import com.mateuszkoslacz.moviper.sample.data.bundle.LocationPoint;
+import com.mateuszkoslacz.moviper.sample.util.mapper.iface.Mapper;
+import com.parse.ParseGeoPoint;
+
+/**
+ * Created by mateuszkoslacz on 15.08.2016.
+ */
+public class LocationPointToParseGeoPointMapper implements Mapper {
+ @Override
+ public ParseGeoPoint map(LocationPoint locationPoint) {
+ return new ParseGeoPoint(locationPoint.getLatitude(), locationPoint.getLongitude());
+ }
+}
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/mapper/iface/Mapper.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/mapper/iface/Mapper.java
new file mode 100644
index 0000000..ea0ee27
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/util/mapper/iface/Mapper.java
@@ -0,0 +1,8 @@
+package com.mateuszkoslacz.moviper.sample.util.mapper.iface;
+
+/**
+ * Created by mateuszkoslacz on 13.08.2016.
+ */
+public interface Mapper {
+ To map(From from);
+}
\ No newline at end of file
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/viper/contract/AuthorizationContract.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/viper/contract/AuthorizationContract.java
new file mode 100644
index 0000000..1e153c0
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/viper/contract/AuthorizationContract.java
@@ -0,0 +1,24 @@
+package com.mateuszkoslacz.moviper.sample.viper.contract;
+
+import com.hannesdorfmann.mosby.mvp.MvpPresenter;
+import com.hannesdorfmann.mosby.mvp.MvpView;
+
+/**
+ * Created by mateuszkoslacz on 10.08.2016.
+ *
+ * It has less interfaces because it's a pure MVP contract.
+ */
+public interface AuthorizationContract {
+
+ interface Presenter extends MvpPresenter {
+
+ void onUiCreated();
+ }
+
+ interface View extends MvpView {
+
+ void showLoginFragment();
+
+ void showRegisterFragment();
+ }
+}
diff --git a/sample/src/main/java/com/mateuszkoslacz/moviper/sample/viper/contract/LoginContract.java b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/viper/contract/LoginContract.java
new file mode 100644
index 0000000..610c5a0
--- /dev/null
+++ b/sample/src/main/java/com/mateuszkoslacz/moviper/sample/viper/contract/LoginContract.java
@@ -0,0 +1,74 @@
+package com.mateuszkoslacz.moviper.sample.viper.contract;
+
+import com.hannesdorfmann.mosby.mvp.MvpPresenter;
+import com.hannesdorfmann.mosby.mvp.lce.MvpLceView;
+import com.mateuszkoslacz.moviper.iface.interactor.MoviperInteractor;
+import com.mateuszkoslacz.moviper.iface.presenter.interactor.MoviperPresenterForInteractor;
+import com.mateuszkoslacz.moviper.iface.presenter.routing.MoviperFragmentPresenterForRouting;
+import com.mateuszkoslacz.moviper.iface.routing.MoviperViewHelperRouting;
+import com.mateuszkoslacz.moviper.iface.viewhelper.MoviperViewHelper;
+
+/**
+ * Created by mateuszkoslacz on 10.08.2016.
+ *
+ * See {@link MainContract} to check out the freshly generated contract with usage description.
+ */
+public interface LoginContract {
+
+ interface Presenter extends MvpPresenter {
+
+ void onLoginClicked(String username, String password);
+
+ void onShowRegisterFragmentClicked();
+ }
+
+ interface View extends MvpLceView