├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── generators └── app │ ├── index.js │ ├── sync_template_java.js │ ├── sync_template_kotlin.js │ └── templates │ ├── template-java │ ├── .travis.yml │ ├── README.md │ ├── app │ │ ├── build.gradle │ │ ├── dependencies.gradle │ │ ├── gitignore │ │ ├── lint.xml │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── androidTest │ │ │ ├── assets │ │ │ │ ├── getPokemon.json │ │ │ │ └── getPokemonDetails.json │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── mvpstarter │ │ │ │ └── sample │ │ │ │ ├── DetailActivityTest.java │ │ │ │ ├── MainActivityTest.java │ │ │ │ ├── runner │ │ │ │ ├── RxAndroidJUnitRunner.java │ │ │ │ ├── TestRunner.java │ │ │ │ └── UnlockDeviceAndroidJUnitRunner.java │ │ │ │ └── util │ │ │ │ ├── ErrorTestUtil.java │ │ │ │ ├── RxIdlingExecutionHook.java │ │ │ │ └── RxIdlingResource.java │ │ │ ├── commonTest │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── mvpstarter │ │ │ │ └── sample │ │ │ │ └── common │ │ │ │ ├── TestComponentRule.java │ │ │ │ ├── TestDataFactory.java │ │ │ │ └── injection │ │ │ │ ├── component │ │ │ │ └── TestComponent.java │ │ │ │ └── module │ │ │ │ └── ApplicationTestModule.java │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── res │ │ │ │ └── values │ │ │ │ └── google_maps_api.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── io │ │ │ │ │ └── mvpstarter │ │ │ │ │ └── sample │ │ │ │ │ ├── Constants.java │ │ │ │ │ ├── MvpStarterApplication.java │ │ │ │ │ ├── data │ │ │ │ │ ├── DataManager.java │ │ │ │ │ ├── local │ │ │ │ │ │ ├── DbManager.java │ │ │ │ │ │ └── PreferencesHelper.java │ │ │ │ │ ├── model │ │ │ │ │ │ └── response │ │ │ │ │ │ │ ├── NamedResource.java │ │ │ │ │ │ │ ├── Pokemon.java │ │ │ │ │ │ │ ├── PokemonListResponse.java │ │ │ │ │ │ │ ├── Sprites.java │ │ │ │ │ │ │ └── Statistic.java │ │ │ │ │ └── remote │ │ │ │ │ │ └── PokemonService.java │ │ │ │ │ ├── features │ │ │ │ │ ├── base │ │ │ │ │ │ ├── BaseActivity.java │ │ │ │ │ │ ├── BaseFragment.java │ │ │ │ │ │ ├── BasePresenter.java │ │ │ │ │ │ ├── MvpView.java │ │ │ │ │ │ └── Presenter.java │ │ │ │ │ ├── common │ │ │ │ │ │ └── ErrorView.java │ │ │ │ │ ├── detail │ │ │ │ │ │ ├── DetailActivity.java │ │ │ │ │ │ ├── DetailMvpView.java │ │ │ │ │ │ ├── DetailPresenter.java │ │ │ │ │ │ ├── MapsSampleActivity.java │ │ │ │ │ │ └── widget │ │ │ │ │ │ │ └── StatisticView.java │ │ │ │ │ └── main │ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ │ ├── MainMvpView.java │ │ │ │ │ │ ├── MainPresenter.java │ │ │ │ │ │ └── PokemonAdapter.java │ │ │ │ │ ├── injection │ │ │ │ │ ├── ActivityContext.java │ │ │ │ │ ├── ApplicationContext.java │ │ │ │ │ ├── ConfigPersistent.java │ │ │ │ │ ├── PerActivity.java │ │ │ │ │ ├── PerFragment.java │ │ │ │ │ ├── component │ │ │ │ │ │ ├── ActivityComponent.java │ │ │ │ │ │ ├── AppComponent.java │ │ │ │ │ │ ├── ConfigPersistentComponent.java │ │ │ │ │ │ └── FragmentComponent.java │ │ │ │ │ └── module │ │ │ │ │ │ ├── ActivityModule.java │ │ │ │ │ │ ├── ApiModule.java │ │ │ │ │ │ ├── AppModule.java │ │ │ │ │ │ ├── FragmentModule.java │ │ │ │ │ │ └── NetworkModule.java │ │ │ │ │ └── util │ │ │ │ │ ├── DialogFactory.java │ │ │ │ │ ├── NetworkUtil.java │ │ │ │ │ ├── ViewUtil.java │ │ │ │ │ └── rx │ │ │ │ │ └── scheduler │ │ │ │ │ ├── BaseScheduler.java │ │ │ │ │ ├── ComputationMainScheduler.java │ │ │ │ │ ├── IoMainScheduler.java │ │ │ │ │ ├── NewThreadMainScheduler.java │ │ │ │ │ ├── SchedulerUtils.java │ │ │ │ │ ├── SingleMainScheduler.java │ │ │ │ │ └── TrampolineMainScheduler.java │ │ │ └── res │ │ │ │ ├── layout │ │ │ │ ├── activity_detail.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── activity_maps_sample.xml │ │ │ │ ├── item_pokemon.xml │ │ │ │ ├── view_error.xml │ │ │ │ └── view_statistic.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── release │ │ │ └── res │ │ │ │ └── values │ │ │ │ └── google_maps_api.xml │ │ │ └── test │ │ │ ├── java │ │ │ └── io │ │ │ │ └── mvpstarter │ │ │ │ └── sample │ │ │ │ ├── DataManagerTest.java │ │ │ │ ├── DetailPresenterTest.java │ │ │ │ ├── MainPresenterTest.java │ │ │ │ └── util │ │ │ │ ├── DefaultConfig.java │ │ │ │ └── RxSchedulersOverrideRule.java │ │ │ └── resources │ │ │ └── mockito-extensions │ │ │ └── org.mockito.plugins.MockMaker │ ├── build.gradle │ ├── config │ │ └── quality │ │ │ ├── checkstyle │ │ │ └── checkstyle-config.xml │ │ │ ├── findbugs │ │ │ └── android-exclude-filter.xml │ │ │ ├── pmd │ │ │ └── pmd-ruleset.xml │ │ │ └── quality.gradle │ ├── gitignore │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle │ └── template-kotlin │ ├── .travis.yml │ ├── README.md │ ├── app │ ├── build.gradle │ ├── dependencies.gradle │ ├── gitignore │ ├── lint.xml │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ ├── assets │ │ │ ├── getPokemon.json │ │ │ └── getPokemonDetails.json │ │ └── java │ │ │ └── io │ │ │ └── mvpstarter │ │ │ └── sample │ │ │ ├── DetailActivityTest.kt │ │ │ ├── MainActivityTest.kt │ │ │ ├── runner │ │ │ ├── RxAndroidJUnitRunner.kt │ │ │ ├── TestRunner.kt │ │ │ └── UnlockDeviceAndroidJUnitRunner.kt │ │ │ └── util │ │ │ ├── ErrorTestUtil.kt │ │ │ ├── RxIdlingExecutionHook.kt │ │ │ └── RxIdlingResource.kt │ │ ├── commonTest │ │ └── java │ │ │ └── io │ │ │ └── mvpstarter │ │ │ └── sample │ │ │ └── common │ │ │ ├── TestComponentRule.kt │ │ │ ├── TestDataFactory.kt │ │ │ └── injection │ │ │ ├── component │ │ │ └── TestComponent.kt │ │ │ └── module │ │ │ └── ApplicationTestModule.kt │ │ ├── debug │ │ ├── AndroidManifest.xml │ │ └── res │ │ │ └── values │ │ │ └── google_maps_api.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── io │ │ │ │ └── mvpstarter │ │ │ │ └── sample │ │ │ │ ├── MvpStarterApplication.kt │ │ │ │ ├── data │ │ │ │ ├── DataManager.kt │ │ │ │ ├── local │ │ │ │ │ └── PreferencesHelper.kt │ │ │ │ ├── model │ │ │ │ │ ├── NamedResource.kt │ │ │ │ │ ├── Pokemon.kt │ │ │ │ │ ├── PokemonListResponse.kt │ │ │ │ │ ├── Sprites.kt │ │ │ │ │ └── Statistic.kt │ │ │ │ └── remote │ │ │ │ │ └── PokemonApi.kt │ │ │ │ ├── features │ │ │ │ ├── base │ │ │ │ │ ├── BaseActivity.kt │ │ │ │ │ ├── BaseFragment.kt │ │ │ │ │ ├── BasePresenter.kt │ │ │ │ │ ├── MvpView.kt │ │ │ │ │ └── Presenter.kt │ │ │ │ ├── common │ │ │ │ │ └── ErrorView.kt │ │ │ │ ├── detail │ │ │ │ │ ├── DetailActivity.kt │ │ │ │ │ ├── DetailMvpView.kt │ │ │ │ │ ├── DetailPresenter.kt │ │ │ │ │ ├── MapsSampleActivity.kt │ │ │ │ │ └── widget │ │ │ │ │ │ └── StatisticView.kt │ │ │ │ └── main │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ ├── MainMvpView.kt │ │ │ │ │ ├── MainPresenter.kt │ │ │ │ │ └── PokemonAdapter.kt │ │ │ │ ├── injection │ │ │ │ ├── ActivityContext.kt │ │ │ │ ├── ApplicationContext.kt │ │ │ │ ├── ConfigPersistent.kt │ │ │ │ ├── PerActivity.kt │ │ │ │ ├── PerFragment.kt │ │ │ │ ├── component │ │ │ │ │ ├── ActivityComponent.kt │ │ │ │ │ ├── AppComponent.kt │ │ │ │ │ ├── ConfigPersistentComponent.kt │ │ │ │ │ └── FragmentComponent.kt │ │ │ │ └── module │ │ │ │ │ ├── ActivityModule.kt │ │ │ │ │ ├── ApiModule.kt │ │ │ │ │ ├── AppModule.kt │ │ │ │ │ ├── FragmentModule.kt │ │ │ │ │ └── NetworkModule.kt │ │ │ │ └── util │ │ │ │ ├── AndroidUtils.kt │ │ │ │ ├── NetworkUtil.kt │ │ │ │ ├── ViewUtil.kt │ │ │ │ └── rx │ │ │ │ └── scheduler │ │ │ │ ├── BaseScheduler.kt │ │ │ │ ├── ComputationMainScheduler.kt │ │ │ │ ├── IoMainScheduler.kt │ │ │ │ ├── NewThreadMainScheduler.kt │ │ │ │ ├── SchedulerUtils.kt │ │ │ │ ├── SingleMainScheduler.kt │ │ │ │ └── TrampolineMainScheduler.kt │ │ └── res │ │ │ ├── layout │ │ │ ├── activity_detail.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_maps_sample.xml │ │ │ ├── item_pokemon.xml │ │ │ ├── view_error.xml │ │ │ └── view_statistic.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── release │ │ └── res │ │ │ └── values │ │ │ └── google_maps_api.xml │ │ └── test │ │ ├── java │ │ └── io │ │ │ └── mvpstarter │ │ │ └── sample │ │ │ ├── DataManagerTest.kt │ │ │ ├── DetailPresenterTest.kt │ │ │ ├── MainPresenterTest.kt │ │ │ └── util │ │ │ ├── DefaultConfig.kt │ │ │ └── RxSchedulersOverrideRule.kt │ │ └── resources │ │ └── mockito-extensions │ │ └── org.mockito.plugins.MockMaker │ ├── build.gradle │ ├── gitignore │ ├── gradle.properties │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── gulpfile.js ├── package-lock.json ├── package.json └── test ├── app.js └── app_kotlin.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - v7 4 | - v6 -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016 Ravindra Kumar (https://github.com/ravidsrk/) 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /generators/app/sync_template_java.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const rimraf = require('rimraf'); 5 | const mv = require('mv'); 6 | const mkdirp = require('mkdirp'); 7 | const clone = require('nodegit').Clone; 8 | const replace = require('replace'); 9 | const ncp = require('ncp').ncp; 10 | 11 | // Clone a given repository into the `./tmp` folder. 12 | 13 | rimraf.sync(path.join(__dirname, '/templates')); 14 | rimraf.sync(path.join(__dirname, '/tmp')); 15 | mkdirp('./templates'); 16 | 17 | clone('https://github.com/ravidsrk/android-starter.git', './tmp') 18 | .then(function (repo) { 19 | checkOutAndCopy(repo, 'develop'); 20 | }) 21 | .catch(function (err) { 22 | console.log(err); 23 | }); 24 | 25 | function checkOutAndCopy(repo, name) { 26 | repo.getBranch('refs/remotes/origin/' + name) 27 | .then(function (reference) { 28 | console.log('Checking out branch ' + name); 29 | return repo.checkoutRef(reference); 30 | }) 31 | .then(function () { 32 | replace({ 33 | regex: 'io.mvpstarter.sample', 34 | replacement: '<%= appPackage %>', 35 | paths: ['./tmp/app'], 36 | recursive: true, 37 | silent: true 38 | }); 39 | 40 | mv('./tmp/.gitignore', './tmp/gitignore', function (err) { 41 | if (err) { 42 | console.log(err); 43 | } 44 | console.log('Renamed root folder .gitignore'); 45 | }); 46 | 47 | mv('./tmp/app/.gitignore', './tmp/app/gitignore', function (err) { 48 | if (err) { 49 | console.log(err); 50 | } 51 | console.log('Renamed app folder .gitignore'); 52 | }); 53 | 54 | rimraf.sync(path.join(__dirname, '/tmp/.git')); 55 | 56 | console.log('Copying files to ./templates/template-java'); 57 | ncp.limit = 1600; 58 | ncp('./tmp', './templates/template-java', function (err) { 59 | if (err) { 60 | return console.error(err); 61 | } 62 | console.log('Copying complete!'); 63 | rimraf.sync(path.join(__dirname, '/tmp')); 64 | }); 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /generators/app/sync_template_kotlin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const rimraf = require('rimraf'); 5 | const mv = require('mv'); 6 | const clone = require('nodegit').Clone; 7 | const replace = require('replace'); 8 | const ncp = require('ncp').ncp; 9 | 10 | clone('https://github.com/ravidsrk/kotlin-android-starter.git', './tmp') 11 | .then(function (repo) { 12 | checkOutAndCopy(repo, 'develop'); 13 | }) 14 | .catch(function (err) { 15 | console.log(err); 16 | }); 17 | 18 | function checkOutAndCopy(repo, name) { 19 | repo.getBranch('refs/remotes/origin/' + name) 20 | .then(function (reference) { 21 | console.log('Checking out branch ' + name); 22 | return repo.checkoutRef(reference); 23 | }) 24 | .then(function () { 25 | replace({ 26 | regex: 'io.mvpstarter.sample', 27 | replacement: '<%= appPackage %>', 28 | paths: ['./tmp/app'], 29 | recursive: true, 30 | silent: true 31 | }); 32 | 33 | mv('./tmp/.gitignore', './tmp/gitignore', function (err) { 34 | if (err) { 35 | console.log(err); 36 | } 37 | console.log('Renamed root folder .gitignore'); 38 | }); 39 | 40 | mv('./tmp/app/.gitignore', './tmp/app/gitignore', function (err) { 41 | if (err) { 42 | console.log(err); 43 | } 44 | console.log('Renamed app folder .gitignore'); 45 | }); 46 | 47 | rimraf.sync(path.join(__dirname, '/tmp/.git')); 48 | 49 | console.log('Copying files to ./templates/template-kotlin'); 50 | 51 | ncp.limit = 1600; 52 | ncp('./tmp', './templates/template-kotlin', function (err) { 53 | if (err) { 54 | return console.error(err); 55 | } 56 | console.log('Copying complete!'); 57 | rimraf.sync(path.join(__dirname, '/tmp')); 58 | }); 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: android 4 | jdk: oraclejdk8 5 | 6 | android: 7 | components: 8 | - platform-tools 9 | - tools # to get the new `repository-11.xml` 10 | - tools # see https://github.com/travis-ci/travis-ci/issues/6040#issuecomment-219367943) 11 | - build-tools-26.0.0 12 | - android-26 13 | - extra-android-m2repository 14 | - extra-google-m2repository 15 | - extra-android-support 16 | - extra-google-google_play_services 17 | 18 | before_script: 19 | - chmod +x gradlew 20 | 21 | script: "./gradlew testDebug -PdisablePreDex" 22 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | *iml 3 | *.iml 4 | .idea -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | // Unused will be removed on release 9 | // Using the material icons provided from Google 10 | // We might want to index our app later 11 | // Butterknife, Okio and Realm 12 | // Annotation binding 13 | 14 | 15 | // CI issue with sdk.dir in local.properties 16 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.4.1/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/androidTest/java/io/mvpstarter/sample/DetailActivityTest.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>; 2 | 3 | import android.support.test.InstrumentationRegistry; 4 | import android.support.test.rule.ActivityTestRule; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | import org.junit.rules.RuleChain; 10 | import org.junit.rules.TestRule; 11 | import org.junit.runner.RunWith; 12 | 13 | import <%= appPackage %>.common.TestComponentRule; 14 | import <%= appPackage %>.common.TestDataFactory; 15 | import <%= appPackage %>.data.model.response.Pokemon; 16 | import <%= appPackage %>.data.model.response.Statistic; 17 | import <%= appPackage %>.features.detail.DetailActivity; 18 | import <%= appPackage %>.util.ErrorTestUtil; 19 | import io.reactivex.Single; 20 | 21 | import static android.support.test.espresso.Espresso.onView; 22 | import static android.support.test.espresso.assertion.ViewAssertions.matches; 23 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; 24 | import static android.support.test.espresso.matcher.ViewMatchers.withText; 25 | import static org.mockito.Matchers.anyString; 26 | import static org.mockito.Mockito.when; 27 | 28 | @RunWith(AndroidJUnit4.class) 29 | public class DetailActivityTest { 30 | 31 | public final TestComponentRule component = 32 | new TestComponentRule(InstrumentationRegistry.getTargetContext()); 33 | public final ActivityTestRule main = 34 | new ActivityTestRule<>(DetailActivity.class, false, false); 35 | 36 | // TestComponentRule needs to go first to make sure the Dagger ApplicationTestComponent is set 37 | // in the Application before any Activity is launched. 38 | @Rule 39 | public TestRule chain = RuleChain.outerRule(component).around(main); 40 | 41 | @Test 42 | public void checkPokemonDisplays() { 43 | Pokemon pokemon = TestDataFactory.makePokemon("id"); 44 | stubDataManagerGetPokemon(Single.just(pokemon)); 45 | main.launchActivity( 46 | DetailActivity.getStartIntent(InstrumentationRegistry.getContext(), pokemon.name)); 47 | 48 | for (Statistic stat : pokemon.stats) { 49 | onView(withText(stat.stat.name)).check(matches(isDisplayed())); 50 | } 51 | } 52 | 53 | @Test 54 | public void checkErrorViewDisplays() { 55 | stubDataManagerGetPokemon(Single.error(new RuntimeException())); 56 | Pokemon pokemon = TestDataFactory.makePokemon("id"); 57 | main.launchActivity( 58 | DetailActivity.getStartIntent(InstrumentationRegistry.getContext(), pokemon.name)); 59 | ErrorTestUtil.checkErrorViewsDisplay(); 60 | } 61 | 62 | public void stubDataManagerGetPokemon(Single single) { 63 | when(component.getMockApiManager().getPokemon(anyString())).thenReturn(single); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/androidTest/java/io/mvpstarter/sample/runner/RxAndroidJUnitRunner.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.runner; 2 | 3 | import android.os.Bundle; 4 | import android.support.test.espresso.Espresso; 5 | 6 | import <%= appPackage %>.util.RxIdlingExecutionHook; 7 | import <%= appPackage %>.util.RxIdlingResource; 8 | import rx.plugins.RxJavaPlugins; 9 | 10 | /** 11 | * Runner that registers a Espresso Indling resource that handles waiting for RxJava Observables to 12 | * finish. WARNING - Using this runner will block the tests if the application uses long-lived hot 13 | * Observables such us event buses, etc. 14 | */ 15 | public class RxAndroidJUnitRunner extends UnlockDeviceAndroidJUnitRunner { 16 | 17 | @Override 18 | public void onCreate(Bundle arguments) { 19 | super.onCreate(arguments); 20 | RxIdlingResource rxIdlingResource = new RxIdlingResource(); 21 | RxJavaPlugins.getInstance() 22 | .registerObservableExecutionHook(new RxIdlingExecutionHook(rxIdlingResource)); 23 | Espresso.registerIdlingResources(rxIdlingResource); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/androidTest/java/io/mvpstarter/sample/runner/TestRunner.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.runner; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import io.appflate.restmock.android.RESTMockTestRunner; 7 | import <%= appPackage %>.MvpStarterApplication; 8 | 9 | /** 10 | * Created by ravindra on 4/2/17. 11 | */ 12 | public class TestRunner extends RESTMockTestRunner { 13 | 14 | @Override 15 | public Application newApplication(ClassLoader cl, String className, Context context) 16 | throws InstantiationException, IllegalAccessException, ClassNotFoundException { 17 | return super.newApplication(cl, MvpStarterApplication.class.getName(), context); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/androidTest/java/io/mvpstarter/sample/runner/UnlockDeviceAndroidJUnitRunner.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.runner; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Application; 5 | import android.app.KeyguardManager; 6 | import android.os.PowerManager; 7 | import android.support.test.runner.AndroidJUnitRunner; 8 | 9 | import static android.content.Context.KEYGUARD_SERVICE; 10 | import static android.content.Context.POWER_SERVICE; 11 | import static android.os.PowerManager.ACQUIRE_CAUSES_WAKEUP; 12 | import static android.os.PowerManager.FULL_WAKE_LOCK; 13 | import static android.os.PowerManager.ON_AFTER_RELEASE; 14 | 15 | public class UnlockDeviceAndroidJUnitRunner extends AndroidJUnitRunner { 16 | 17 | private PowerManager.WakeLock mWakeLock; 18 | 19 | @SuppressLint("MissingPermission") 20 | @Override 21 | public void onStart() { 22 | Application application = (Application) getTargetContext().getApplicationContext(); 23 | String simpleName = UnlockDeviceAndroidJUnitRunner.class.getSimpleName(); 24 | // Unlock the device so that the tests can input keystrokes. 25 | ((KeyguardManager) application.getSystemService(KEYGUARD_SERVICE)) 26 | .newKeyguardLock(simpleName) 27 | .disableKeyguard(); 28 | // Wake up the screen. 29 | PowerManager powerManager = ((PowerManager) application.getSystemService(POWER_SERVICE)); 30 | mWakeLock = 31 | powerManager.newWakeLock( 32 | FULL_WAKE_LOCK | ACQUIRE_CAUSES_WAKEUP | ON_AFTER_RELEASE, simpleName); 33 | mWakeLock.acquire(); 34 | super.onStart(); 35 | } 36 | 37 | @Override 38 | public void onDestroy() { 39 | super.onDestroy(); 40 | mWakeLock.release(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/androidTest/java/io/mvpstarter/sample/util/ErrorTestUtil.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util; 2 | 3 | import <%= appPackage %>.R; 4 | 5 | import static android.support.test.espresso.Espresso.onView; 6 | import static android.support.test.espresso.action.ViewActions.click; 7 | import static android.support.test.espresso.assertion.ViewAssertions.matches; 8 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; 9 | import static android.support.test.espresso.matcher.ViewMatchers.withText; 10 | import static org.hamcrest.Matchers.allOf; 11 | 12 | public class ErrorTestUtil { 13 | 14 | public static void checkErrorViewsDisplay() { 15 | onView(allOf(withText(R.string.error_title), isDisplayed())).check(matches(isDisplayed())); 16 | onView(allOf(withText(R.string.error_message), isDisplayed())) 17 | .check(matches(isDisplayed())); 18 | } 19 | 20 | public static void checkClickingReloadShowsContentWithText(String expectedText) { 21 | onView(allOf(withText(R.string.error_reload), isDisplayed())).perform(click()); 22 | onView(withText(expectedText)).check(matches(isDisplayed())); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/androidTest/java/io/mvpstarter/sample/util/RxIdlingExecutionHook.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util; 2 | 3 | import rx.Observable; 4 | import rx.Subscription; 5 | import rx.plugins.RxJavaObservableExecutionHook; 6 | 7 | /** 8 | * RxJava Observable execution hook that handles updating the active subscription count for a given 9 | * Espresso RxIdlingResource. 10 | */ 11 | public class RxIdlingExecutionHook extends RxJavaObservableExecutionHook { 12 | 13 | private RxIdlingResource mRxIdlingResource; 14 | 15 | public RxIdlingExecutionHook(RxIdlingResource rxIdlingResource) { 16 | mRxIdlingResource = rxIdlingResource; 17 | } 18 | 19 | @Override 20 | public Observable.OnSubscribe onSubscribeStart( 21 | Observable observableInstance, Observable.OnSubscribe onSubscribe) { 22 | mRxIdlingResource.incrementActiveSubscriptionsCount(); 23 | return super.onSubscribeStart(observableInstance, onSubscribe); 24 | } 25 | 26 | @Override 27 | public Throwable onSubscribeError(Throwable e) { 28 | mRxIdlingResource.decrementActiveSubscriptionsCount(); 29 | return super.onSubscribeError(e); 30 | } 31 | 32 | @Override 33 | public Subscription onSubscribeReturn(Subscription subscription) { 34 | mRxIdlingResource.decrementActiveSubscriptionsCount(); 35 | return super.onSubscribeReturn(subscription); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/androidTest/java/io/mvpstarter/sample/util/RxIdlingResource.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util; 2 | 3 | import android.support.test.espresso.IdlingResource; 4 | 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | 7 | import timber.log.Timber; 8 | 9 | /** 10 | * Espresso Idling resource that handles waiting for RxJava Observables executions. This class must 11 | * be used with RxIdlingExecutionHook. Before registering this idling resource you must: 1. Create 12 | * an instance of RxIdlingExecutionHook by passing an instance of this class. 2. Register 13 | * RxIdlingExecutionHook with the RxJavaPlugins using registerObservableExecutionHook() 3. Register 14 | * this idle resource with Espresso using Espresso.registerIdlingResources() 15 | */ 16 | public class RxIdlingResource implements IdlingResource { 17 | 18 | private final AtomicInteger mActiveSubscriptionsCount = new AtomicInteger(0); 19 | private ResourceCallback mResourceCallback; 20 | 21 | @Override 22 | public String getName() { 23 | return getClass().getSimpleName(); 24 | } 25 | 26 | @Override 27 | public boolean isIdleNow() { 28 | return mActiveSubscriptionsCount.get() <= 0; 29 | } 30 | 31 | @Override 32 | public void registerIdleTransitionCallback(ResourceCallback callback) { 33 | mResourceCallback = callback; 34 | } 35 | 36 | public void incrementActiveSubscriptionsCount() { 37 | int count = mActiveSubscriptionsCount.incrementAndGet(); 38 | Timber.i("Active subscriptions count increased to %d", count); 39 | } 40 | 41 | public void decrementActiveSubscriptionsCount() { 42 | int count = mActiveSubscriptionsCount.decrementAndGet(); 43 | Timber.i("Active subscriptions count decreased to %d", count); 44 | if (isIdleNow()) { 45 | Timber.i("There is no active subscriptions, transitioning to Idle"); 46 | mResourceCallback.onTransitionToIdle(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/commonTest/java/io/mvpstarter/sample/common/TestComponentRule.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.common; 2 | 3 | import android.content.Context; 4 | 5 | import org.junit.rules.TestRule; 6 | import org.junit.runner.Description; 7 | import org.junit.runners.model.Statement; 8 | 9 | import <%= appPackage %>.MvpStarterApplication; 10 | import <%= appPackage %>.common.injection.component.DaggerTestComponent; 11 | import <%= appPackage %>.common.injection.component.TestComponent; 12 | import <%= appPackage %>.common.injection.module.ApplicationTestModule; 13 | import <%= appPackage %>.data.DataManager; 14 | 15 | /** 16 | * Test rule that creates and sets a Dagger TestComponent into the application overriding the 17 | * existing application component. Use this rule in your test case in order for the app to use mock 18 | * dependencies. It also exposes some of the dependencies so they can be easily accessed from the 19 | * tests, e.g. to stub mocks etc. 20 | */ 21 | public class TestComponentRule implements TestRule { 22 | 23 | private final TestComponent testComponent; 24 | private final Context context; 25 | 26 | public TestComponentRule(Context context) { 27 | this.context = context; 28 | MvpStarterApplication application = MvpStarterApplication.get(context); 29 | testComponent = 30 | DaggerTestComponent.builder() 31 | .applicationTestModule(new ApplicationTestModule(application)) 32 | .build(); 33 | } 34 | 35 | public TestComponent getTestComponent() { 36 | return testComponent; 37 | } 38 | 39 | public Context getContext() { 40 | return context; 41 | } 42 | 43 | public DataManager getMockApiManager() { 44 | return testComponent.apiManager(); 45 | } 46 | 47 | @Override 48 | public Statement apply(final Statement base, Description description) { 49 | return new Statement() { 50 | @Override 51 | public void evaluate() throws Throwable { 52 | MvpStarterApplication application = MvpStarterApplication.get(context); 53 | application.setComponent(testComponent); 54 | base.evaluate(); 55 | application.setComponent(null); 56 | } 57 | }; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/commonTest/java/io/mvpstarter/sample/common/TestDataFactory.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.common; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Random; 6 | import java.util.UUID; 7 | 8 | import <%= appPackage %>.data.model.response.NamedResource; 9 | import <%= appPackage %>.data.model.response.Pokemon; 10 | import <%= appPackage %>.data.model.response.Sprites; 11 | import <%= appPackage %>.data.model.response.Statistic; 12 | 13 | /** 14 | * Factory class that makes instances of data models with random field values. The aim of this class 15 | * is to help setting up test fixtures. 16 | */ 17 | public class TestDataFactory { 18 | 19 | private static final Random random = new Random(); 20 | 21 | public static String randomUuid() { 22 | return UUID.randomUUID().toString(); 23 | } 24 | 25 | public static Pokemon makePokemon(String id) { 26 | Pokemon pokemon = new Pokemon(); 27 | pokemon.id = id; 28 | pokemon.name = randomUuid() + id; 29 | pokemon.stats = makeStatisticList(3); 30 | pokemon.sprites = makeSprites(); 31 | return pokemon; 32 | } 33 | 34 | public static List makePokemonNamesList(int count) { 35 | List pokemonList = new ArrayList<>(); 36 | for (int i = 0; i < count; i++) { 37 | pokemonList.add(makePokemon(String.valueOf(i)).name); 38 | } 39 | return pokemonList; 40 | } 41 | 42 | public static List makePokemonNameList(List pokemonList) { 43 | List names = new ArrayList<>(); 44 | for (NamedResource pokemon : pokemonList) { 45 | names.add(pokemon.name); 46 | } 47 | return names; 48 | } 49 | 50 | public static Statistic makeStatistic() { 51 | Statistic statistic = new Statistic(); 52 | statistic.baseStat = random.nextInt(); 53 | statistic.stat = makeNamedResource(randomUuid()); 54 | return statistic; 55 | } 56 | 57 | public static List makeStatisticList(int count) { 58 | List statisticList = new ArrayList<>(); 59 | for (int i = 0; i < count; i++) { 60 | statisticList.add(makeStatistic()); 61 | } 62 | return statisticList; 63 | } 64 | 65 | public static Sprites makeSprites() { 66 | Sprites sprites = new Sprites(); 67 | sprites.frontDefault = randomUuid(); 68 | return sprites; 69 | } 70 | 71 | public static NamedResource makeNamedResource(String unique) { 72 | NamedResource namedResource = new NamedResource(); 73 | namedResource.name = randomUuid() + unique; 74 | namedResource.url = randomUuid(); 75 | return namedResource; 76 | } 77 | 78 | public static List makeNamedResourceList(int count) { 79 | List namedResourceList = new ArrayList<>(); 80 | for (int i = 0; i < count; i++) { 81 | namedResourceList.add(makeNamedResource(String.valueOf(i))); 82 | } 83 | return namedResourceList; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/commonTest/java/io/mvpstarter/sample/common/injection/component/TestComponent.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.common.injection.component; 2 | 3 | import javax.inject.Singleton; 4 | 5 | import dagger.Component; 6 | import <%= appPackage %>.common.injection.module.ApplicationTestModule; 7 | import <%= appPackage %>.injection.component.AppComponent; 8 | 9 | @Singleton 10 | @Component(modules = ApplicationTestModule.class) 11 | public interface TestComponent extends AppComponent { 12 | } 13 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/commonTest/java/io/mvpstarter/sample/common/injection/module/ApplicationTestModule.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.common.injection.module; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import javax.inject.Singleton; 7 | 8 | import dagger.Module; 9 | import dagger.Provides; 10 | import <%= appPackage %>.data.DataManager; 11 | import <%= appPackage %>.data.remote.PokemonService; 12 | import <%= appPackage %>.injection.ApplicationContext; 13 | 14 | import static org.mockito.Mockito.mock; 15 | 16 | /** 17 | * Provides application-level dependencies for an app running on a testing environment This allows 18 | * injecting mocks if necessary. 19 | */ 20 | @Module 21 | public class ApplicationTestModule { 22 | private final Application mApplication; 23 | 24 | public ApplicationTestModule(Application application) { 25 | mApplication = application; 26 | } 27 | 28 | @Provides 29 | @Singleton 30 | Application provideApplication() { 31 | return mApplication; 32 | } 33 | 34 | @Provides 35 | @ApplicationContext 36 | Context provideContext() { 37 | return mApplication; 38 | } 39 | 40 | /** 41 | * ********** MOCKS *********** 42 | */ 43 | @Provides 44 | @Singleton 45 | DataManager providesDataManager() { 46 | return mock(DataManager.class); 47 | } 48 | 49 | @Provides 50 | @Singleton 51 | PokemonService provideMvpBoilerplateService() { 52 | return mock(PokemonService.class); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/debug/res/values/google_maps_api.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | YOUR_KEY_HERE 20 | 21 | 22 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 43 | 46 | 47 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/Constants.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>; 2 | 3 | /** 4 | * Created by shivam on 29/5/17. 5 | */ 6 | public interface Constants { 7 | 8 | String PREF_FILE_NAME = "mvpstarter_pref_file"; 9 | } 10 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/MvpStarterApplication.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.facebook.stetho.Stetho; 7 | import com.singhajit.sherlock.core.Sherlock; 8 | import com.squareup.leakcanary.LeakCanary; 9 | import com.tspoon.traceur.Traceur; 10 | 11 | import <%= appPackage %>.injection.component.AppComponent; 12 | import <%= appPackage %>.injection.component.DaggerAppComponent; 13 | import <%= appPackage %>.injection.module.AppModule; 14 | import <%= appPackage %>.injection.module.NetworkModule; 15 | import timber.log.Timber; 16 | 17 | public class MvpStarterApplication extends Application { 18 | 19 | private AppComponent appComponent; 20 | 21 | public static MvpStarterApplication get(Context context) { 22 | return (MvpStarterApplication) context.getApplicationContext(); 23 | } 24 | 25 | @Override 26 | public void onCreate() { 27 | super.onCreate(); 28 | 29 | if (BuildConfig.DEBUG) { 30 | Timber.plant(new Timber.DebugTree()); 31 | Stetho.initializeWithDefaults(this); 32 | LeakCanary.install(this); 33 | Sherlock.init(this); 34 | Traceur.enableLogging(); 35 | } 36 | } 37 | 38 | public AppComponent getComponent() { 39 | if (appComponent == null) { 40 | appComponent = DaggerAppComponent.builder() 41 | .networkModule(new NetworkModule(this)) 42 | .appModule(new AppModule(this)) 43 | .build(); 44 | } 45 | return appComponent; 46 | } 47 | 48 | // Needed to replace the component with a test specific one 49 | public void setComponent(AppComponent appComponent) { 50 | this.appComponent = appComponent; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/data/DataManager.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.data; 2 | 3 | import java.util.List; 4 | 5 | import javax.inject.Inject; 6 | import javax.inject.Singleton; 7 | 8 | import <%= appPackage %>.data.model.response.Pokemon; 9 | import <%= appPackage %>.data.remote.PokemonService; 10 | import io.reactivex.Single; 11 | 12 | /** 13 | * Created by shivam on 29/5/17. 14 | */ 15 | @Singleton 16 | public class DataManager { 17 | 18 | private PokemonService pokemonService; 19 | 20 | @Inject 21 | public DataManager(PokemonService pokemonService) { 22 | this.pokemonService = pokemonService; 23 | } 24 | 25 | public Single> getPokemonList(int limit) { 26 | return pokemonService 27 | .getPokemonList(limit) 28 | .toObservable() 29 | .flatMapIterable(namedResources -> namedResources.results) 30 | .map(namedResource -> namedResource.name) 31 | .toList(); 32 | } 33 | 34 | public Single getPokemon(String name) { 35 | return pokemonService.getPokemon(name); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/data/local/DbManager.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.data.local; 2 | 3 | import javax.inject.Inject; 4 | import javax.inject.Singleton; 5 | 6 | /** 7 | * Created by shivam on 29/5/17. 8 | */ 9 | 10 | // To be implemented with Realm 11 | 12 | @Singleton 13 | public class DbManager { 14 | 15 | @Inject 16 | public DbManager() { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/data/local/PreferencesHelper.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.data.local; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import javax.annotation.Nonnull; 6 | import javax.inject.Inject; 7 | import javax.inject.Singleton; 8 | 9 | @Singleton 10 | public class PreferencesHelper { 11 | 12 | private static final String PREF_FILE_NAME = "mvpstarter_pref_file"; 13 | 14 | private final SharedPreferences preferences; 15 | 16 | @Inject 17 | PreferencesHelper(SharedPreferences sharedPreferences) { 18 | preferences = sharedPreferences; 19 | } 20 | 21 | public void putString(@Nonnull String key, @Nonnull String value) { 22 | preferences.edit().putString(key, value).apply(); 23 | } 24 | 25 | public String getString(@Nonnull String key) { 26 | return preferences.getString(key, ""); 27 | } 28 | 29 | public void putBoolean(@Nonnull String key, @Nonnull boolean value) { 30 | preferences.edit().putBoolean(key, value).apply(); 31 | } 32 | 33 | public boolean getBoolean(@Nonnull String key) { 34 | return preferences.getBoolean(key, false); 35 | } 36 | 37 | public void putInt(@Nonnull String key, @Nonnull boolean value) { 38 | preferences.edit().putBoolean(key, value).apply(); 39 | } 40 | 41 | public int getInt(@Nonnull String key) { 42 | return preferences.getInt(key, -1); 43 | } 44 | 45 | public void clear() { 46 | preferences.edit().clear().apply(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/data/model/response/NamedResource.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.data.model.response; 2 | 3 | public class NamedResource { 4 | public String name; 5 | public String url; 6 | } 7 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/data/model/response/Pokemon.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.data.model.response; 2 | 3 | import java.util.List; 4 | 5 | public class Pokemon { 6 | public String id; 7 | public String name; 8 | public Sprites sprites; 9 | public List stats; 10 | } 11 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/data/model/response/PokemonListResponse.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.data.model.response; 2 | 3 | import java.util.List; 4 | 5 | public class PokemonListResponse { 6 | public List results; 7 | } 8 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/data/model/response/Sprites.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.data.model.response; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | public class Sprites { 6 | @SerializedName("front_default") 7 | public String frontDefault; 8 | } 9 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/data/model/response/Statistic.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.data.model.response; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | public class Statistic { 6 | public NamedResource stat; 7 | 8 | @SerializedName("base_stat") 9 | public int baseStat; 10 | } 11 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/data/remote/PokemonService.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.data.remote; 2 | 3 | import <%= appPackage %>.data.model.response.Pokemon; 4 | import <%= appPackage %>.data.model.response.PokemonListResponse; 5 | import io.reactivex.Single; 6 | import retrofit2.http.GET; 7 | import retrofit2.http.Path; 8 | import retrofit2.http.Query; 9 | 10 | public interface PokemonService { 11 | 12 | @GET("pokemon") 13 | Single getPokemonList(@Query("limit") int limit); 14 | 15 | @GET("pokemon/{name}") 16 | Single getPokemon(@Path("name") String name); 17 | } 18 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/features/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.features.base; 2 | 3 | import io.reactivex.disposables.CompositeDisposable; 4 | import io.reactivex.disposables.Disposable; 5 | import rx.Observable; 6 | import rx.Single; 7 | 8 | /** 9 | * Base class that implements the Presenter interface and provides a base implementation for 10 | * attachView() and detachView(). It also handles keeping a reference to the mvpView that can be 11 | * accessed from the children classes by calling getView(). 12 | */ 13 | public class BasePresenter implements Presenter { 14 | 15 | private final CompositeDisposable compositeDisposable = new CompositeDisposable(); 16 | private T mvpView; 17 | 18 | @Override 19 | public void attachView(T mvpView) { 20 | this.mvpView = mvpView; 21 | } 22 | 23 | @Override 24 | public void detachView() { 25 | mvpView = null; 26 | if (!compositeDisposable.isDisposed()) { 27 | compositeDisposable.clear(); 28 | } 29 | } 30 | 31 | protected boolean isViewAttached() { 32 | return mvpView != null; 33 | } 34 | 35 | protected T getView() { 36 | return mvpView; 37 | } 38 | 39 | protected void checkViewAttached() { 40 | if (!isViewAttached()) throw new MvpViewNotAttachedException(); 41 | } 42 | 43 | public void addDisposable(Disposable disposable) { 44 | compositeDisposable.add(disposable); 45 | } 46 | 47 | private static class MvpViewNotAttachedException extends RuntimeException { 48 | MvpViewNotAttachedException() { 49 | super( 50 | "Please call Presenter.attachView(MvpView) before" 51 | + " requesting data to the Presenter"); 52 | } 53 | } 54 | 55 | /** 56 | * Encapsulate the result of an rx Observable. This model is meant to be used by the children 57 | * presenters to easily keep a reference to the latest loaded result so that it can be easily 58 | * emitted again when on configuration changes. 59 | */ 60 | protected static class DataResult { 61 | 62 | private T mData; 63 | private Throwable mError; 64 | 65 | public DataResult(T data) { 66 | mData = data; 67 | } 68 | 69 | public DataResult(Throwable error) { 70 | mError = error; 71 | } 72 | 73 | public Single toSingle() { 74 | if (mError != null) { 75 | return Single.error(mError); 76 | } 77 | return Single.just(mData); 78 | } 79 | 80 | public Observable toObservable() { 81 | if (mError != null) { 82 | return Observable.error(mError); 83 | } 84 | return Observable.just(mData); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/features/base/MvpView.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.features.base; 2 | 3 | /** 4 | * Base interface that any class that wants to act as a View in the MVP (Model View Presenter) 5 | * pattern must implement. Generally this interface will be extended by a more specific interface 6 | * that then usually will be implemented by an Activity or Fragment. 7 | */ 8 | public interface MvpView { 9 | } 10 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/features/base/Presenter.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.features.base; 2 | 3 | /** 4 | * Every presenter in the app must either implement this interface or extend BasePresenter 5 | * indicating the MvpView type that wants to be attached with. 6 | */ 7 | public interface Presenter { 8 | 9 | void attachView(V mvpView); 10 | 11 | void detachView(); 12 | } 13 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/features/common/ErrorView.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.features.common; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.util.AttributeSet; 7 | import android.view.Gravity; 8 | import android.view.LayoutInflater; 9 | import android.widget.LinearLayout; 10 | 11 | import butterknife.ButterKnife; 12 | import butterknife.OnClick; 13 | import <%= appPackage %>.R; 14 | 15 | public class ErrorView extends LinearLayout { 16 | 17 | private ErrorListener errorListener; 18 | 19 | public ErrorView(Context context) { 20 | super(context); 21 | init(); 22 | } 23 | 24 | public ErrorView(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | init(); 27 | } 28 | 29 | public ErrorView(Context context, AttributeSet attrs, int defStyleAttr) { 30 | super(context, attrs, defStyleAttr); 31 | init(); 32 | } 33 | 34 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 35 | public ErrorView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 36 | super(context, attrs, defStyleAttr, defStyleRes); 37 | init(); 38 | } 39 | 40 | private void init() { 41 | setOrientation(VERTICAL); 42 | setGravity(Gravity.CENTER); 43 | LayoutInflater.from(getContext()).inflate(R.layout.view_error, this); 44 | ButterKnife.bind(this); 45 | } 46 | 47 | @OnClick(R.id.button_reload) 48 | public void onReloadButtonClick() { 49 | if (errorListener != null) { 50 | errorListener.onReloadData(); 51 | } 52 | } 53 | 54 | public void setErrorListener(ErrorListener errorListener) { 55 | this.errorListener = errorListener; 56 | } 57 | 58 | public interface ErrorListener { 59 | void onReloadData(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/features/detail/DetailMvpView.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.features.detail; 2 | 3 | import <%= appPackage %>.data.model.response.Pokemon; 4 | import <%= appPackage %>.data.model.response.Statistic; 5 | import <%= appPackage %>.features.base.MvpView; 6 | 7 | public interface DetailMvpView extends MvpView { 8 | 9 | void showPokemon(Pokemon pokemon); 10 | 11 | void showStat(Statistic statistic); 12 | 13 | void showProgress(boolean show); 14 | 15 | void showError(Throwable error); 16 | } 17 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/features/detail/DetailPresenter.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.features.detail; 2 | 3 | import javax.inject.Inject; 4 | 5 | import <%= appPackage %>.data.DataManager; 6 | import <%= appPackage %>.data.model.response.Statistic; 7 | import <%= appPackage %>.features.base.BasePresenter; 8 | import <%= appPackage %>.injection.ConfigPersistent; 9 | import <%= appPackage %>.util.rx.scheduler.SchedulerUtils; 10 | 11 | @ConfigPersistent 12 | public class DetailPresenter extends BasePresenter { 13 | 14 | private final DataManager dataManager; 15 | 16 | @Inject 17 | public DetailPresenter(DataManager dataManager) { 18 | this.dataManager = dataManager; 19 | } 20 | 21 | @Override 22 | public void attachView(DetailMvpView mvpView) { 23 | super.attachView(mvpView); 24 | } 25 | 26 | public void getPokemon(String name) { 27 | checkViewAttached(); 28 | getView().showProgress(true); 29 | dataManager 30 | .getPokemon(name) 31 | .compose(SchedulerUtils.ioToMain()) 32 | .subscribe( 33 | pokemon -> { 34 | // It should be always checked if MvpView (Fragment or Activity) is attached. 35 | // Calling showProgress() on a not-attached fragment will throw a NPE 36 | // It is possible to ask isAdded() in the fragment, but it's better to ask in the presenter 37 | getView().showProgress(false); 38 | getView().showPokemon(pokemon); 39 | for (Statistic statistic : pokemon.stats) { 40 | getView().showStat(statistic); 41 | } 42 | }, 43 | throwable -> { 44 | getView().showProgress(false); 45 | getView().showError(throwable); 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/features/detail/MapsSampleActivity.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.features.detail; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import com.google.android.gms.maps.CameraUpdateFactory; 7 | import com.google.android.gms.maps.GoogleMap; 8 | import com.google.android.gms.maps.OnMapReadyCallback; 9 | import com.google.android.gms.maps.SupportMapFragment; 10 | import com.google.android.gms.maps.model.LatLng; 11 | import com.google.android.gms.maps.model.MarkerOptions; 12 | 13 | import <%= appPackage %>.R; 14 | 15 | public class MapsSampleActivity extends AppCompatActivity implements OnMapReadyCallback { 16 | 17 | private GoogleMap googleMap; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_maps_sample); 23 | // Obtain the SupportMapFragment and get notified when the map is ready to be used. 24 | SupportMapFragment mapFragment = 25 | (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); 26 | mapFragment.getMapAsync(this); 27 | } 28 | 29 | /** 30 | * Manipulates the map once available. This callback is triggered when the map is ready to be 31 | * used. This is where we can add markers or lines, add listeners or move the camera. In this 32 | * case, we just add a marker near Sydney, Australia. If Google Play services is not installed 33 | * on the device, the user will be prompted to install it inside the SupportMapFragment. This 34 | * method will only be triggered once the user has installed Google Play services and returned 35 | * to the app. 36 | */ 37 | @Override 38 | public void onMapReady(GoogleMap googleMap) { 39 | this.googleMap = googleMap; 40 | 41 | // Add a marker in Sydney and move the camera 42 | LatLng sydney = new LatLng(-34, 151); 43 | googleMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney")); 44 | googleMap.moveCamera(CameraUpdateFactory.newLatLng(sydney)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/features/detail/widget/StatisticView.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.features.detail.widget; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.annotation.TargetApi; 5 | import android.content.Context; 6 | import android.os.Build; 7 | import android.util.AttributeSet; 8 | import android.view.LayoutInflater; 9 | import android.widget.ProgressBar; 10 | import android.widget.RelativeLayout; 11 | import android.widget.TextView; 12 | 13 | import butterknife.BindView; 14 | import butterknife.ButterKnife; 15 | import <%= appPackage %>.R; 16 | import <%= appPackage %>.data.model.response.Statistic; 17 | 18 | public class StatisticView extends RelativeLayout { 19 | 20 | @BindView(R.id.text_name) 21 | TextView nameText; 22 | 23 | @BindView(R.id.progress_stat) 24 | ProgressBar statProgress; 25 | 26 | public StatisticView(Context context) { 27 | super(context); 28 | init(); 29 | } 30 | 31 | public StatisticView(Context context, AttributeSet attrs) { 32 | super(context, attrs); 33 | init(); 34 | } 35 | 36 | public StatisticView(Context context, AttributeSet attrs, int defStyleAttr) { 37 | super(context, attrs, defStyleAttr); 38 | init(); 39 | } 40 | 41 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 42 | public StatisticView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 43 | super(context, attrs, defStyleAttr, defStyleRes); 44 | init(); 45 | } 46 | 47 | private void init() { 48 | LayoutInflater.from(getContext()).inflate(R.layout.view_statistic, this); 49 | ButterKnife.bind(this); 50 | } 51 | 52 | @SuppressLint("SetTextI18n") 53 | public void setStat(Statistic statistic) { 54 | nameText.setText( 55 | statistic.stat.name.substring(0, 1).toUpperCase() 56 | + statistic.stat.name.substring(1)); 57 | statProgress.setProgress(statistic.baseStat); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/features/main/MainMvpView.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.features.main; 2 | 3 | import java.util.List; 4 | 5 | import <%= appPackage %>.features.base.MvpView; 6 | 7 | public interface MainMvpView extends MvpView { 8 | 9 | void showPokemon(List pokemon); 10 | 11 | void showProgress(boolean show); 12 | 13 | void showError(Throwable error); 14 | } 15 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/features/main/MainPresenter.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.features.main; 2 | 3 | import javax.inject.Inject; 4 | 5 | import <%= appPackage %>.data.DataManager; 6 | import <%= appPackage %>.features.base.BasePresenter; 7 | import <%= appPackage %>.injection.ConfigPersistent; 8 | import <%= appPackage %>.util.rx.scheduler.SchedulerUtils; 9 | 10 | @ConfigPersistent 11 | public class MainPresenter extends BasePresenter { 12 | 13 | private final DataManager dataManager; 14 | 15 | @Inject 16 | public MainPresenter(DataManager dataManager) { 17 | this.dataManager = dataManager; 18 | } 19 | 20 | @Override 21 | public void attachView(MainMvpView mvpView) { 22 | super.attachView(mvpView); 23 | } 24 | 25 | public void getPokemon(int limit) { 26 | checkViewAttached(); 27 | getView().showProgress(true); 28 | dataManager 29 | .getPokemonList(limit) 30 | .compose(SchedulerUtils.ioToMain()) 31 | .subscribe( 32 | pokemons -> { 33 | getView().showProgress(false); 34 | getView().showPokemon(pokemons); 35 | }, 36 | throwable -> { 37 | getView().showProgress(false); 38 | getView().showError(throwable); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/features/main/PokemonAdapter.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.features.main; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | import javax.inject.Inject; 13 | 14 | import butterknife.BindView; 15 | import butterknife.ButterKnife; 16 | import <%= appPackage %>.R; 17 | import io.reactivex.Observable; 18 | import io.reactivex.subjects.PublishSubject; 19 | import io.reactivex.subjects.Subject; 20 | 21 | public class PokemonAdapter extends RecyclerView.Adapter { 22 | 23 | private List pokemonList; 24 | private Subject pokemonClickSubject; 25 | 26 | @Inject 27 | PokemonAdapter() { 28 | pokemonClickSubject = PublishSubject.create(); 29 | pokemonList = Collections.emptyList(); 30 | } 31 | 32 | public void setPokemon(List pokemon) { 33 | this.pokemonList = pokemon; 34 | notifyDataSetChanged(); 35 | } 36 | 37 | @Override 38 | public PokemonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 39 | View view = 40 | LayoutInflater.from(parent.getContext()) 41 | .inflate(R.layout.item_pokemon, parent, false); 42 | return new PokemonViewHolder(view); 43 | } 44 | 45 | @Override 46 | public void onBindViewHolder(PokemonViewHolder holder, int position) { 47 | String pokemon = this.pokemonList.get(position); 48 | holder.onBind(pokemon); 49 | } 50 | 51 | @Override 52 | public int getItemCount() { 53 | return pokemonList.size(); 54 | } 55 | 56 | Observable getPokemonClick() { 57 | return pokemonClickSubject; 58 | } 59 | 60 | class PokemonViewHolder extends RecyclerView.ViewHolder { 61 | 62 | @BindView(R.id.text_name) 63 | TextView nameText; 64 | 65 | private String pokemon; 66 | 67 | PokemonViewHolder(View itemView) { 68 | super(itemView); 69 | ButterKnife.bind(this, itemView); 70 | itemView.setOnClickListener(v -> pokemonClickSubject.onNext(pokemon)); 71 | } 72 | 73 | void onBind(String pokemon) { 74 | this.pokemon = pokemon; 75 | nameText.setText( 76 | String.format( 77 | "%s%s", pokemon.substring(0, 1).toUpperCase(), pokemon.substring(1))); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/ActivityContext.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Qualifier; 7 | 8 | @Qualifier 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface ActivityContext { 11 | } 12 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/ApplicationContext.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Qualifier; 7 | 8 | @Qualifier 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface ApplicationContext { 11 | } 12 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/ConfigPersistent.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Scope; 7 | 8 | import <%= appPackage %>.injection.component.ConfigPersistentComponent; 9 | 10 | /** 11 | * A scoping annotation to permit dependencies conform to the life of the {@link 12 | * ConfigPersistentComponent} 13 | */ 14 | @Scope 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface ConfigPersistent { 17 | } 18 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/PerActivity.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Scope; 7 | 8 | /** 9 | * A scoping annotation to permit objects whose lifetime should conform to the life of the Activity 10 | * to be memorised in the correct component. 11 | */ 12 | @Scope 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface PerActivity { 15 | } 16 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/PerFragment.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Scope; 7 | 8 | /** 9 | * A scoping annotation to permit objects whose lifetime should conform to the life of the Fragment 10 | * to be memorised in the correct component. 11 | */ 12 | @Scope 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface PerFragment { 15 | } 16 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/component/ActivityComponent.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection.component; 2 | 3 | import dagger.Subcomponent; 4 | import <%= appPackage %>.features.detail.DetailActivity; 5 | import <%= appPackage %>.features.main.MainActivity; 6 | import <%= appPackage %>.injection.PerActivity; 7 | import <%= appPackage %>.injection.module.ActivityModule; 8 | 9 | @PerActivity 10 | @Subcomponent(modules = ActivityModule.class) 11 | public interface ActivityComponent { 12 | 13 | void inject(MainActivity mainActivity); 14 | 15 | void inject(DetailActivity detailActivity); 16 | } 17 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/component/AppComponent.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection.component; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import javax.inject.Singleton; 7 | 8 | import dagger.Component; 9 | import <%= appPackage %>.data.DataManager; 10 | import <%= appPackage %>.injection.ApplicationContext; 11 | import <%= appPackage %>.injection.module.AppModule; 12 | 13 | @Singleton 14 | @Component(modules = AppModule.class) 15 | public interface AppComponent { 16 | 17 | @ApplicationContext 18 | Context context(); 19 | 20 | Application application(); 21 | 22 | DataManager apiManager(); 23 | } 24 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/component/ConfigPersistentComponent.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection.component; 2 | 3 | import dagger.Component; 4 | import <%= appPackage %>.features.base.BaseActivity; 5 | import <%= appPackage %>.features.base.BaseFragment; 6 | import <%= appPackage %>.injection.ConfigPersistent; 7 | import <%= appPackage %>.injection.module.ActivityModule; 8 | import <%= appPackage %>.injection.module.FragmentModule; 9 | 10 | /** 11 | * A dagger component that will live during the lifecycle of an Activity or Fragment but it won't be 12 | * destroy during configuration changes. Check {@link BaseActivity} and {@link BaseFragment} to see 13 | * how this components survives configuration changes. Use the {@link ConfigPersistent} scope to 14 | * annotate dependencies that need to survive configuration changes (for example Presenters). 15 | */ 16 | @ConfigPersistent 17 | @Component(dependencies = AppComponent.class) 18 | public interface ConfigPersistentComponent { 19 | 20 | ActivityComponent activityComponent(ActivityModule activityModule); 21 | 22 | FragmentComponent fragmentComponent(FragmentModule fragmentModule); 23 | } 24 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/component/FragmentComponent.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection.component; 2 | 3 | import dagger.Subcomponent; 4 | import <%= appPackage %>.injection.PerFragment; 5 | import <%= appPackage %>.injection.module.FragmentModule; 6 | 7 | /** 8 | * This component inject dependencies to all Fragments across the application 9 | */ 10 | @PerFragment 11 | @Subcomponent(modules = FragmentModule.class) 12 | public interface FragmentComponent { 13 | } 14 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/module/ActivityModule.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection.module; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | 6 | import dagger.Module; 7 | import dagger.Provides; 8 | import <%= appPackage %>.injection.ActivityContext; 9 | 10 | @Module 11 | public class ActivityModule { 12 | 13 | private Activity activity; 14 | 15 | public ActivityModule(Activity activity) { 16 | this.activity = activity; 17 | } 18 | 19 | @Provides 20 | Activity provideActivity() { 21 | return activity; 22 | } 23 | 24 | @Provides 25 | @ActivityContext 26 | Context providesContext() { 27 | return activity; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/module/ApiModule.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection.module; 2 | 3 | import javax.inject.Singleton; 4 | 5 | import dagger.Module; 6 | import dagger.Provides; 7 | import <%= appPackage %>.data.remote.PokemonService; 8 | import retrofit2.Retrofit; 9 | 10 | /** 11 | * Created by shivam on 29/5/17. 12 | */ 13 | @Module(includes = {NetworkModule.class}) 14 | public class ApiModule { 15 | 16 | @Provides 17 | @Singleton 18 | PokemonService providePokemonApi(Retrofit retrofit) { 19 | return retrofit.create(PokemonService.class); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/module/AppModule.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection.module; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | 7 | import dagger.Module; 8 | import dagger.Provides; 9 | import <%= appPackage %>.injection.ApplicationContext; 10 | 11 | import static <%= appPackage %>.Constants.PREF_FILE_NAME; 12 | 13 | @Module(includes = {ApiModule.class}) 14 | public class AppModule { 15 | private final Application application; 16 | 17 | public AppModule(Application application) { 18 | this.application = application; 19 | } 20 | 21 | @Provides 22 | Application provideApplication() { 23 | return application; 24 | } 25 | 26 | @Provides 27 | @ApplicationContext 28 | Context provideContext() { 29 | return application; 30 | } 31 | 32 | @Provides 33 | @ApplicationContext 34 | SharedPreferences provideSharedPreference(@ApplicationContext Context context) { 35 | return context.getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/injection/module/FragmentModule.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.injection.module; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.support.v4.app.Fragment; 6 | 7 | import dagger.Module; 8 | import dagger.Provides; 9 | import <%= appPackage %>.injection.ActivityContext; 10 | 11 | @Module 12 | public class FragmentModule { 13 | private Fragment fragment; 14 | 15 | public FragmentModule(Fragment fragment) { 16 | this.fragment = fragment; 17 | } 18 | 19 | @Provides 20 | Fragment providesFragment() { 21 | return fragment; 22 | } 23 | 24 | @Provides 25 | Activity provideActivity() { 26 | return fragment.getActivity(); 27 | } 28 | 29 | @Provides 30 | @ActivityContext 31 | Context providesContext() { 32 | return fragment.getActivity(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/util/DialogFactory.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util; 2 | 3 | import android.app.Dialog; 4 | import android.app.ProgressDialog; 5 | import android.content.Context; 6 | import android.support.annotation.StringRes; 7 | import android.support.v7.app.AlertDialog; 8 | 9 | import <%= appPackage %>.R; 10 | 11 | public final class DialogFactory { 12 | 13 | public static Dialog createSimpleOkErrorDialog(Context context, String title, String message) { 14 | AlertDialog.Builder alertDialog = 15 | new AlertDialog.Builder(context) 16 | .setTitle(title) 17 | .setMessage(message) 18 | .setNeutralButton(R.string.dialog_action_ok, null); 19 | return alertDialog.create(); 20 | } 21 | 22 | public static Dialog createSimpleOkErrorDialog( 23 | Context context, @StringRes int titleResource, @StringRes int messageResource) { 24 | 25 | return createSimpleOkErrorDialog( 26 | context, context.getString(titleResource), context.getString(messageResource)); 27 | } 28 | 29 | public static Dialog createGenericErrorDialog(Context context, String message) { 30 | AlertDialog.Builder alertDialog = 31 | new AlertDialog.Builder(context) 32 | .setTitle(context.getString(R.string.dialog_error_title)) 33 | .setMessage(message) 34 | .setNeutralButton(R.string.dialog_action_ok, null); 35 | return alertDialog.create(); 36 | } 37 | 38 | public static Dialog createGenericErrorDialog(Context context, @StringRes int messageResource) { 39 | return createGenericErrorDialog(context, context.getString(messageResource)); 40 | } 41 | 42 | public static ProgressDialog createProgressDialog(Context context, String message) { 43 | ProgressDialog progressDialog = new ProgressDialog(context); 44 | progressDialog.setMessage(message); 45 | return progressDialog; 46 | } 47 | 48 | public static ProgressDialog createProgressDialog( 49 | Context context, @StringRes int messageResource) { 50 | return createProgressDialog(context, context.getString(messageResource)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/util/NetworkUtil.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | 7 | import retrofit2.HttpException; 8 | 9 | public class NetworkUtil { 10 | 11 | /** 12 | * Returns true if the Throwable is an instance of RetrofitError with an http status code equals 13 | * to the given one. 14 | */ 15 | public static boolean isHttpStatusCode(Throwable throwable, int statusCode) { 16 | return throwable instanceof HttpException 17 | && ((HttpException) throwable).code() == statusCode; 18 | } 19 | 20 | public static boolean isNetworkConnected(Context context) { 21 | ConnectivityManager cm = 22 | (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 23 | NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); 24 | return activeNetwork != null && activeNetwork.isConnectedOrConnecting(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/util/ViewUtil.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.res.Resources; 6 | import android.view.inputmethod.InputMethodManager; 7 | 8 | public final class ViewUtil { 9 | 10 | public static float pxToDp(float px) { 11 | float densityDpi = Resources.getSystem().getDisplayMetrics().densityDpi; 12 | return px / (densityDpi / 160f); 13 | } 14 | 15 | public static int dpToPx(int dp) { 16 | float density = Resources.getSystem().getDisplayMetrics().density; 17 | return Math.round(dp * density); 18 | } 19 | 20 | public static void hideKeyboard(Activity activity) { 21 | InputMethodManager imm = 22 | (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); 23 | imm.hideSoftInputFromWindow(activity.getWindow().getDecorView().getWindowToken(), 0); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/util/rx/scheduler/BaseScheduler.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util.rx.scheduler; 2 | 3 | import org.reactivestreams.Publisher; 4 | 5 | import io.reactivex.Completable; 6 | import io.reactivex.CompletableSource; 7 | import io.reactivex.CompletableTransformer; 8 | import io.reactivex.Flowable; 9 | import io.reactivex.FlowableTransformer; 10 | import io.reactivex.Maybe; 11 | import io.reactivex.MaybeSource; 12 | import io.reactivex.MaybeTransformer; 13 | import io.reactivex.Observable; 14 | import io.reactivex.ObservableSource; 15 | import io.reactivex.ObservableTransformer; 16 | import io.reactivex.Scheduler; 17 | import io.reactivex.Single; 18 | import io.reactivex.SingleSource; 19 | import io.reactivex.SingleTransformer; 20 | 21 | /** 22 | * Created by lam on 2/6/17. 23 | */ 24 | public abstract class BaseScheduler 25 | implements ObservableTransformer, 26 | SingleTransformer, 27 | MaybeTransformer, 28 | CompletableTransformer, 29 | FlowableTransformer { 30 | 31 | private final Scheduler subscribeOnScheduler; 32 | 33 | private final Scheduler observeOnScheduler; 34 | 35 | protected BaseScheduler(Scheduler subscribeOnScheduler, Scheduler observeOnScheduler) { 36 | this.subscribeOnScheduler = subscribeOnScheduler; 37 | this.observeOnScheduler = observeOnScheduler; 38 | } 39 | 40 | @Override 41 | public CompletableSource apply(Completable upstream) { 42 | return upstream.subscribeOn(subscribeOnScheduler).observeOn(observeOnScheduler); 43 | } 44 | 45 | @Override 46 | public Publisher apply(Flowable upstream) { 47 | return upstream.subscribeOn(subscribeOnScheduler).observeOn(observeOnScheduler); 48 | } 49 | 50 | @Override 51 | public MaybeSource apply(Maybe upstream) { 52 | return upstream.subscribeOn(subscribeOnScheduler).observeOn(observeOnScheduler); 53 | } 54 | 55 | @Override 56 | public ObservableSource apply(Observable upstream) { 57 | return upstream.subscribeOn(subscribeOnScheduler).observeOn(observeOnScheduler); 58 | } 59 | 60 | @Override 61 | public SingleSource apply(Single upstream) { 62 | return upstream.subscribeOn(subscribeOnScheduler).observeOn(observeOnScheduler); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/util/rx/scheduler/ComputationMainScheduler.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util.rx.scheduler; 2 | 3 | import io.reactivex.android.schedulers.AndroidSchedulers; 4 | import io.reactivex.schedulers.Schedulers; 5 | 6 | /** 7 | * Created by lam on 2/6/17. 8 | */ 9 | public class ComputationMainScheduler extends BaseScheduler { 10 | 11 | protected ComputationMainScheduler() { 12 | super(Schedulers.computation(), AndroidSchedulers.mainThread()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/util/rx/scheduler/IoMainScheduler.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util.rx.scheduler; 2 | 3 | import io.reactivex.android.schedulers.AndroidSchedulers; 4 | import io.reactivex.schedulers.Schedulers; 5 | 6 | /** 7 | * Created by lam on 2/6/17. 8 | */ 9 | public class IoMainScheduler extends BaseScheduler { 10 | 11 | public IoMainScheduler() { 12 | super(Schedulers.io(), AndroidSchedulers.mainThread()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/util/rx/scheduler/NewThreadMainScheduler.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util.rx.scheduler; 2 | 3 | import io.reactivex.android.schedulers.AndroidSchedulers; 4 | import io.reactivex.schedulers.Schedulers; 5 | 6 | /** 7 | * Created by lam on 2/6/17. 8 | */ 9 | public class NewThreadMainScheduler extends BaseScheduler { 10 | 11 | protected NewThreadMainScheduler() { 12 | super(Schedulers.newThread(), AndroidSchedulers.mainThread()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/util/rx/scheduler/SchedulerUtils.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util.rx.scheduler; 2 | 3 | /** 4 | * Created by lam on 2/6/17. 5 | */ 6 | public class SchedulerUtils { 7 | 8 | public static IoMainScheduler ioToMain() { 9 | return new IoMainScheduler<>(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/util/rx/scheduler/SingleMainScheduler.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util.rx.scheduler; 2 | 3 | import io.reactivex.android.schedulers.AndroidSchedulers; 4 | import io.reactivex.schedulers.Schedulers; 5 | 6 | /** 7 | * Created by lam on 2/6/17. 8 | */ 9 | public class SingleMainScheduler extends BaseScheduler { 10 | 11 | protected SingleMainScheduler() { 12 | super(Schedulers.single(), AndroidSchedulers.mainThread()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/java/io/mvpstarter/sample/util/rx/scheduler/TrampolineMainScheduler.java: -------------------------------------------------------------------------------- 1 | package <%= appPackage %>.util.rx.scheduler; 2 | 3 | import io.reactivex.android.schedulers.AndroidSchedulers; 4 | import io.reactivex.schedulers.Schedulers; 5 | 6 | /** 7 | * Created by lam on 2/6/17. 8 | */ 9 | public class TrampolineMainScheduler extends BaseScheduler { 10 | 11 | protected TrampolineMainScheduler() { 12 | super(Schedulers.trampoline(), AndroidSchedulers.mainThread()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/res/layout/activity_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | 18 | 19 | 20 | 21 | 26 | 27 | 34 | 35 | 44 | 45 | 46 | 47 | <<%= appPackage %>.features.common.ErrorView 48 | android:id="@+id/view_error" 49 | android:layout_width="wrap_content" 50 | android:layout_height="match_parent" 51 | android:visibility="gone" /> 52 | 53 | 59 | 60 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 17 | 18 | 19 | 20 | 25 | 26 | 30 | 31 | 32 | 33 | <<%= appPackage %>.features.common.ErrorView 34 | android:id="@+id/view_error" 35 | android:layout_width="wrap_content" 36 | android:layout_height="match_parent" 37 | android:visibility="gone" /> 38 | 39 | 45 | 46 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/res/layout/activity_maps_sample.xml: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/res/layout/item_pokemon.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /generators/app/templates/template-java/app/src/main/res/layout/view_error.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 22 | 23 |