├── github-mvi-android
├── cache
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── res
│ │ │ │ └── values
│ │ │ │ │ └── strings.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── rightpoint
│ │ │ │ └── mvi
│ │ │ │ └── example
│ │ │ │ └── cache
│ │ │ │ └── room
│ │ │ │ ├── model
│ │ │ │ ├── CommitMetadataEntity.kt
│ │ │ │ ├── OrganizationEntity.kt
│ │ │ │ ├── OwnerEntity.kt
│ │ │ │ ├── CommitEntity.kt
│ │ │ │ └── RepoEntity.kt
│ │ │ │ ├── DatabaseModule.java
│ │ │ │ ├── dao
│ │ │ │ ├── CommitDao.kt
│ │ │ │ └── RepoDao.kt
│ │ │ │ └── AppDatabase.kt
│ │ ├── test
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── rightpoint
│ │ │ │ └── mvi
│ │ │ │ └── example
│ │ │ │ └── cache
│ │ │ │ └── room
│ │ │ │ └── ExampleUnitTest.java
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── com
│ │ │ └── rightpoint
│ │ │ └── mvi
│ │ │ └── example
│ │ │ └── cache
│ │ │ └── room
│ │ │ └── ExampleInstrumentedTest.java
│ ├── proguard-rules.pro
│ └── build.gradle
├── common
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── rightpoint
│ │ │ └── common
│ │ │ └── Extensions.kt
│ └── build.gradle
├── data
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── rightpoint
│ │ │ └── mvi
│ │ │ └── example
│ │ │ └── data
│ │ │ ├── utils
│ │ │ └── SharedPreferences.kt
│ │ │ ├── mapper
│ │ │ ├── entities
│ │ │ │ ├── CommitMetadataEntityMapper.kt
│ │ │ │ ├── OrganizationEntityMapper.kt
│ │ │ │ ├── OwnerEntityMapper.kt
│ │ │ │ ├── CommitEntityMapper.kt
│ │ │ │ └── RepoEntityMapper.kt
│ │ │ └── model
│ │ │ │ ├── OrganizationModelMapper.kt
│ │ │ │ ├── OwnerModelMapper.kt
│ │ │ │ ├── CommitModelMapper.kt
│ │ │ │ └── RepoModelMapper.kt
│ │ │ ├── injection
│ │ │ ├── DataBindings.kt
│ │ │ └── DataModule.kt
│ │ │ ├── repository
│ │ │ ├── commit
│ │ │ │ └── DefaultCommitRepository.kt
│ │ │ └── repo
│ │ │ │ └── DefaultRepoRepository.kt
│ │ │ └── NetworkBoundResource.kt
│ ├── proguard-rules.pro
│ └── build.gradle
├── device
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── res
│ │ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── test
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── rightpoint
│ │ │ │ └── mvi
│ │ │ │ └── example
│ │ │ │ └── device
│ │ │ │ └── ExampleUnitTest.java
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── com
│ │ │ └── rightpoint
│ │ │ └── mvi
│ │ │ └── example
│ │ │ └── device
│ │ │ └── ExampleInstrumentedTest.java
│ ├── proguard-rules.pro
│ └── build.gradle
├── domain
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── rightpoint
│ │ │ └── mvi
│ │ │ └── example
│ │ │ └── domain
│ │ │ ├── common
│ │ │ └── Mapper.kt
│ │ │ ├── interactor
│ │ │ ├── MaybeUseCase.kt
│ │ │ ├── SingleUseCase.kt
│ │ │ ├── FlowableUseCase.kt
│ │ │ ├── CompletableUseCase.kt
│ │ │ ├── ObservableUseCase.kt
│ │ │ ├── BaseUseCase.kt
│ │ │ ├── repo
│ │ │ │ └── GetListOfRepos.kt
│ │ │ └── commit
│ │ │ │ └── GetListOfCommits.kt
│ │ │ ├── model
│ │ │ ├── OrganizationModel.kt
│ │ │ ├── OwnerModel.kt
│ │ │ ├── CommitModel.kt
│ │ │ └── RepoModel.kt
│ │ │ ├── executors
│ │ │ └── AppExecutors.kt
│ │ │ └── repository
│ │ │ ├── commit
│ │ │ └── CommitRepository.kt
│ │ │ └── repo
│ │ │ └── RepoRepository.kt
│ └── build.gradle
├── remote
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── rightpoint
│ │ │ │ └── mvi
│ │ │ │ └── example
│ │ │ │ └── remote
│ │ │ │ ├── model
│ │ │ │ ├── Organization.kt
│ │ │ │ ├── Owner.kt
│ │ │ │ ├── Commit.kt
│ │ │ │ └── Repo.kt
│ │ │ │ ├── AppJsonAdapterFactory.kt
│ │ │ │ ├── GithubApi.kt
│ │ │ │ └── NetworkModule.kt
│ │ ├── debug
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── rightpoint
│ │ │ │ └── mvi
│ │ │ │ └── example
│ │ │ │ └── remote
│ │ │ │ └── NetworkSettings.java
│ │ └── release
│ │ │ └── java
│ │ │ └── com
│ │ │ └── rightpoint
│ │ │ └── mvi
│ │ │ └── example
│ │ │ └── remote
│ │ │ └── NetworkSettings.java
│ └── build.gradle
├── common-android
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ └── values
│ │ │ │ │ └── strings.xml
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── rightpoint
│ │ │ │ └── common
│ │ │ │ └── android
│ │ │ │ └── list
│ │ │ │ ├── BaseViewHolder.kt
│ │ │ │ ├── Item.kt
│ │ │ │ ├── ItemDiffCallback.kt
│ │ │ │ ├── BaseAdapter.kt
│ │ │ │ └── BindableAdapter.kt
│ │ ├── test
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── rightpoint
│ │ │ │ └── common
│ │ │ │ └── android
│ │ │ │ └── ExampleUnitTest.java
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── com
│ │ │ └── rightpoint
│ │ │ └── common
│ │ │ └── android
│ │ │ └── ExampleInstrumentedTest.java
│ ├── proguard-rules.pro
│ └── build.gradle
├── app
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── values
│ │ │ │ │ ├── styles.xml
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ ├── themes.xml
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ └── dimens.xml
│ │ │ │ ├── font
│ │ │ │ │ ├── firacode_bold.ttf
│ │ │ │ │ ├── firacode_light.ttf
│ │ │ │ │ ├── firacode_medium.ttf
│ │ │ │ │ └── firacode_regular.ttf
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── layout
│ │ │ │ │ ├── activity_main.xml
│ │ │ │ │ ├── fragment_repo_list.xml
│ │ │ │ │ ├── fragment_repo_list_loading.xml
│ │ │ │ │ ├── list_item_repo.xml
│ │ │ │ │ ├── layout_latest_commit.xml
│ │ │ │ │ └── list_item_repo_grid.xml
│ │ │ │ ├── xml
│ │ │ │ │ └── repo_list_layout_states.xml
│ │ │ │ └── drawable
│ │ │ │ │ ├── ic_error_outline.xml
│ │ │ │ │ └── ic_code.xml
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── rightpoint
│ │ │ │ │ └── mvi
│ │ │ │ │ └── example
│ │ │ │ │ ├── repo
│ │ │ │ │ ├── Action.kt
│ │ │ │ │ ├── State.kt
│ │ │ │ │ ├── injection
│ │ │ │ │ │ ├── RepoBindings.kt
│ │ │ │ │ │ └── RepoModule.kt
│ │ │ │ │ ├── model
│ │ │ │ │ │ ├── RepoItemMapper.kt
│ │ │ │ │ │ └── RepoItem.kt
│ │ │ │ │ ├── RepoListViewModel.kt
│ │ │ │ │ └── RepoListFragment.kt
│ │ │ │ │ ├── commit
│ │ │ │ │ ├── Action.kt
│ │ │ │ │ ├── State.kt
│ │ │ │ │ ├── injection
│ │ │ │ │ │ ├── CommitBindings.kt
│ │ │ │ │ │ └── CommitModule.kt
│ │ │ │ │ ├── CommitItem.kt
│ │ │ │ │ └── CommitViewModel.kt
│ │ │ │ │ ├── ExampleGlideModule.kt
│ │ │ │ │ ├── injection
│ │ │ │ │ ├── AppBindings.kt
│ │ │ │ │ └── AppComponent.kt
│ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ └── GithubExampleApp.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── androidTest
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── rightpoint
│ │ │ │ └── mvi
│ │ │ │ └── example
│ │ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── debug
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── rightpoint
│ │ │ │ └── mvi
│ │ │ │ └── example
│ │ │ │ └── injection
│ │ │ │ └── BuildTypeModule.java
│ │ └── release
│ │ │ └── java
│ │ │ └── com
│ │ │ └── rightpoint
│ │ │ └── mvi
│ │ │ └── example
│ │ │ └── injection
│ │ │ └── BuildTypeModule.java
│ └── proguard-rules.pro
├── .gitignore
├── gradle
│ ├── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ └── quality.gradle
├── settings.gradle
├── README.md
├── docs
│ └── dependency_configuration.md
├── gradle.properties
└── gradlew.bat
├── {{ cookiecutter.repo_name }}
├── cache
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ │ └── java
│ │ │ └── {{ cookiecutter.package_dir }}
│ │ │ └── cache
│ │ │ └── room
│ │ │ ├── AppDatabase.kt
│ │ │ ├── DatabaseModule.kt
│ │ │ └── model
│ │ │ └── ToBeDeleted.kt
│ ├── proguard-rules.pro
│ └── build.gradle
├── common
│ ├── .gitignore
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ └── java
│ │ └── {{ cookiecutter.package_dir }}
│ │ └── util
│ │ └── Extensions.kt
├── data
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── {{ cookiecutter.package_dir }}
│ │ │ └── data
│ │ │ ├── injection
│ │ │ └── DataModule.kt
│ │ │ └── NetworkBoundResource.kt
│ ├── build.gradle
│ └── proguard-rules.pro
├── device
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ ├── build.gradle
│ └── proguard-rules.pro
├── domain
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ └── java
│ │ │ └── {{ cookiecutter.package_dir }}
│ │ │ └── domain
│ │ │ ├── interactor
│ │ │ ├── MaybeUseCase.kt
│ │ │ ├── SingleUseCase.kt
│ │ │ ├── FlowableUseCase.kt
│ │ │ ├── CompletableUseCase.kt
│ │ │ ├── ObservableUseCase.kt
│ │ │ └── BaseUseCase.kt
│ │ │ └── executors
│ │ │ └── AppExecutors.kt
│ └── build.gradle
├── remote
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── {{ cookiecutter.package_dir }}
│ │ │ │ └── remote
│ │ │ │ └── NetworkModule.kt
│ │ ├── debug
│ │ │ └── java
│ │ │ │ └── {{ cookiecutter.package_dir }}
│ │ │ │ └── remote
│ │ │ │ └── NetworkSettings.kt
│ │ └── release
│ │ │ └── java
│ │ │ └── {{ cookiecutter.package_dir }}
│ │ │ └── remote
│ │ │ └── NetworkSettings.kt
│ └── build.gradle
├── app
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── values
│ │ │ │ │ ├── styles.xml
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ ├── themes.xml
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ ├── dimens.xml
│ │ │ │ │ ├── themes_base.xml
│ │ │ │ │ └── text_appearances.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ └── layout
│ │ │ │ │ └── activity_main.xml
│ │ │ ├── java
│ │ │ │ └── {{ cookiecutter.package_dir }}
│ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ ├── AppComponent.kt
│ │ │ │ │ └── {{ cookiecutter.app_name | replace(' ', '') }}App.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── release
│ │ │ └── java
│ │ │ │ └── {{ cookiecutter.package_dir }}
│ │ │ │ └── BuildTypeModule.kt
│ │ ├── debug
│ │ │ └── java
│ │ │ │ └── {{ cookiecutter.package_dir }}
│ │ │ │ └── BuildTypeModule.kt
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── {{ cookiecutter.package_dir }}
│ │ │ └── ExampleInstrumentedTest.kt
│ └── proguard-rules.pro
├── gradle
│ ├── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ └── coverage.gradle
├── Gemfile
├── settings.gradle
├── buildSrc
│ ├── src
│ │ └── main
│ │ │ └── java
│ │ │ ├── utils
│ │ │ └── Project.kt
│ │ │ ├── extensions
│ │ │ ├── BaseExtension.kt
│ │ │ ├── LibraryExtension.kt
│ │ │ └── AppExtension.kt
│ │ │ └── plugins
│ │ │ ├── KotlinConfigPlugin.kt
│ │ │ ├── KtlintConfigPlugin.kt
│ │ │ ├── AndroidConfigPlugin.kt
│ │ │ └── AppCenterConfigPlugin.kt
│ └── build.gradle.kts
├── .gitignore
├── Dangerfile-dependencies
├── README.md
├── Dangerfile-ktlint
├── Dangerfile-lint
├── docs
│ ├── dependency_configuration.md
│ └── code_coverage.md
├── lint
│ ├── src
│ │ ├── main
│ │ │ └── java
│ │ │ │ └── {{ cookiecutter.package_dir }}
│ │ │ │ └── lint
│ │ │ │ ├── InternalIssueRegistry.kt
│ │ │ │ ├── DefaultLayoutAttributeDetector.kt
│ │ │ │ ├── InvalidImportDetector.kt
│ │ │ │ ├── TodoDetector.kt
│ │ │ │ └── NamingPatternDetector.kt
│ │ └── test
│ │ │ └── java
│ │ │ └── {{ cookiecutter.package_dir }}
│ │ │ └── lint
│ │ │ ├── InvalidImportTest.kt
│ │ │ └── DefaultLayoutAttributeTest.kt
│ └── build.gradle
├── report-merger.py
├── gradle.properties
├── Gemfile.lock
├── build.gradle
└── gradlew.bat
├── .gitignore
├── documentation
└── studio_file_menu.png
├── local_test.sh
├── ci_script.sh
├── cookiecutter.json
├── contributing.md
├── .circleci
└── config.yml
└── hooks
├── pre_gen_project.py
└── post_gen_project.sh
/github-mvi-android/cache/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/github-mvi-android/common/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/github-mvi-android/data/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/github-mvi-android/device/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/github-mvi-android/domain/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/github-mvi-android/remote/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/github-mvi-android/common-android/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/cache/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/common/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/data/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/device/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/domain/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/remote/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/github-mvi-android/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /develop
3 | /sprint
4 | /production
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /develop
3 | /sprint
4 | /production
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | .idea
4 | .DS_Store
5 | /build
6 | /captures
7 | .externalNativeBuild
8 |
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/github-mvi-android/remote/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/github-mvi-android/device/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/data/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/remote/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/documentation/studio_file_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/documentation/studio_file_menu.png
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/cache/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/device/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | cache
3 |
4 |
--------------------------------------------------------------------------------
/github-mvi-android/device/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | device
3 |
4 |
--------------------------------------------------------------------------------
/github-mvi-android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | .idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/cache/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | cache
3 |
4 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/device/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | device
3 |
4 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/github-mvi-android/common-android/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | common_android
3 |
4 |
--------------------------------------------------------------------------------
/github-mvi-android/common/src/main/java/com/rightpoint/common/Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.common
2 |
3 | val T.exhaustive: T
4 | get() = this
--------------------------------------------------------------------------------
/github-mvi-android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | {{ cookiecutter.app_name }}
3 |
4 |
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/font/firacode_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/font/firacode_bold.ttf
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/font/firacode_light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/font/firacode_light.ttf
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/font/firacode_medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/font/firacode_medium.ttf
--------------------------------------------------------------------------------
/local_test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | cp -a ../android-template/. "${TMPDIR}android-template/"
4 |
5 | cd $TMPDIR
6 |
7 | cookiecutter "${TMPDIR}android-template/" --no-input
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/font/firacode_regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/font/firacode_regular.ttf
--------------------------------------------------------------------------------
/github-mvi-android/common-android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/github-mvi-android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':common-android', ':common'
2 | include ':data'
3 | include ':device'
4 | include ':domain'
5 | include ':remote'
6 | include ':cache'
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/{{ cookiecutter.repo_name }}/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | source "https://rubygems.org"
3 |
4 | gem "danger"
5 | gem "danger-android_lint"
6 | gem "danger-checkstyle_format"
7 |
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/common/Mapper.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.common
2 |
3 | interface Mapper {
4 | fun map(t: T): R
5 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/github-mvi-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Github MVI
3 | Latest Commit
4 |
5 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | include ':data'
3 | include ':device'
4 | include ':domain'
5 | include ':remote'
6 | include ':cache'
7 | include ':lint'
8 | include ':common'
9 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/repo/Action.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.repo
2 |
3 | sealed class Action {
4 | data class LoadListOfRepos(val organization: String) : Action()
5 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/android-template/HEAD/{{ cookiecutter.repo_name }}/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/commit/Action.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.commit
2 |
3 | sealed class Action {
4 | data class LoadListOfCommits(val owner: String, val repo: String) : Action()
5 | }
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/interactor/MaybeUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.interactor
2 |
3 | import io.reactivex.Maybe
4 |
5 | abstract class MaybeUseCase : BaseUseCase>()
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/buildSrc/src/main/java/utils/Project.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import org.gradle.api.Project
4 |
5 | fun Project.propOrEmpty(name: String): String {
6 | return if (hasProperty(name)) findProperty(name)!!.toString() else ""
7 | }
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/interactor/SingleUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.interactor
2 |
3 | import io.reactivex.Single
4 |
5 | abstract class SingleUseCase : BaseUseCase>()
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches/build_file_checksums.ser
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | .DS_Store
9 | /build
10 | /captures
11 | .externalNativeBuild
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/interactor/FlowableUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.interactor
2 |
3 | import io.reactivex.Flowable
4 |
5 | abstract class FlowableUseCase : BaseUseCase>()
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/interactor/CompletableUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.interactor
2 |
3 | import io.reactivex.Completable
4 |
5 | abstract class CompletableUseCase : BaseUseCase()
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/interactor/ObservableUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.interactor
2 |
3 | import io.reactivex.Observable
4 |
5 | abstract class ObservableUseCase : BaseUseCase>()
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/domain/src/main/java/{{ cookiecutter.package_dir }}/domain/interactor/MaybeUseCase.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.domain.interactor
2 |
3 | import io.reactivex.Maybe
4 |
5 | abstract class MaybeUseCase : BaseUseCase>()
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/domain/src/main/java/{{ cookiecutter.package_dir }}/domain/interactor/SingleUseCase.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.domain.interactor
2 |
3 | import io.reactivex.Single
4 |
5 | abstract class SingleUseCase : BaseUseCase>()
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/domain/src/main/java/{{ cookiecutter.package_dir }}/domain/interactor/FlowableUseCase.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.domain.interactor
2 |
3 | import io.reactivex.Flowable
4 |
5 | abstract class FlowableUseCase : BaseUseCase>()
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/ExampleGlideModule.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example
2 |
3 | import com.bumptech.glide.annotation.GlideModule
4 | import com.bumptech.glide.module.AppGlideModule
5 |
6 | @GlideModule
7 | class ExampleGlideModule : AppGlideModule()
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/Dangerfile-dependencies:
--------------------------------------------------------------------------------
1 | fileContent = File.open("./build/dependencyUpdates/report.txt", "rb").read
2 | toRemove = "The following dependencies have later milestone versions:”
3 | contentToPrint = fileContent.slice(fileContent.index(toRemove)..-1)
4 |
5 | warn(contentToPrint)
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/common/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.Libraries
2 |
3 | apply plugin: 'kotlin-config'
4 | apply plugin: 'ktlint-config'
5 |
6 | dependencies {
7 | // Kotlin
8 | implementation Libraries.kotlinStdlib
9 |
10 | testImplementation Libraries.junit
11 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/domain/src/main/java/{{ cookiecutter.package_dir }}/domain/interactor/CompletableUseCase.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.domain.interactor
2 |
3 | import io.reactivex.Completable
4 |
5 | abstract class CompletableUseCase : BaseUseCase()
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/domain/src/main/java/{{ cookiecutter.package_dir }}/domain/interactor/ObservableUseCase.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.domain.interactor
2 |
3 | import io.reactivex.Observable
4 |
5 | abstract class ObservableUseCase : BaseUseCase>()
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/github-mvi-android/common-android/src/main/java/com/rightpoint/common/android/list/BaseViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.common.android.list
2 |
3 | import android.view.View
4 | import androidx.recyclerview.widget.RecyclerView
5 |
6 | open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
--------------------------------------------------------------------------------
/github-mvi-android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 13:22:22 PDT 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
7 |
--------------------------------------------------------------------------------
/github-mvi-android/remote/src/main/java/com/rightpoint/mvi/example/remote/model/Organization.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.remote.model
2 |
3 | data class Organization(
4 | val login: String,
5 | val id: Long,
6 | val avatar_url: String,
7 | val url: String,
8 | val html_url: String
9 | )
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ci_script.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | [[ -z "$CIRCLE_BRANCH" ]] && branch='develop' || branch="$CIRCLE_BRANCH"
4 |
5 | echo ${branch}
6 |
7 | cookiecutter git@github.com:Rightpoint/android-template.git --checkout ${branch} --no-input
8 |
9 | cd project-name-android
10 |
11 | ./gradlew lint:test assembleDebug
12 |
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/model/OrganizationModel.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.model
2 |
3 | data class OrganizationModel(
4 | val login: String,
5 | val id: Long,
6 | val avatarUrl: String,
7 | val url: String,
8 | val htmlUrl: String
9 | )
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 13:22:22 PDT 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/github-mvi-android/README.md:
--------------------------------------------------------------------------------
1 | # Github MVI
2 |
3 | [][develop-hockey]
4 | [][sprint-hockey]
5 |
6 | [develop-hockey]: https://rink.hockeyapp.net/apps/
7 | [sprint-hockey]: https://rink.hockeyapp.net/apps/
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/executors/AppExecutors.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.executors
2 |
3 | import io.reactivex.Scheduler
4 |
5 | interface AppExecutors {
6 | fun diskIo(): Scheduler
7 | fun networkIo(): Scheduler
8 | fun mainThread(): Scheduler
9 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/commit/State.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.commit
2 |
3 | sealed class State {
4 | object Loading : State()
5 | data class Loaded(val item: CommitItem) : State()
6 | object Empty : State()
7 | data class Error(val error: Throwable) : State()
8 | }
--------------------------------------------------------------------------------
/github-mvi-android/remote/src/main/java/com/rightpoint/mvi/example/remote/model/Owner.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.remote.model
2 |
3 | data class Owner(
4 | val login: String,
5 | val id: Long,
6 | val avatar_url: String,
7 | val url: String,
8 | val html_url: String,
9 | val type: String
10 | )
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/model/OwnerModel.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.model
2 |
3 | data class OwnerModel(
4 | val login: String,
5 | val id: Long,
6 | val avatarUrl: String,
7 | val url: String,
8 | val htmlUrl: String,
9 | val type: String
10 | )
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/domain/src/main/java/{{ cookiecutter.package_dir }}/domain/executors/AppExecutors.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.domain.executors
2 |
3 | import io.reactivex.Scheduler
4 |
5 | interface AppExecutors {
6 | fun diskIo(): Scheduler
7 | fun networkIo(): Scheduler
8 | fun mainThread(): Scheduler
9 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/README.md:
--------------------------------------------------------------------------------
1 | # {{ cookiecutter.app_name }}
2 |
3 | [][develop-hockey]
4 | [][sprint-hockey]
5 |
6 | [develop-hockey]: https://rink.hockeyapp.net/apps/
7 | [sprint-hockey]: https://rink.hockeyapp.net/apps/
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @color/white
4 | #303F9F
5 | #FF4081
6 | #000000
7 | #ffffff
8 |
9 |
--------------------------------------------------------------------------------
/github-mvi-android/gradle/quality.gradle:
--------------------------------------------------------------------------------
1 | android {
2 | lintOptions {
3 | ignoreTestSources true
4 | abortOnError true
5 | xmlReport false
6 | htmlReport true
7 | htmlOutput file("${project.buildDir}/reports/lint/lint-result.html")
8 | xmlOutput file("${project.buildDir}/reports/lint/lint-result.xml")
9 | }
10 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #000000
7 | #ffffff
8 |
9 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/buildSrc/src/main/java/extensions/BaseExtension.kt:
--------------------------------------------------------------------------------
1 | package extensions
2 |
3 | import com.android.build.gradle.BaseExtension
4 | import org.gradle.api.Project
5 |
6 | fun BaseExtension.configureTestOptions(project: Project) {
7 | testOptions.apply {
8 | reportDir = "${project.rootDir}/reports/${project.displayName}"
9 | }
10 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/repo/State.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.repo
2 |
3 | import com.rightpoint.mvi.example.repo.model.RepoItem
4 |
5 | sealed class State {
6 | object Loading : State()
7 | data class Loaded(val data: List) : State()
8 | object Empty : State()
9 | data class Error(val error: Throwable) : State()
10 | }
--------------------------------------------------------------------------------
/github-mvi-android/common/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'kotlin'
2 | apply plugin: 'kotlin-kapt'
3 |
4 | sourceCompatibility = 1.8
5 | targetCompatibility = 1.8
6 |
7 | dependencies {
8 | // Kotlin
9 | implementation libs.kotlin.stdlib
10 |
11 | testImplementation libs.junit
12 | }
13 |
14 | testReportDirName = "$rootDir/reports"
15 | testResultsDirName = "$rootDir/test-results"
16 |
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/repository/commit/CommitRepository.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.repository.commit
2 |
3 | import com.rightpoint.mvi.example.domain.model.CommitModel
4 | import io.reactivex.Single
5 |
6 | interface CommitRepository {
7 | fun getListOfCommitsByRepo(owner: String, repo: String): Single>
8 | }
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/model/CommitModel.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.model
2 |
3 | data class CommitModel(
4 | val sha: String,
5 | val message: String,
6 | val commentCount: Int,
7 | val url: String,
8 | val htmlUrl: String,
9 | val commentsUrl: String,
10 | val author: OwnerModel?,
11 | val committer: OwnerModel?
12 | )
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/remote/src/debug/java/{{ cookiecutter.package_dir }}/remote/NetworkSettings.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.remote;
2 |
3 | import dagger.Module
4 | import dagger.Provides
5 | import okhttp3.logging.HttpLoggingInterceptor.Level
6 |
7 | @Module
8 | internal object NetworkSettings {
9 | @Provides
10 | @JvmStatic
11 | fun providesLoggingLevel(): Level = Level.BODY
12 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/interactor/BaseUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.interactor
2 |
3 | abstract class BaseUseCase {
4 | operator fun invoke(params: T): R {
5 | return execute(params)
6 | }
7 |
8 | protected abstract fun execute(params: T): R
9 | }
10 |
11 | operator fun BaseUseCase.invoke(): R = this(Unit)
--------------------------------------------------------------------------------
/github-mvi-android/remote/src/debug/java/com/rightpoint/mvi/example/remote/NetworkSettings.java:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.remote;
2 |
3 | import dagger.Module;
4 | import dagger.Provides;
5 | import okhttp3.logging.HttpLoggingInterceptor.Level;
6 |
7 | @Module
8 | public abstract class NetworkSettings {
9 | @Provides
10 | static Level providesLoggingLevel() {
11 | return Level.BODY;
12 | }
13 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/remote/src/release/java/{{ cookiecutter.package_dir }}/remote/NetworkSettings.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.remote;
2 |
3 | import dagger.Module
4 | import dagger.Provides
5 | import okhttp3.logging.HttpLoggingInterceptor.Level
6 |
7 | @Module
8 | internal object NetworkSettings {
9 | @Provides
10 | @JvmStatic
11 | fun providesLoggingLevel(): Level = Level.NONE
12 | }
--------------------------------------------------------------------------------
/github-mvi-android/remote/src/release/java/com/rightpoint/mvi/example/remote/NetworkSettings.java:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.remote;
2 |
3 | import dagger.Module;
4 | import dagger.Provides;
5 | import okhttp3.logging.HttpLoggingInterceptor.Level;
6 |
7 | @Module
8 | public abstract class NetworkSettings {
9 | @Provides
10 | static Level providesLoggingLevel() {
11 | return Level.NONE;
12 | }
13 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/Dangerfile-ktlint:
--------------------------------------------------------------------------------
1 | # Ignore inline messages which lay outside a diff's range of PR
2 | github.dismiss_out_of_range_messages
3 |
4 | # Make it more obvious that a PR is a work in progress and shouldn't be merged yet
5 | warn("PR is classed as Work in Progress") if github.pr_title.include? "WIP"
6 |
7 | # ktlint
8 | checkstyle_format.base_path = Dir.pwd
9 | checkstyle_format.report "ktlint-report.xml"
10 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/cache/src/main/java/{{ cookiecutter.package_dir }}/cache/room/AppDatabase.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.cache.room
2 |
3 | import androidx.room.Database
4 | import androidx.room.RoomDatabase
5 | import {{ cookiecutter.package_name }}.cache.room.model.ToBeDeleted
6 |
7 | @Database(entities = [
8 | ToBeDeleted::class
9 | ], version = 1)
10 | abstract class AppDatabase : RoomDatabase() {
11 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/domain/src/main/java/{{ cookiecutter.package_dir }}/domain/interactor/BaseUseCase.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.domain.interactor
2 |
3 | abstract class BaseUseCase {
4 | operator fun invoke(params: T): R {
5 | return execute(params)
6 | }
7 |
8 | protected abstract fun execute(params: T): R
9 | }
10 |
11 | operator fun BaseUseCase.invoke(): R = this(Unit)
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/java/{{ cookiecutter.package_dir }}/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 | import android.os.Bundle
5 |
6 | class MainActivity : AppCompatActivity() {
7 |
8 | override fun onCreate(savedInstanceState: Bundle?) {
9 | super.onCreate(savedInstanceState)
10 | setContentView(R.layout.activity_main)
11 | }
12 | }
--------------------------------------------------------------------------------
/github-mvi-android/common-android/src/main/java/com/rightpoint/common/android/list/Item.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.common.android.list
2 |
3 | import androidx.annotation.LayoutRes
4 |
5 | interface Item {
6 | @LayoutRes fun layoutId(): Int
7 | fun uniqueId(): Long
8 | fun bind(holder: BaseViewHolder)
9 | fun bind(holder: BaseViewHolder, payloads: MutableList) {
10 | bind(holder)
11 | }
12 | fun spanSize(): Int = 1
13 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/domain/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.Libraries
2 |
3 | apply plugin: 'kotlin-config'
4 | apply plugin: 'ktlint-config'
5 |
6 | dependencies {
7 | // Dagger
8 | implementation Libraries.dagger
9 | kapt Libraries.daggerCompiler
10 |
11 | // Kotlin
12 | implementation Libraries.kotlinStdlib
13 |
14 | // Rx
15 | implementation Libraries.rxJava
16 |
17 | testImplementation Libraries.junit
18 | }
--------------------------------------------------------------------------------
/github-mvi-android/remote/src/main/java/com/rightpoint/mvi/example/remote/AppJsonAdapterFactory.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.remote
2 |
3 | import com.squareup.moshi.JsonAdapter
4 | import se.ansman.kotshi.KotshiJsonAdapterFactory
5 |
6 | @KotshiJsonAdapterFactory
7 | abstract class AppJsonAdapterFactory : JsonAdapter.Factory {
8 | companion object {
9 | @JvmField val INSTANCE: AppJsonAdapterFactory = KotshiAppJsonAdapterFactory()
10 | }
11 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/xml/repo_list_layout_states.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
8 |
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/commit/injection/CommitBindings.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.commit.injection
2 |
3 | import com.rightpoint.mvi.example.commit.CommitBottomSheetDialog
4 | import dagger.Module
5 | import dagger.android.ContributesAndroidInjector
6 |
7 | @Module
8 | abstract class CommitBindings {
9 | @ContributesAndroidInjector(modules = [CommitModule::class])
10 | abstract fun commitDialog(): CommitBottomSheetDialog
11 | }
--------------------------------------------------------------------------------
/github-mvi-android/remote/src/main/java/com/rightpoint/mvi/example/remote/model/Commit.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.remote.model
2 |
3 | data class Commit(
4 | val sha: String,
5 | val commit: Metadata,
6 | val url: String,
7 | val html_url: String,
8 | val comments_url: String,
9 | val author: Owner,
10 | val committer: Owner
11 | ) {
12 | data class Metadata(
13 | val message: String,
14 | val comment_count: Int
15 | )
16 | }
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/repository/repo/RepoRepository.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.repository.repo
2 |
3 | import com.rightpoint.mvi.example.domain.model.RepoModel
4 | import io.reactivex.Flowable
5 |
6 | /**
7 | * Unfortunately named interface
8 | */
9 | interface RepoRepository {
10 | fun getListOfReposByOrg(org: String): Flowable>
11 | fun getRepo(owner: String, repo: String): Flowable
12 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/injection/AppBindings.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.injection
2 |
3 | import com.rightpoint.mvi.example.MainActivity
4 | import com.rightpoint.mvi.example.repo.injection.RepoBindings
5 | import dagger.Module
6 | import dagger.android.ContributesAndroidInjector
7 |
8 | @Module
9 | abstract class AppBindings {
10 | @ContributesAndroidInjector(modules = [RepoBindings::class])
11 | abstract fun mainActivity(): MainActivity
12 | }
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/main/java/com/rightpoint/mvi/example/cache/room/model/CommitMetadataEntity.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.cache.room.model
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 |
7 | @Entity
8 | data class CommitMetadataEntity(
9 | @PrimaryKey(autoGenerate = true) val id: Long,
10 | @field:ColumnInfo(name = "message") val message: String,
11 | @field:ColumnInfo(name = "comment_count") val comment_count: Int
12 | )
--------------------------------------------------------------------------------
/cookiecutter.json:
--------------------------------------------------------------------------------
1 | {
2 | "company_name": "Beacon",
3 | "app_name": "Project Name",
4 | "repo_name": "{{ cookiecutter.app_name | replace(' ', '-') | lower }}-android",
5 | "package_name": "com.{{ cookiecutter.company_name | replace(' ', '') | replace('-', '') | lower }}.{{ cookiecutter.app_name | replace(' ', '') | replace('-', '') | lower }}",
6 | "package_dir": "{{ cookiecutter.package_name | replace('.', '/') }}",
7 | "launch_studio": ["false", "true"],
8 | "_copy_without_render": [".gradle", "gradle/", "*gradlew", "*gradlew.bat"]
9 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/Dangerfile-lint:
--------------------------------------------------------------------------------
1 | # Ignore inline messages which lay outside a diff's range of PR
2 | github.dismiss_out_of_range_messages
3 |
4 | # Make it more obvious that a PR is a work in progress and shouldn't be merged yet
5 | warn("PR is classed as Work in Progress") if github.pr_title.include? "WIP"
6 |
7 | # Android lint
8 | android_lint.gradle_task = "app:lintDevelopRelease"
9 | android_lint.report_file = "app/build/reports/lint/lint-result.xml"
10 | android_lint.filtering = true
11 | android_lint.lint(inline_mode: true)
12 |
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/repo/injection/RepoBindings.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.repo.injection
2 |
3 | import com.rightpoint.mvi.example.commit.injection.CommitBindings
4 | import com.rightpoint.mvi.example.repo.RepoListFragment
5 | import dagger.Module
6 | import dagger.android.ContributesAndroidInjector
7 |
8 | @Module
9 | abstract class RepoBindings {
10 | @ContributesAndroidInjector(modules = [RepoModule::class, CommitBindings::class])
11 | abstract fun repoListFragment(): RepoListFragment
12 | }
--------------------------------------------------------------------------------
/github-mvi-android/common-android/src/test/java/com/rightpoint/common/android/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.rightpoint.common.android;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/github-mvi-android/domain/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'kotlin'
2 | apply plugin: 'kotlin-kapt'
3 |
4 | sourceCompatibility = 1.8
5 | targetCompatibility = 1.8
6 |
7 | dependencies {
8 | // Dagger
9 | implementation libs.dagger.runtime
10 | kapt libs.dagger.compiler
11 |
12 | // Kotlin
13 | implementation libs.kotlin.stdlib
14 |
15 | // Rx
16 | implementation libs.rx.java
17 |
18 | testImplementation libs.junit
19 | }
20 |
21 | testReportDirName = "$rootDir/reports"
22 | testResultsDirName = "$rootDir/test-results"
23 |
--------------------------------------------------------------------------------
/github-mvi-android/device/src/test/java/com/rightpoint/mvi/example/device/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.device;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/test/java/com/rightpoint/mvi/example/cache/room/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.cache.room;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/github-mvi-android/common-android/src/main/java/com/rightpoint/common/android/list/ItemDiffCallback.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.common.android.list
2 |
3 | import androidx.recyclerview.widget.DiffUtil
4 | import java.util.Objects
5 |
6 | class ItemDiffCallback : DiffUtil.ItemCallback() {
7 | override fun areItemsTheSame(oldItem: I, newItem: I): Boolean {
8 | return oldItem.uniqueId() == newItem.uniqueId()
9 | }
10 |
11 | override fun areContentsTheSame(oldItem: I, newItem: I): Boolean {
12 | return Objects.equals(oldItem, newItem)
13 | }
14 | }
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/main/java/com/rightpoint/mvi/example/cache/room/DatabaseModule.java:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.cache.room;
2 |
3 | import android.app.Application;
4 |
5 | import javax.inject.Singleton;
6 |
7 | import androidx.room.Room;
8 | import dagger.Module;
9 | import dagger.Provides;
10 |
11 | @Module
12 | public abstract class DatabaseModule {
13 | @Provides
14 | @Singleton
15 | static AppDatabase providesDatabase(Application app) {
16 | return Room.databaseBuilder(app, AppDatabase.class, "github-mvi-android.db").build();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/commit/CommitItem.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.commit
2 |
3 | import android.os.Parcelable
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | @Parcelize
7 | data class CommitItem(
8 | val sha: String,
9 | val message: String,
10 | val commentCount: Int,
11 | val url: String,
12 | val htmlUrl: String,
13 | val commentsUrl: String,
14 | val authorLogin: String,
15 | val authorId: Long,
16 | val authorAvatarUrl: String,
17 | val authorUrl: String,
18 | val authorHtmlUrl: String
19 | ) : Parcelable
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/drawable/ic_error_outline.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/main/java/com/rightpoint/mvi/example/cache/room/model/OrganizationEntity.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.cache.room.model
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 |
6 | @Entity(primaryKeys = ["id"])
7 | data class OrganizationEntity(
8 | @field:ColumnInfo(name = "login") val login: String,
9 | @field:ColumnInfo(name = "id") val id: Long,
10 | @field:ColumnInfo(name = "avatar_url") val avatar_url: String,
11 | @field:ColumnInfo(name = "url") val url: String,
12 | @field:ColumnInfo(name = "html_url") val html_url: String
13 | )
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/cache/src/main/java/{{ cookiecutter.package_dir }}/cache/room/DatabaseModule.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.cache.room;
2 |
3 | import android.app.Application
4 | import androidx.room.Room
5 |
6 | import javax.inject.Singleton
7 |
8 | import dagger.Module
9 | import dagger.Provides
10 |
11 | @Module
12 | object DatabaseModule {
13 | @Provides
14 | @Singleton
15 | @JvmStatic
16 | fun providesDatabase(app: Application): AppDatabase {
17 | return Room.databaseBuilder(app, AppDatabase::class.java, "{{ cookiecutter.repo_name }}-db").build()
18 | }
19 | }
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/utils/SharedPreferences.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.utils
2 |
3 | import android.content.SharedPreferences
4 | import org.threeten.bp.Instant
5 |
6 | fun SharedPreferences.Editor.putInstant(key: String, instant: Instant): SharedPreferences.Editor {
7 | return putLong(key, instant.toEpochMilli())
8 | }
9 |
10 | fun SharedPreferences.getInstant(key: String): Instant {
11 | val milli = getLong(key, -1L)
12 | return if (milli < 0) {
13 | Instant.now()
14 | } else {
15 | Instant.ofEpochMilli(milli)
16 | }
17 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/cache/src/main/java/{{ cookiecutter.package_dir }}/cache/room/model/ToBeDeleted.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.cache.room.model
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | /**
7 | * This class is acting as a placeholder in order to compile the template project with the Room
8 | * persistence library. Please delete this class once the project has been successfully compiled.
9 | */
10 | @Deprecated("Please remove this class as it is only needed for code generation purposes.")
11 | @Entity
12 | data class ToBeDeleted(@PrimaryKey(autoGenerate = true) val id: Long)
--------------------------------------------------------------------------------
/github-mvi-android/docs/dependency_configuration.md:
--------------------------------------------------------------------------------
1 | # Dependency Configurations
2 |
3 | ## Gradle Dependency Configuration
4 |
5 | Each module contains its own `build.gradle` to declare the correct build & flavor configurations, and android project configuration.
6 | All dependency versioning is managed through a single gradle file `dependencies.gradle` to prevent multiple versions of the same dependency from being used and to keep
7 | version consistency across all project modules.
8 |
9 | When adding a new dependency, it should first be declared under `dependencies.gradle`, followed by declaring them within each module where it needs to be compiled.
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/java/{{ cookiecutter.package_dir }}/AppComponent.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}
2 |
3 | import android.app.Application
4 | import {{ cookiecutter.package_name }}.data.injection.DataModule
5 | import dagger.BindsInstance
6 | import dagger.Component
7 | import javax.inject.Singleton
8 |
9 | @Singleton
10 | @Component(modules = [
11 | BuildTypeModule::class,
12 | DataModule::class
13 | ])
14 | interface AppComponent {
15 | @Component.Builder
16 | interface Builder {
17 | @BindsInstance fun app(app: Application): Builder
18 | fun build(): AppComponent
19 | }
20 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/docs/dependency_configuration.md:
--------------------------------------------------------------------------------
1 | # Dependency Configurations
2 |
3 | ## Gradle Dependency Configuration
4 |
5 | Each module contains its own `build.gradle` to declare the correct build & flavor configurations, and android project configuration.
6 | All dependency versioning is managed through a single gradle file `dependencies.gradle` to prevent multiple versions of the same dependency from being used and to keep
7 | version consistency across all project modules.
8 |
9 | When adding a new dependency, it should first be declared under `dependencies.gradle`, followed by declaring them within each module where it needs to be compiled.
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/main/java/com/rightpoint/mvi/example/cache/room/model/OwnerEntity.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.cache.room.model
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 |
6 | @Entity(primaryKeys = ["id"])
7 | data class OwnerEntity(
8 | @field:ColumnInfo(name = "login") val login: String,
9 | @field:ColumnInfo(name = "id") val id: Long,
10 | @field:ColumnInfo(name = "avatar_url") val avatar_url: String,
11 | @field:ColumnInfo(name = "url") val url: String,
12 | @field:ColumnInfo(name = "html_url") val html_url: String,
13 | @field:ColumnInfo(name = "type") val type: String
14 | )
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/docs/code_coverage.md:
--------------------------------------------------------------------------------
1 | # Code Coverage
2 |
3 | ## Generating a Coverage Report
4 | A code coverage report can be generated using the `testUnitTestCoverage` task (e.g `testDebugUnitTestCoverage`, `testDevelopDebugUnitTestCoverage`).
5 | The generated report HTML can be found in each module's corresponding /build/reports/jacoco/ directory.
6 |
7 | ## Coverage Verification
8 |
9 | Code coverage can be verified using the `testUnitTestCoverageVerification` task.
10 | The minimum code-coverage percentage is 60% by default.
11 | This can be changed by changing the value of `MINIMUM_COVERAGE` in `coverage.gradle`
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example
2 |
3 | import android.os.Bundle
4 | import com.rightpoint.mvi.example.R
5 | import com.rightpoint.mvi.example.repo.RepoListFragment
6 | import dagger.android.support.DaggerAppCompatActivity
7 |
8 | class MainActivity : DaggerAppCompatActivity() {
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 | setContentView(R.layout.activity_main)
12 | supportFragmentManager.beginTransaction()
13 | .replace(R.id.contentFrame, RepoListFragment())
14 | .commit()
15 | }
16 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/drawable/ic_code.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 70dp
8 | 16dp
9 | 16dp
10 |
11 |
12 | 8dp
13 | 72dp
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/lint/src/main/java/{{ cookiecutter.package_dir }}/lint/InternalIssueRegistry.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.lint
2 |
3 | import com.android.tools.lint.client.api.IssueRegistry
4 | import com.android.tools.lint.detector.api.Issue
5 |
6 | /**
7 | * Custom Lint issue registry for {{ cookiecutter.package_name }}.
8 | */
9 | class InternalIssueRegistry : IssueRegistry() {
10 | override val issues: List
11 | get() = listOf(
12 | ISSUE_TODO,
13 | ISSUE_INVALID_IMPORT,
14 | ISSUE_NAMING_PATTERN,
15 | ISSUE_DEFAULT_LAYOUT_ATTRIBUTE
16 | )
17 |
18 | override val api: Int = com.android.tools.lint.detector.api.CURRENT_API
19 | }
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/model/RepoModel.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.model
2 |
3 | data class RepoModel(
4 | val id: Long,
5 | val name: String,
6 | val fullName: String,
7 | val owner: OwnerModel,
8 | val isPrivate: Boolean,
9 | val htmlUrl: String,
10 | val description: String?,
11 | val fork: Boolean,
12 | val url: String,
13 | val stargazersCount: Int,
14 | val watchersCount: Int,
15 | val language: String?,
16 | val hasIssues: Boolean,
17 | val hasProjects: Boolean,
18 | val hasDownloads: Boolean,
19 | val hasWiki: Boolean,
20 | val hasPages: Boolean,
21 | val forksCount: Int,
22 | val openIssuesCount: Int
23 | )
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/mapper/entities/CommitMetadataEntityMapper.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.mapper.entities
2 |
3 | import com.rightpoint.mvi.example.cache.room.model.CommitMetadataEntity
4 | import com.rightpoint.mvi.example.domain.common.Mapper
5 | import com.rightpoint.mvi.example.remote.model.Commit
6 | import javax.inject.Inject
7 |
8 | class CommitMetadataEntityMapper
9 | @Inject constructor() : Mapper {
10 | override fun map(t: Commit.Metadata): CommitMetadataEntity {
11 | return CommitMetadataEntity(
12 | id = 0,
13 | message = t.message,
14 | comment_count = t.comment_count
15 | )
16 | }
17 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/common/src/main/java/{{ cookiecutter.package_dir }}/util/Extensions.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.util
2 |
3 | /**
4 | * Helper to force a when statement to assert all options are matched in a when statement.
5 | *
6 | * By default, Kotlin doesn't care if all branches are handled in a when statement. However, if you
7 | * use the when statement as an expression (with a value) it will force all cases to be handled.
8 | *
9 | * This helper is to make a lightweight way to say you meant to match all of them.
10 | *
11 | * Usage:
12 | *
13 | * ```
14 | * when(sealedObject) {
15 | * is OneType -> //
16 | * is AnotherType -> //
17 | * }.exhaustive
18 | */
19 | val T.exhaustive: T
20 | get() = this
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/mapper/entities/OrganizationEntityMapper.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.mapper.entities
2 |
3 | import com.rightpoint.mvi.example.cache.room.model.OrganizationEntity
4 | import com.rightpoint.mvi.example.domain.common.Mapper
5 | import com.rightpoint.mvi.example.remote.model.Organization
6 | import javax.inject.Inject
7 |
8 | class OrganizationEntityMapper @Inject constructor() : Mapper {
9 | override fun map(t: Organization): OrganizationEntity {
10 | return OrganizationEntity(
11 | t.login,
12 | t.id,
13 | t.avatar_url,
14 | t.url,
15 | t.html_url
16 | )
17 | }
18 | }
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/mapper/entities/OwnerEntityMapper.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.mapper.entities
2 |
3 | import com.rightpoint.mvi.example.cache.room.model.OwnerEntity
4 | import com.rightpoint.mvi.example.domain.common.Mapper
5 | import com.rightpoint.mvi.example.remote.model.Owner
6 | import javax.inject.Inject
7 |
8 | class OwnerEntityMapper @Inject constructor() : Mapper {
9 | override fun map(t: Owner?): OwnerEntity? {
10 | if (t == null) return null
11 | return OwnerEntity(
12 | t.login,
13 | t.id,
14 | t.avatar_url,
15 | t.url,
16 | t.html_url,
17 | t.type
18 | )
19 | }
20 | }
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/mapper/model/OrganizationModelMapper.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.mapper.model
2 |
3 | import com.rightpoint.mvi.example.cache.room.model.OrganizationEntity
4 | import com.rightpoint.mvi.example.domain.common.Mapper
5 | import com.rightpoint.mvi.example.domain.model.OrganizationModel
6 | import javax.inject.Inject
7 |
8 | class OrganizationModelMapper
9 | @Inject constructor() : Mapper {
10 | override fun map(t: OrganizationEntity): OrganizationModel {
11 | return OrganizationModel(
12 | t.login,
13 | t.id,
14 | t.avatar_url,
15 | t.url,
16 | t.html_url
17 | )
18 | }
19 | }
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/mapper/model/OwnerModelMapper.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.mapper.model
2 |
3 | import com.rightpoint.mvi.example.cache.room.model.OwnerEntity
4 | import com.rightpoint.mvi.example.domain.common.Mapper
5 | import com.rightpoint.mvi.example.domain.model.OwnerModel
6 | import javax.inject.Inject
7 |
8 | class OwnerModelMapper @Inject constructor() : Mapper {
9 | override fun map(t: OwnerEntity?): OwnerModel? {
10 | if (t == null) return null
11 | return OwnerModel(
12 | t.login,
13 | t.id,
14 | t.avatar_url,
15 | t.url,
16 | t.html_url,
17 | t.type
18 | )
19 | }
20 | }
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/injection/DataBindings.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.injection
2 |
3 | import com.rightpoint.mvi.example.data.repository.commit.DefaultCommitRepository
4 | import com.rightpoint.mvi.example.data.repository.repo.DefaultRepoRepository
5 | import com.rightpoint.mvi.example.domain.repository.commit.CommitRepository
6 | import com.rightpoint.mvi.example.domain.repository.repo.RepoRepository
7 | import dagger.Binds
8 | import dagger.Module
9 |
10 | @Module
11 | abstract class DataBindings {
12 | @Binds
13 | abstract fun bindRepoRepository(repository: DefaultRepoRepository): RepoRepository
14 |
15 | @Binds
16 | abstract fun bindCommitRepository(repository: DefaultCommitRepository): CommitRepository
17 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/device/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.Libraries
2 |
3 | apply plugin: 'com.android.library'
4 | apply plugin: 'android-config'
5 | apply plugin: 'ktlint-config'
6 | apply from: "$rootDir/gradle/coverage.gradle"
7 |
8 | dependencies {
9 | androidTestImplementation Libraries.androidxEspressoCore
10 | androidTestImplementation Libraries.androidxEspressoContrib
11 | androidTestImplementation Libraries.androidxTestRules
12 | androidTestImplementation Libraries.androidxTestRunner
13 |
14 | // Android
15 | implementation Libraries.androidxAnnotations
16 |
17 | // Kotlin
18 | implementation Libraries.kotlinStdlib
19 |
20 | testImplementation Libraries.androidxTestExtJUnit
21 | testImplementation Libraries.androidxTestExtTruth
22 | }
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/main/java/com/rightpoint/mvi/example/cache/room/model/CommitEntity.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.cache.room.model
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Embedded
5 | import androidx.room.Entity
6 |
7 | @Entity(primaryKeys = ["sha"])
8 | data class CommitEntity(
9 | @field:ColumnInfo(name = "sha") val sha: String,
10 | @field:Embedded(prefix = "commit_") val commit: CommitMetadataEntity,
11 | @field:ColumnInfo(name = "url") val url: String,
12 | @field:ColumnInfo(name = "html_url") val html_url: String,
13 | @field:ColumnInfo(name = "comments_url") val comments_url: String,
14 | @field:Embedded(prefix = "author_") val author: OwnerEntity?,
15 | @field:Embedded(prefix = "committer_") val committer: OwnerEntity?
16 | )
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/interactor/repo/GetListOfRepos.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.interactor.repo
2 |
3 | import com.rightpoint.mvi.example.domain.interactor.FlowableUseCase
4 | import com.rightpoint.mvi.example.domain.model.RepoModel
5 | import com.rightpoint.mvi.example.domain.repository.repo.RepoRepository
6 | import io.reactivex.Flowable
7 | import javax.inject.Inject
8 |
9 | class GetListOfRepos @Inject constructor(
10 | private val repository: RepoRepository
11 | ) : FlowableUseCase>() {
12 | override fun execute(params: Params): Flowable> {
13 | return repository.getListOfReposByOrg(params.org)
14 | }
15 |
16 | data class Params(val org: String)
17 | }
--------------------------------------------------------------------------------
/github-mvi-android/remote/src/main/java/com/rightpoint/mvi/example/remote/model/Repo.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.remote.model
2 |
3 | import com.squareup.moshi.Json
4 |
5 | data class Repo(
6 | val id: Long,
7 | val name: String,
8 | val full_name: String,
9 | val owner: Owner,
10 | @Json(name = "private") val is_private: Boolean,
11 | val html_url: String,
12 | val description: String?,
13 | val fork: Boolean,
14 | val url: String,
15 | val stargazers_count: Int,
16 | val watchers_count: Int,
17 | val language: String?,
18 | val has_issues: Boolean,
19 | val has_projects: Boolean,
20 | val has_downloads: Boolean,
21 | val has_wiki: Boolean,
22 | val has_pages: Boolean,
23 | val forks_count: Int,
24 | val open_issues_count: Int
25 | )
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 70dp
8 | 16dp
9 | 16dp
10 |
11 |
12 | 8dp
13 | 72dp
14 |
15 |
16 | 8dp
17 | 4dp
18 |
19 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/report-merger.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import os
4 | import fnmatch
5 | from xml.etree import ElementTree
6 |
7 | first = None
8 | ktlintFile = 'ktlint-report.xml'
9 |
10 | for root, dir, files in os.walk("."):
11 | for items in fnmatch.filter(files, "ktlint*.xml"):
12 | print root
13 | print dir
14 | data = ElementTree.parse(root + "/" + items).getroot()
15 | if first is None:
16 | first = data
17 | else:
18 | first.extend(data)
19 | if first is not None:
20 | f = open(ktlintFile, 'w')
21 | f.write("\n")
22 | f.write(ElementTree.tostring(first))
23 | f.close()
24 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/data/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.Libraries
2 |
3 | apply plugin: 'com.android.library'
4 | apply plugin: 'android-config'
5 | apply plugin: 'ktlint-config'
6 | apply from: "$rootDir/gradle/coverage.gradle"
7 |
8 | dependencies {
9 | implementation project(":domain")
10 | api project(':cache')
11 | api project(':remote')
12 |
13 | // Android
14 | implementation Libraries.androidxAnnotations
15 |
16 | // Dagger
17 | implementation Libraries.dagger
18 | kapt Libraries.daggerCompiler
19 |
20 | // Kotlin
21 | implementation Libraries.kotlinStdlib
22 |
23 | // Rx
24 | implementation Libraries.rxJava
25 | implementation Libraries.rxAndroid
26 |
27 | testImplementation Libraries.androidxTestExtJUnit
28 | testImplementation Libraries.androidxTestExtTruth
29 | }
--------------------------------------------------------------------------------
/github-mvi-android/domain/src/main/java/com/rightpoint/mvi/example/domain/interactor/commit/GetListOfCommits.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.domain.interactor.commit
2 |
3 | import com.rightpoint.mvi.example.domain.interactor.SingleUseCase
4 | import com.rightpoint.mvi.example.domain.model.CommitModel
5 | import com.rightpoint.mvi.example.domain.repository.commit.CommitRepository
6 | import io.reactivex.Single
7 | import javax.inject.Inject
8 |
9 | class GetListOfCommits @Inject constructor(
10 | private val repository: CommitRepository
11 | ) : SingleUseCase>() {
12 | override fun execute(params: Params): Single> {
13 | return repository.getListOfCommitsByRepo(params.owner, params.repo)
14 | }
15 |
16 | data class Params(val owner: String, val repo: String)
17 | }
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | ### Opening Project
2 | 1. Clone project.
3 | 2. Open Android Studio.
4 | 3. On the welcome screen in Android Studio click the `Open an existing Android Studio project` option and point it to the cloned repo.
5 |
6 | Note: Code completion and other IDE sugar will not be functional because this is not a recognized project format.
7 |
8 | ### Testing Changes
9 | 1. Make your changes to the cookie cutter project.
10 | 2. Use [these instructions](https://github.com/Rightpoint/android-template#usage) to generate a project based on your changes.
11 |
12 | Note: Be sure to use the cookiecutter namespace e.g. `{{ cookiecutter.package_name }}` to make generated files compile properly.
13 |
14 | ### Notable Files
15 | * [Pre Project Generation Logic](./hooks/pre_gen_project.py)
16 | * [Post Project Generation Logic](./hooks/post_gen_project.sh)
17 |
--------------------------------------------------------------------------------
/github-mvi-android/remote/src/main/java/com/rightpoint/mvi/example/remote/GithubApi.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.remote
2 |
3 | import com.rightpoint.mvi.example.remote.model.Commit
4 | import com.rightpoint.mvi.example.remote.model.Repo
5 | import io.reactivex.Single
6 | import retrofit2.http.GET
7 | import retrofit2.http.Path
8 |
9 | interface GithubApi {
10 | @GET("users/{org}/repos")
11 | fun getListOfReposByOrg(@Path("org") org: String): Single>
12 |
13 | @GET("repos/{owner}/{repo}")
14 | fun getRepo(
15 | @Path("owner") owner: String,
16 | @Path("repo") repo: String
17 | ): Single
18 |
19 | @GET("repos/{owner}/{repo}/commits")
20 | fun getListOfCommitsForRepo(
21 | @Path("owner") owner: String,
22 | @Path("repo") repo: String
23 | ): Single>
24 | }
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/main/java/com/rightpoint/mvi/example/cache/room/dao/CommitDao.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.cache.room.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import com.rightpoint.mvi.example.cache.room.model.CommitEntity
8 | import io.reactivex.Flowable
9 | import io.reactivex.Single
10 |
11 | @Dao
12 | interface CommitDao {
13 | @Query("SELECT * FROM CommitEntity")
14 | fun getAll(): Flowable>
15 |
16 | @Query("SELECT * FROM CommitEntity")
17 | fun check(): Single>
18 |
19 | @Insert(onConflict = OnConflictStrategy.REPLACE)
20 | fun insert(repo: CommitEntity)
21 |
22 | @Insert(onConflict = OnConflictStrategy.REPLACE)
23 | fun insertAll(repos: List)
24 | }
--------------------------------------------------------------------------------
/github-mvi-android/cache/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/github-mvi-android/device/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/java/{{ cookiecutter.package_dir }}/{{ cookiecutter.app_name | replace(' ', '') }}App.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}
2 |
3 | import android.app.Application
4 | import android.os.StrictMode
5 | import timber.log.Timber
6 | import javax.inject.Inject
7 |
8 | class {{ cookiecutter.app_name | replace(' ', '') }}App : Application() {
9 | @Inject lateinit var threadPolicy: StrictMode.ThreadPolicy
10 | @Inject lateinit var vmPolicy: StrictMode.VmPolicy
11 | @Inject lateinit var tree: Timber.Tree
12 |
13 | val component by lazy {
14 | DaggerAppComponent.builder().app(this).build()
15 | }
16 |
17 | override fun onCreate() {
18 | super.onCreate()
19 |
20 | StrictMode.setThreadPolicy(threadPolicy)
21 | StrictMode.setVmPolicy(vmPolicy)
22 | Timber.plant(tree)
23 | }
24 | }
--------------------------------------------------------------------------------
/github-mvi-android/common-android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/cache/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/device/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/github-mvi-android/app/src/androidTest/java/com/rightpoint/mvi/example/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example
2 |
3 | import androidx.test.InstrumentationRegistry
4 | import androidx.test.runner.AndroidJUnit4
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumentation test, which will execute on an Android device.
11 | *
12 | * @see [Testing documentation](http://d.android.com/tools/testing)
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | @Throws(Exception::class)
18 | fun useAppContext() {
19 | // Context of the app under test.
20 | val appContext = InstrumentationRegistry.getTargetContext()
21 |
22 | assertEquals("com.rightpoint.mvi.example.${BuildConfig.FLAVOR}", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/repo/model/RepoItemMapper.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.repo.model
2 |
3 | import com.rightpoint.mvi.example.domain.common.Mapper
4 | import com.rightpoint.mvi.example.domain.model.RepoModel
5 |
6 | class RepoItemMapper(private val onCommitClick: ((String) -> Unit)?) : Mapper {
7 | override fun map(t: RepoModel): RepoItem {
8 | return RepoItem(
9 | id = t.id,
10 | name = t.name,
11 | description = t.description,
12 | ownerAvatarUrl = t.owner.avatarUrl,
13 | htmlUrl = t.htmlUrl,
14 | forks = t.forksCount,
15 | stars = t.stargazersCount,
16 | watchers = t.watchersCount,
17 | openIssues = t.openIssuesCount,
18 | onCommitClick = onCommitClick
19 | )
20 | }
21 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/lint/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.Libraries
2 |
3 | apply plugin: 'kotlin-config'
4 | apply plugin: 'ktlint-config'
5 |
6 | dependencies {
7 | compileOnly Libraries.androidLintApi
8 | compileOnly Libraries.androidLintChecks
9 | testImplementation Libraries.junit
10 | testImplementation Libraries.androidLintCore
11 | testImplementation Libraries.androidLintTest
12 | testImplementation Libraries.androidLintTestUtils
13 | }
14 |
15 | jar {
16 | manifest {
17 | attributes("Lint-Registry-v2": "{{ cookiecutter.package_name }}.lint.InternalIssueRegistry")
18 | }
19 | }
20 |
21 | task copyLintJar(type: Copy) {
22 | dependsOn 'clean'
23 | dependsOn 'assemble'
24 | from "${project.buildDir.absolutePath}/libs/lint.jar"
25 | into "${System.getProperty('user.home')}/.android/lint/"
26 | tasks.findByName('assemble').mustRunAfter('clean')
27 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/injection/AppComponent.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.injection
2 |
3 | import android.app.Application
4 | import com.rightpoint.mvi.example.GithubExampleApp
5 | import com.rightpoint.mvi.example.data.injection.DataModule
6 | import dagger.BindsInstance
7 | import dagger.Component
8 | import dagger.android.AndroidInjector
9 | import dagger.android.support.AndroidSupportInjectionModule
10 | import javax.inject.Singleton
11 |
12 | @Singleton
13 | @Component(modules = [
14 | AndroidSupportInjectionModule::class,
15 | AppBindings::class,
16 | BuildTypeModule::class,
17 | DataModule::class
18 | ])
19 | interface AppComponent : AndroidInjector {
20 | @Component.Builder
21 | interface Builder {
22 | @BindsInstance fun app(app: Application): Builder
23 | fun build(): AppComponent
24 | }
25 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/buildSrc/src/main/java/plugins/KotlinConfigPlugin.kt:
--------------------------------------------------------------------------------
1 | package plugins
2 |
3 | import org.gradle.api.JavaVersion
4 | import org.gradle.api.Plugin
5 | import org.gradle.api.Project
6 | import org.gradle.api.plugins.JavaPluginConvention
7 | import org.gradle.kotlin.dsl.getByType
8 | import org.gradle.kotlin.dsl.getPlugin
9 | import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
10 |
11 | class KotlinConfigPlugin : Plugin {
12 | override fun apply(project: Project) {
13 | project.plugins.apply("kotlin")
14 | project.plugins.apply("kotlin-kapt")
15 | project.convention.getPlugin().apply {
16 | sourceCompatibility = JavaVersion.VERSION_1_8
17 | targetCompatibility = JavaVersion.VERSION_1_8
18 | testReportDirName = "${project.rootDir}/reports/${project.displayName}"
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/release/java/{{ cookiecutter.package_dir }}/BuildTypeModule.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}
2 |
3 | import android.os.StrictMode
4 |
5 | import dagger.Module
6 | import dagger.Provides
7 | import timber.log.Timber
8 |
9 | @Module
10 | internal object BuildTypeModule {
11 | @Provides
12 | @JvmStatic
13 | fun providesThreadPolicy(): StrictMode.ThreadPolicy {
14 | return StrictMode.ThreadPolicy.LAX
15 | }
16 |
17 | @Provides
18 | @JvmStatic
19 | fun providesVmPolicy(): StrictMode.VmPolicy {
20 | return StrictMode.VmPolicy.LAX
21 | }
22 |
23 | @Provides
24 | @JvmStatic
25 | fun providesTree(): Timber.Tree {
26 | return object : Timber.Tree() {
27 | override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
28 | // Do nothing
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/github-mvi-android/app/src/debug/java/com/rightpoint/mvi/example/injection/BuildTypeModule.java:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.injection;
2 |
3 | import android.os.StrictMode;
4 |
5 | import dagger.Module;
6 | import dagger.Provides;
7 | import timber.log.Timber;
8 |
9 | @Module
10 | abstract class BuildTypeModule {
11 | @Provides
12 | static StrictMode.ThreadPolicy providesThreadPolicy() {
13 | return new StrictMode.ThreadPolicy.Builder()
14 | .detectAll()
15 | .penaltyLog()
16 | .build();
17 | }
18 |
19 | @Provides
20 | static StrictMode.VmPolicy providesVmPolicy() {
21 | return new StrictMode.VmPolicy.Builder()
22 | .detectAll()
23 | .penaltyLog()
24 | .build();
25 | }
26 |
27 | @Provides
28 | static Timber.Tree providesTree() {
29 | return new Timber.DebugTree();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/debug/java/{{ cookiecutter.package_dir }}/BuildTypeModule.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }};
2 |
3 | import android.os.StrictMode
4 |
5 | import dagger.Module
6 | import dagger.Provides
7 | import timber.log.Timber
8 |
9 | @Module
10 | internal object BuildTypeModule {
11 | @Provides
12 | @JvmStatic
13 | fun providesThreadPolicy(): StrictMode.ThreadPolicy {
14 | return StrictMode.ThreadPolicy.Builder()
15 | .detectAll()
16 | .penaltyLog()
17 | .build()
18 | }
19 |
20 | @Provides
21 | @JvmStatic
22 | fun providesVmPolicy(): StrictMode.VmPolicy {
23 | return StrictMode.VmPolicy.Builder()
24 | .detectAll()
25 | .penaltyLog()
26 | .build()
27 | }
28 |
29 | @Provides
30 | @JvmStatic
31 | fun providesTree(): Timber.Tree {
32 | return Timber.DebugTree()
33 | }
34 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/androidTest/java/{{ cookiecutter.package_dir }}/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumentation test, which will execute on an Android device.
11 | *
12 | * @see [Testing documentation](http://d.android.com/tools/testing)
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | @Throws(Exception::class)
18 | fun useAppContext() {
19 | // Context of the app under test.
20 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
21 |
22 | assertEquals("{{ cookiecutter.package_name }}.${BuildConfig.FLAVOR}", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/github-mvi-android/common-android/src/androidTest/java/com/rightpoint/common/android/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.rightpoint.common.android;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.rightpoint.common.android.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/github-mvi-android/device/src/androidTest/java/com/rightpoint/mvi/example/device/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.device;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.assertEquals;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.rightpoint.mvi.example.device.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/buildSrc/src/main/java/plugins/KtlintConfigPlugin.kt:
--------------------------------------------------------------------------------
1 | package plugins
2 |
3 | import dependencies.Config
4 | import dependencies.Versions
5 | import org.gradle.api.Plugin
6 | import org.gradle.api.Project
7 | import org.jlleitschuh.gradle.ktlint.KtlintExtension
8 | import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
9 |
10 | class KtlintConfigPlugin : Plugin {
11 | override fun apply(target: Project) {
12 | target.plugins.apply("org.jlleitschuh.gradle.ktlint")
13 | val extension = target.extensions.findByType(KtlintExtension::class.java)
14 | extension?.configure()
15 | }
16 |
17 | private fun KtlintExtension.configure() {
18 | version.set(Versions.ktlint)
19 | android.set(true)
20 | outputToConsole.set(true)
21 | reporters.set(listOf(ReporterType.CHECKSTYLE, ReporterType.JSON))
22 | ignoreFailures.set(Config.isCiBuild)
23 | verbose.set(true)
24 | }
25 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/remote/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.Libraries
2 |
3 | apply plugin: 'com.android.library'
4 | apply plugin: 'android-config'
5 | apply plugin: 'ktlint-config'
6 | apply from: "$rootDir/gradle/coverage.gradle"
7 |
8 | dependencies {
9 | // Kotlin
10 | implementation Libraries.kotlinStdlib
11 |
12 | // Dagger
13 | implementation Libraries.dagger
14 | kapt Libraries.daggerCompiler
15 |
16 | // OkHttp
17 | api Libraries.okhttp
18 | api Libraries.okhttpLogging
19 |
20 | // Retrofit
21 | api Libraries.retrofit
22 | api Libraries.retrofitRxJavaAdapter
23 | api Libraries.retrofitMoshiConverter
24 |
25 | // Moshi
26 | implementation Libraries.moshi
27 |
28 | // RxJava
29 | implementation Libraries.rxJava
30 |
31 | // Timber
32 | implementation Libraries.timber
33 |
34 | testImplementation Libraries.androidxTestExtJUnit
35 | testImplementation Libraries.androidxTestExtTruth
36 | }
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/mapper/model/CommitModelMapper.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.mapper.model
2 |
3 | import com.rightpoint.mvi.example.cache.room.model.CommitEntity
4 | import com.rightpoint.mvi.example.domain.common.Mapper
5 | import com.rightpoint.mvi.example.domain.model.CommitModel
6 | import javax.inject.Inject
7 |
8 | class CommitModelMapper @Inject constructor(
9 | private val ownerMapper: OwnerModelMapper
10 | ) : Mapper {
11 | override fun map(t: CommitEntity): CommitModel {
12 | return CommitModel(
13 | sha = t.sha,
14 | message = t.commit.message,
15 | commentCount = t.commit.comment_count,
16 | url = t.url,
17 | htmlUrl = t.html_url,
18 | commentsUrl = t.comments_url,
19 | author = ownerMapper.map(t.author),
20 | committer = ownerMapper.map(t.committer)
21 | )
22 | }
23 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/androidTest/java/com/rightpoint/mvi/example/cache/room/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.cache.room;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.assertEquals;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.rightpoint.mvi.example.cache.room.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/mapper/entities/CommitEntityMapper.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.mapper.entities
2 |
3 | import com.rightpoint.mvi.example.cache.room.model.CommitEntity
4 | import com.rightpoint.mvi.example.domain.common.Mapper
5 | import com.rightpoint.mvi.example.remote.model.Commit
6 | import javax.inject.Inject
7 |
8 | class CommitEntityMapper @Inject constructor(
9 | private val metadataMapper: CommitMetadataEntityMapper,
10 | private val ownerMapper: OwnerEntityMapper
11 | ) : Mapper {
12 | override fun map(t: Commit): CommitEntity {
13 | return CommitEntity(
14 | sha = t.sha,
15 | commit = metadataMapper.map(t.commit),
16 | url = t.url,
17 | html_url = t.html_url,
18 | comments_url = t.comments_url,
19 | author = ownerMapper.map(t.author),
20 | committer = ownerMapper.map(t.committer)
21 | )
22 | }
23 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
14 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/main/java/com/rightpoint/mvi/example/cache/room/AppDatabase.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.cache.room
2 |
3 | import androidx.room.Database
4 | import androidx.room.RoomDatabase
5 | import com.rightpoint.mvi.example.cache.room.dao.CommitDao
6 | import com.rightpoint.mvi.example.cache.room.dao.RepoDao
7 | import com.rightpoint.mvi.example.cache.room.model.CommitEntity
8 | import com.rightpoint.mvi.example.cache.room.model.CommitMetadataEntity
9 | import com.rightpoint.mvi.example.cache.room.model.OrganizationEntity
10 | import com.rightpoint.mvi.example.cache.room.model.OwnerEntity
11 | import com.rightpoint.mvi.example.cache.room.model.RepoEntity
12 |
13 | @Database(entities = [
14 | RepoEntity::class,
15 | OwnerEntity::class,
16 | OrganizationEntity::class,
17 | CommitEntity::class,
18 | CommitMetadataEntity::class
19 | ], version = 1)
20 | abstract class AppDatabase : RoomDatabase() {
21 | abstract fun repoDao(): RepoDao
22 | abstract fun commitDao(): CommitDao
23 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/buildSrc/src/main/java/extensions/LibraryExtension.kt:
--------------------------------------------------------------------------------
1 | package extensions
2 |
3 | import com.android.build.gradle.LibraryExtension
4 | import org.gradle.api.JavaVersion
5 |
6 | fun LibraryExtension.configure() {
7 | setCompileSdkVersion(28)
8 | defaultConfig {
9 | setMinSdkVersion(23)
10 | setTargetSdkVersion(28)
11 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
12 | consumerProguardFiles("consumer-rules.pro")
13 | }
14 | buildTypes {
15 | getByName("debug") {
16 | isMinifyEnabled = false
17 | isTestCoverageEnabled = true
18 | }
19 | getByName("release") {
20 | isMinifyEnabled = true
21 | proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
22 | }
23 | }
24 | compileOptions {
25 | setSourceCompatibility(JavaVersion.VERSION_1_8)
26 | setTargetCompatibility(JavaVersion.VERSION_1_8)
27 | }
28 | }
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/main/java/com/rightpoint/mvi/example/cache/room/dao/RepoDao.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.cache.room.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import com.rightpoint.mvi.example.cache.room.model.RepoEntity
8 | import io.reactivex.Flowable
9 | import io.reactivex.Single
10 |
11 | @Dao
12 | interface RepoDao {
13 | @Query("SELECT * FROM RepoEntity")
14 | fun getAll(): Flowable>
15 |
16 | @Query("SELECT * FROM RepoEntity WHERE owner_login = :org")
17 | fun getReposByOrg(org: String): Flowable>
18 |
19 | @Query("SELECT * FROM RepoEntity WHERE owner_login = :org")
20 | fun checkReposByOrg(org: String): Single>
21 |
22 | @Insert(onConflict = OnConflictStrategy.REPLACE)
23 | fun insert(repo: RepoEntity)
24 |
25 | @Insert(onConflict = OnConflictStrategy.REPLACE)
26 | fun insertAll(repos: List)
27 | }
--------------------------------------------------------------------------------
/github-mvi-android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | org.gradle.parallel=true
18 | org.gradle.caching=true
19 |
20 | # Enable D8
21 | android.enableD8=true
22 |
23 | # Disable Configuration on Demand
24 | org.gradle.configureondemand=false
25 |
26 | # Enable AndroidX
27 | android.useAndroidX=true
28 | android.enableJetifier=true
--------------------------------------------------------------------------------
/github-mvi-android/data/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 /Users/priyakarambelkar/Library/Android/sdk/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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | org.gradle.parallel=true
18 | org.gradle.caching=true
19 |
20 | # Enable D8
21 | android.enableD8=true
22 |
23 | # Disable Configuration on Demand
24 | org.gradle.configureondemand=false
25 |
26 | # Enable AndroidX
27 | android.useAndroidX=true
28 | android.enableJetifier=true
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/data/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 /Users/priyakarambelkar/Library/Android/sdk/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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/commit/injection/CommitModule.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.commit.injection
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import androidx.lifecycle.ViewModelProviders
6 | import com.rightpoint.mvi.example.commit.CommitBottomSheetDialog
7 | import com.rightpoint.mvi.example.commit.CommitViewModel
8 | import com.rightpoint.mvi.example.domain.interactor.commit.GetListOfCommits
9 | import dagger.Module
10 | import dagger.Provides
11 |
12 | @Module
13 | object CommitModule {
14 | @Provides
15 | @JvmStatic
16 | fun providesViewModel(
17 | dialog: CommitBottomSheetDialog,
18 | getListOfCommits: GetListOfCommits
19 | ): CommitViewModel {
20 | val factory = object : ViewModelProvider.Factory {
21 | override fun create(modelClass: Class): T {
22 | return CommitViewModel(getListOfCommits) as T
23 | }
24 | }
25 | return ViewModelProviders.of(dialog, factory)[CommitViewModel::class.java]
26 | }
27 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/GithubExampleApp.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example
2 |
3 | import android.os.StrictMode
4 | import com.jakewharton.threetenabp.AndroidThreeTen
5 | import com.rightpoint.mvi.example.injection.DaggerAppComponent
6 | import dagger.android.AndroidInjector
7 | import dagger.android.support.DaggerApplication
8 | import timber.log.Timber
9 | import javax.inject.Inject
10 |
11 | class GithubExampleApp : DaggerApplication() {
12 | @Inject lateinit var threadPolicy: StrictMode.ThreadPolicy
13 | @Inject lateinit var vmPolicy: StrictMode.VmPolicy
14 | @Inject lateinit var tree: Timber.Tree
15 |
16 | val component by lazy {
17 | DaggerAppComponent.builder().app(this).build()
18 | }
19 |
20 | override fun onCreate() {
21 | super.onCreate()
22 | StrictMode.setThreadPolicy(threadPolicy)
23 | StrictMode.setVmPolicy(vmPolicy)
24 | Timber.plant(tree)
25 | AndroidThreeTen.init(this)
26 | }
27 |
28 | override fun applicationInjector(): AndroidInjector {
29 | return component
30 | }
31 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/release/java/com/rightpoint/mvi/example/injection/BuildTypeModule.java:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.injection;
2 |
3 | import android.os.StrictMode;
4 |
5 | import org.jetbrains.annotations.NotNull;
6 | import org.jetbrains.annotations.Nullable;
7 |
8 | import dagger.Module;
9 | import dagger.Provides;
10 | import timber.log.Timber;
11 |
12 | @Module
13 | abstract class BuildTypeModule {
14 | @Provides
15 | static StrictMode.ThreadPolicy providesThreadPolicy() {
16 | return StrictMode.ThreadPolicy.LAX;
17 | }
18 |
19 | @Provides
20 | static StrictMode.VmPolicy providesVmPolicy() {
21 | return StrictMode.VmPolicy.LAX;
22 | }
23 |
24 | @Provides
25 | static Timber.Tree providesTree() {
26 | return new Timber.Tree() {
27 | @Override
28 | protected void log(int priority,
29 | @Nullable String tag,
30 | @NotNull String message,
31 | @Nullable Throwable t) {
32 | // Do nothing
33 | }
34 | };
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/mapper/entities/RepoEntityMapper.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.mapper.entities
2 |
3 | import com.rightpoint.mvi.example.cache.room.model.RepoEntity
4 | import com.rightpoint.mvi.example.domain.common.Mapper
5 | import com.rightpoint.mvi.example.remote.model.Repo
6 | import javax.inject.Inject
7 |
8 | class RepoEntityMapper @Inject constructor(
9 | private val ownerMapper: OwnerEntityMapper
10 | ) : Mapper {
11 | override fun map(t: Repo): RepoEntity {
12 | return RepoEntity(
13 | t.id,
14 | t.name,
15 | t.full_name,
16 | ownerMapper.map(t.owner)!!,
17 | t.is_private,
18 | t.html_url,
19 | t.description,
20 | t.fork,
21 | t.url,
22 | t.stargazers_count,
23 | t.watchers_count,
24 | t.language,
25 | t.has_issues,
26 | t.has_projects,
27 | t.has_downloads,
28 | t.has_wiki,
29 | t.has_pages,
30 | t.forks_count,
31 | t.open_issues_count
32 | )
33 | }
34 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/cache/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.Libraries
2 |
3 | apply plugin: 'com.android.library'
4 | apply plugin: 'android-config'
5 | apply plugin: 'ktlint-config'
6 | apply from: "$rootDir/gradle/coverage.gradle"
7 |
8 | dependencies {
9 | // Testing
10 | androidTestImplementation Libraries.androidxEspressoCore
11 | androidTestImplementation Libraries.androidxEspressoContrib
12 | androidTestImplementation Libraries.androidxTestRules
13 | androidTestImplementation Libraries.androidxTestRunner
14 | androidTestImplementation Libraries.androidxRoomTest
15 |
16 | // Support Database
17 | implementation Libraries.androidxSqlite
18 |
19 | // Room
20 | api Libraries.androidxRoom
21 | kapt Libraries.androidxRoomCompiler
22 | api Libraries.androidxRoomRxJava
23 |
24 | // Dagger
25 | implementation Libraries.dagger
26 | kapt Libraries.daggerCompiler
27 |
28 | implementation Libraries.androidxAnnotations
29 | implementation Libraries.kotlinStdlib
30 | implementation Libraries.rxJava
31 |
32 | testImplementation Libraries.androidxTestExtJUnit
33 | testImplementation Libraries.androidxTestExtTruth
34 | }
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/mapper/model/RepoModelMapper.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.mapper.model
2 |
3 | import com.rightpoint.mvi.example.cache.room.model.RepoEntity
4 | import com.rightpoint.mvi.example.domain.common.Mapper
5 | import com.rightpoint.mvi.example.domain.model.RepoModel
6 | import javax.inject.Inject
7 |
8 | class RepoModelMapper @Inject constructor(
9 | private val ownerMapper: OwnerModelMapper
10 | ) : Mapper {
11 | override fun map(t: RepoEntity): RepoModel {
12 | return RepoModel(
13 | t.id,
14 | t.name,
15 | t.full_name,
16 | ownerMapper.map(t.owner)!!,
17 | t.is_private,
18 | t.html_url,
19 | t.description,
20 | t.fork,
21 | t.url,
22 | t.stargazers_count,
23 | t.watchers_count,
24 | t.language,
25 | t.has_issues,
26 | t.has_projects,
27 | t.has_downloads,
28 | t.has_wiki,
29 | t.has_pages,
30 | t.forks_count,
31 | t.open_issues_count
32 | )
33 | }
34 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/values/themes_base.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/data/src/main/java/{{ cookiecutter.package_dir }}/data/injection/DataModule.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.data.injection;
2 |
3 | import {{ cookiecutter.package_name }}.cache.room.DatabaseModule;
4 | import {{ cookiecutter.package_name }}.domain.executors.AppExecutors;
5 | import {{ cookiecutter.package_name }}.remote.NetworkModule;
6 |
7 | import java.util.concurrent.Executors
8 |
9 | import dagger.Module
10 | import dagger.Provides
11 | import io.reactivex.Scheduler
12 | import io.reactivex.android.schedulers.AndroidSchedulers
13 | import io.reactivex.schedulers.Schedulers
14 |
15 | @Module(includes = [DatabaseModule::class, NetworkModule::class])
16 | object DataModule {
17 | @Provides
18 | @JvmStatic
19 | fun providesExecutors(): AppExecutors {
20 | return object : AppExecutors {
21 | override fun diskIo(): Scheduler {
22 | return Schedulers.from(Executors.newFixedThreadPool(3))
23 | }
24 |
25 | override fun networkIo(): Scheduler {
26 | return Schedulers.io()
27 | }
28 |
29 | override fun mainThread(): Scheduler {
30 | return AndroidSchedulers.mainThread()
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/repo/injection/RepoModule.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.repo.injection
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import androidx.lifecycle.ViewModelProviders
6 | import com.rightpoint.mvi.example.domain.interactor.repo.GetListOfRepos
7 | import com.rightpoint.mvi.example.repo.RepoListFragment
8 | import com.rightpoint.mvi.example.repo.RepoListViewModel
9 | import com.rightpoint.mvi.example.repo.model.RepoItemMapper
10 | import dagger.Module
11 | import dagger.Provides
12 |
13 | @Module
14 | object RepoModule {
15 | @Provides
16 | @JvmStatic
17 | fun providesMapper(fragment: RepoListFragment): RepoItemMapper {
18 | return RepoItemMapper(fragment.onCommitClick)
19 | }
20 |
21 | @Provides
22 | @JvmStatic
23 | fun providesViewModel(
24 | fragment: RepoListFragment,
25 | useCase: GetListOfRepos,
26 | mapper: RepoItemMapper
27 | ): RepoListViewModel {
28 | val factory = object : ViewModelProvider.Factory {
29 | override fun create(modelClass: Class): T {
30 | return RepoListViewModel(useCase, mapper) as T
31 | }
32 | }
33 | return ViewModelProviders.of(fragment, factory)[RepoListViewModel::class.java]
34 | }
35 | }
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.0
2 |
3 | defaults: &defaults
4 | docker:
5 | - image: circleci/android:api-28-alpha
6 | working_directory: ~/android-template
7 | environment:
8 | _JAVA_OPTIONS: "-Xmx1400m -XX:ParallelGCThreads=2 -XX:ConcGCThreads=2 -XX:ParallelGCThreads=2 -Djava.util.concurrent.ForkJoinPool.common.parallelism=2"
9 | TERM: dumb
10 |
11 | update_sdk: &update_sdk
12 | name: Update SDK
13 | command: |
14 | mkdir "$ANDROID_HOME/licenses" || true
15 | echo "d56f5187479451eabf01fb78af6dfcb131a6481e" > "$ANDROID_HOME/licenses/android-sdk-license"
16 | echo "84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license"
17 | sdkmanager "platform-tools" "platforms;android-28"
18 |
19 | jobs:
20 | test:
21 | <<: *defaults
22 | steps:
23 | - run:
24 | <<: *update_sdk
25 |
26 | - checkout
27 |
28 | - run:
29 | name: Update apt-get
30 | command: sudo apt-get update
31 |
32 | - run:
33 | name: Install cookiecutter
34 | command: sudo apt-get install cookiecutter
35 |
36 | - run:
37 | name: Provide script permissions
38 | command: chmod 777 ci_script.sh
39 |
40 | - run:
41 | name: Run tests
42 | command: ./ci_script.sh
43 |
44 | workflows:
45 | version: 2
46 | build_and_test:
47 | jobs:
48 | - test
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl` version "1.1.3"
3 | id("java-gradle-plugin")
4 | }
5 |
6 | configure {
7 | plugins.apply {
8 | create("android-config") {
9 | id = "android-config"
10 | implementationClass = "plugins.AndroidConfigPlugin"
11 | }
12 | create("kotlin-config") {
13 | id = "kotlin-config"
14 | implementationClass = "plugins.KotlinConfigPlugin"
15 | }
16 | create("appcenter-config") {
17 | id = "appcenter-config"
18 | implementationClass = "plugins.AppCenterConfigPlugin"
19 | }
20 | create("ktlint-config") {
21 | id = "ktlint-config"
22 | implementationClass = "plugins.KtlintConfigPlugin"
23 | }
24 | }
25 | }
26 |
27 | repositories {
28 | mavenCentral()
29 | maven("https://plugins.gradle.org/m2/")
30 | google()
31 | jcenter()
32 | }
33 |
34 | dependencies {
35 | implementation("com.android.tools.build:gradle:3.4.1")
36 | implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.31")
37 | implementation("gradle.plugin.com.betomorrow.gradle:appcenter-plugin:1.1.16")
38 | implementation("org.eclipse.jgit:org.eclipse.jgit.pgm:5.3.1.201904271842-r")
39 | implementation("org.jlleitschuh.gradle:ktlint-gradle:8.0.0")
40 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/buildSrc/src/main/java/plugins/AndroidConfigPlugin.kt:
--------------------------------------------------------------------------------
1 | package plugins
2 |
3 | import com.android.build.gradle.AbstractAppPlugin
4 | import com.android.build.gradle.AppExtension
5 | import com.android.build.gradle.LibraryExtension
6 | import com.android.build.gradle.LibraryPlugin
7 | import extensions.configure
8 | import extensions.configureTestOptions
9 | import org.gradle.api.Plugin
10 | import org.gradle.api.Project
11 | import org.gradle.kotlin.dsl.getByType
12 |
13 | class AndroidConfigPlugin : Plugin {
14 | override fun apply(project: Project) {
15 | project.plugins.apply("kotlin-android")
16 | project.plugins.apply("kotlin-kapt")
17 | project.plugins.forEach { plugin ->
18 | when (plugin) {
19 | is AbstractAppPlugin -> configureApp(project)
20 | is LibraryPlugin -> configureLibrary(project)
21 | }
22 | }
23 | }
24 |
25 | private fun configureApp(project: Project) {
26 | val extension = project.extensions.getByType()
27 | extension.configureTestOptions(project)
28 | extension.configure(project)
29 | }
30 |
31 | private fun configureLibrary(project: Project) {
32 | val extension = project.extensions.getByType()
33 | extension.configureTestOptions(project)
34 | extension.configure()
35 | }
36 | }
--------------------------------------------------------------------------------
/hooks/pre_gen_project.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 |
4 | # Check the app name
5 |
6 | APP_NAME_REGEX = r'[^A-Za-z0-9 ]'
7 |
8 | app_name = '{{ cookiecutter.app_name }}'
9 |
10 | if re.search(APP_NAME_REGEX, app_name):
11 | print('ERROR: please avoid using any special characters in your app name!')
12 | print('Include only alphanumeric characters and spaces.')
13 |
14 | # Exits with status 1 to indicate failure
15 | sys.exit(1)
16 |
17 | # Check the package name
18 |
19 | PACKAGE_REGEX = r'[^A-Za-z0-9.]'
20 |
21 | package_name = '{{ cookiecutter.package_name }}'
22 |
23 | if re.search(PACKAGE_REGEX, package_name):
24 | print('ERROR: %s is not a valid Android package name!' % package_name)
25 | print('Avoid using any special characters. Only alphanumeric characters are allowed.')
26 |
27 | # Exits with status 1 to indicate failure
28 | sys.exit(1)
29 |
30 | # Check the directory name
31 |
32 | DIRECTORY_REGEX = r'[^A-Za-z0-9\/]'
33 |
34 | directory_name = '{{ cookiecutter.package_dir }}'
35 |
36 | if re.search(DIRECTORY_REGEX, directory_name):
37 | print('ERROR: %s is not a valid Android package directory!' % directory_name)
38 | print('Avoid using any special characters. Only alphanumeric characters are allowed.')
39 |
40 | # Exits with status 1 to indicate failure
41 | sys.exit(1)
42 |
43 | print ('Proceeding with app name: {}, package name: {}, directory name: {}'.format(app_name, package_name, directory_name))
44 |
--------------------------------------------------------------------------------
/github-mvi-android/device/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion buildConfig.compileSdk
5 | buildToolsVersion buildConfig.buildTools
6 |
7 | defaultConfig {
8 | minSdkVersion buildConfig.minSdk
9 | targetSdkVersion buildConfig.targetSdk
10 | }
11 |
12 | compileOptions {
13 | sourceCompatibility JavaVersion.VERSION_1_8
14 | targetCompatibility JavaVersion.VERSION_1_8
15 | }
16 |
17 | buildTypes {
18 | debug {
19 | minifyEnabled false
20 | }
21 | release {
22 | minifyEnabled true
23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 |
27 | sourceSets {
28 |
29 | }
30 |
31 | testOptions {
32 | reportDir "$rootDir/reports/app"
33 | resultsDir "$rootDir/test-results/app"
34 | }
35 | }
36 |
37 | dependencies {
38 | androidTestImplementation libs.android.test.espresso.core
39 | androidTestImplementation libs.android.test.espresso.contrib
40 | androidTestImplementation libs.android.test.rules
41 | androidTestImplementation libs.android.test.runner
42 |
43 | // Android
44 | implementation libs.android.x.annotations
45 |
46 | // Kotlin
47 | implementation libs.kotlin.stdlib
48 |
49 | testImplementation libs.android.test.ext.junit
50 | testImplementation libs.android.test.ext.truth
51 | }
52 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/buildSrc/src/main/java/extensions/AppExtension.kt:
--------------------------------------------------------------------------------
1 | package extensions
2 |
3 | import com.android.build.gradle.AppExtension
4 | import dependencies.Config
5 | import org.gradle.api.JavaVersion
6 | import org.gradle.api.Project
7 | import java.io.File
8 |
9 | fun AppExtension.configure(project: Project) {
10 | setCompileSdkVersion(28)
11 | defaultConfig {
12 | setMinSdkVersion(23)
13 | setTargetSdkVersion(28)
14 | versionCode = Config.code
15 | versionName = Config.name
16 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 | buildTypes {
19 | getByName("debug") {
20 | isMinifyEnabled = false
21 | isTestCoverageEnabled = true
22 | }
23 | getByName("release") {
24 | isMinifyEnabled = true
25 | proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
26 | }
27 | }
28 | compileOptions {
29 | setSourceCompatibility(JavaVersion.VERSION_1_8)
30 | setTargetCompatibility(JavaVersion.VERSION_1_8)
31 | }
32 | lintOptions {
33 | isCheckDependencies = true
34 | isIgnoreTestSources = true
35 | isAbortOnError = true
36 | xmlReport = true
37 | xmlOutput = File("${project.buildDir}/reports/lint/lint-result.xml")
38 | htmlReport = true
39 | htmlOutput = File("${project.buildDir}/reports/lint/lint-result.html")
40 | }
41 | }
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/repository/commit/DefaultCommitRepository.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.repository.commit
2 |
3 | import com.rightpoint.mvi.example.cache.room.AppDatabase
4 | import com.rightpoint.mvi.example.cache.room.model.CommitEntity
5 | import com.rightpoint.mvi.example.data.NetworkBoundResource
6 | import com.rightpoint.mvi.example.data.mapper.entities.CommitEntityMapper
7 | import com.rightpoint.mvi.example.data.mapper.model.CommitModelMapper
8 | import com.rightpoint.mvi.example.domain.executors.AppExecutors
9 | import com.rightpoint.mvi.example.domain.model.CommitModel
10 | import com.rightpoint.mvi.example.domain.repository.commit.CommitRepository
11 | import com.rightpoint.mvi.example.remote.GithubApi
12 | import com.rightpoint.mvi.example.remote.model.Commit
13 | import io.reactivex.Flowable
14 | import io.reactivex.Single
15 | import javax.inject.Inject
16 |
17 | class DefaultCommitRepository @Inject constructor(
18 | private val executors: AppExecutors,
19 | private val api: GithubApi,
20 | private val database: AppDatabase,
21 | private val entityMapper: CommitEntityMapper,
22 | private val modelMapper: CommitModelMapper
23 | ) : CommitRepository {
24 | override fun getListOfCommitsByRepo(owner: String, repo: String): Single> {
25 | return api.getListOfCommitsForRepo(owner, repo)
26 | .map { it.map(entityMapper::map) }
27 | .map { it.map(modelMapper::map) }
28 | }
29 | }
--------------------------------------------------------------------------------
/github-mvi-android/data/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion buildConfig.compileSdk
5 | buildToolsVersion buildConfig.buildTools
6 |
7 | defaultConfig {
8 | minSdkVersion buildConfig.minSdk
9 | targetSdkVersion buildConfig.targetSdk
10 | }
11 |
12 | compileOptions {
13 | sourceCompatibility JavaVersion.VERSION_1_8
14 | targetCompatibility JavaVersion.VERSION_1_8
15 | }
16 |
17 | buildTypes {
18 | debug {
19 | minifyEnabled false
20 | }
21 | release {
22 | minifyEnabled true
23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 |
27 | sourceSets {
28 |
29 | }
30 |
31 | testOptions {
32 | reportDir "$rootDir/reports/app"
33 | resultsDir "$rootDir/test-results/app"
34 | }
35 | }
36 |
37 | dependencies {
38 | implementation project(":domain")
39 | api project(':cache')
40 |
41 | api project(':remote')
42 |
43 | // Android
44 | implementation libs.android.x.annotations
45 |
46 | // Dagger
47 | implementation libs.dagger.runtime
48 | kapt libs.dagger.compiler
49 |
50 | // Kotlin
51 | implementation libs.kotlin.stdlib
52 |
53 | // Rx
54 | implementation libs.rx.java
55 | implementation libs.rx.android
56 |
57 | implementation libs.threeTen.androidBackport
58 |
59 | testImplementation libs.android.test.ext.junit
60 | testImplementation libs.android.test.ext.truth
61 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/repo/RepoListViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.repo
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.rightpoint.common.exhaustive
5 | import com.rightpoint.mvi.example.domain.interactor.repo.GetListOfRepos
6 | import com.rightpoint.mvi.example.repo.model.RepoItemMapper
7 | import io.reactivex.Observable
8 | import io.reactivex.ObservableTransformer
9 | import javax.inject.Inject
10 |
11 | class RepoListViewModel @Inject constructor(
12 | private val getListOfRepos: GetListOfRepos,
13 | private val itemMapper: RepoItemMapper
14 | ) : ViewModel() {
15 | fun model(): ObservableTransformer {
16 | return ObservableTransformer { upstream ->
17 | upstream.flatMap { action ->
18 | when (action) {
19 | is Action.LoadListOfRepos -> getListOfReposByOrganization(action)
20 | }.exhaustive
21 | }
22 | }
23 | }
24 |
25 | private fun getListOfReposByOrganization(action: Action.LoadListOfRepos): Observable {
26 | val params = GetListOfRepos.Params(action.organization)
27 | return getListOfRepos(params)
28 | .toObservable()
29 | .map { it.map(itemMapper::map) }
30 | .map { list ->
31 | if (list.isEmpty()) {
32 | State.Empty
33 | } else {
34 | State.Loaded(list)
35 | }
36 | }
37 | .onErrorReturn(State::Error)
38 | .startWith(State.Loading)
39 | }
40 | }
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/injection/DataModule.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.injection
2 |
3 | import android.app.Application
4 | import android.content.SharedPreferences
5 | import android.preference.PreferenceManager
6 | import com.rightpoint.mvi.example.cache.room.DatabaseModule
7 | import com.rightpoint.mvi.example.domain.executors.AppExecutors
8 | import com.rightpoint.mvi.example.remote.NetworkModule
9 | import dagger.Module
10 | import dagger.Provides
11 | import io.reactivex.Scheduler
12 | import io.reactivex.android.schedulers.AndroidSchedulers
13 | import io.reactivex.schedulers.Schedulers
14 | import java.util.concurrent.Executors
15 | import javax.inject.Singleton
16 |
17 | @Module(includes = [DataBindings::class, DatabaseModule::class, NetworkModule::class])
18 | object DataModule {
19 | @Provides
20 | @JvmStatic
21 | fun providesExecutors(): AppExecutors {
22 | return object : AppExecutors {
23 | override fun diskIo(): Scheduler {
24 | return Schedulers.from(Executors.newFixedThreadPool(3))
25 | }
26 |
27 | override fun networkIo(): Scheduler {
28 | return Schedulers.io()
29 | }
30 |
31 | override fun mainThread(): Scheduler {
32 | return AndroidSchedulers.mainThread()
33 | }
34 | }
35 | }
36 |
37 | @Provides
38 | @JvmStatic
39 | @Singleton
40 | fun providesSharedPreferences(app: Application): SharedPreferences {
41 | return PreferenceManager.getDefaultSharedPreferences(app)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/github-mvi-android/common-android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion buildConfig.compileSdk
5 | buildToolsVersion buildConfig.buildTools
6 |
7 | defaultConfig {
8 | minSdkVersion buildConfig.minSdk
9 | targetSdkVersion buildConfig.targetSdk
10 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
11 | }
12 |
13 | compileOptions {
14 | sourceCompatibility JavaVersion.VERSION_1_8
15 | targetCompatibility JavaVersion.VERSION_1_8
16 | }
17 |
18 | buildTypes {
19 | debug {
20 | minifyEnabled false
21 | }
22 | release {
23 | minifyEnabled true
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 |
28 | sourceSets {
29 |
30 | }
31 |
32 | testOptions {
33 | reportDir "$rootDir/reports/app"
34 | resultsDir "$rootDir/test-results/app"
35 | }
36 | }
37 |
38 | dependencies {
39 | // Android
40 | implementation libs.android.x.annotations
41 |
42 | // Kotlin
43 | implementation libs.kotlin.stdlib
44 |
45 | // RecyclerView
46 | implementation libs.android.x.recyclerView
47 |
48 | testImplementation libs.android.test.ext.junit
49 | testImplementation libs.android.test.ext.truth
50 |
51 | androidTestImplementation libs.android.test.espresso.core
52 | androidTestImplementation libs.android.test.espresso.contrib
53 | androidTestImplementation libs.android.test.rules
54 | androidTestImplementation libs.android.test.runner
55 | }
56 |
--------------------------------------------------------------------------------
/github-mvi-android/common-android/src/main/java/com/rightpoint/common/android/list/BaseAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.common.android.list
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.GridLayoutManager
6 | import androidx.recyclerview.widget.ListAdapter
7 |
8 | class BaseAdapter : ListAdapter(ItemDiffCallback()) {
9 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
10 | val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
11 | return BaseViewHolder(view)
12 | }
13 |
14 | override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
15 | val item = getItem(position)
16 | item?.bind(holder)
17 | }
18 |
19 | override fun onBindViewHolder(
20 | holder: BaseViewHolder,
21 | position: Int,
22 | payloads: MutableList
23 | ) {
24 | if (payloads.isNotEmpty()) {
25 | val item = getItem(position)
26 | item?.bind(holder, payloads)
27 | }
28 | super.onBindViewHolder(holder, position, payloads)
29 | }
30 |
31 | override fun getItemId(position: Int): Long = getItem(position).uniqueId()
32 |
33 | override fun getItemViewType(position: Int): Int = getItem(position).layoutId()
34 |
35 | fun newSpanSizeLookup(): GridLayoutManager.SpanSizeLookup {
36 | return object : GridLayoutManager.SpanSizeLookup() {
37 | override fun getSpanSize(position: Int): Int {
38 | return getItem(position).spanSize()
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/layout/fragment_repo_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
22 |
23 |
32 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/layout/fragment_repo_list_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
22 |
23 |
32 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/lint/src/test/java/{{ cookiecutter.package_dir }}/lint/InvalidImportTest.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.lint
2 |
3 | import com.android.tools.lint.checks.infrastructure.TestFiles.java
4 | import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
5 | import org.junit.Test
6 |
7 | class InvalidImportTest {
8 | @Test fun normalRImport() {
9 | val file = java("""
10 | package foo;
11 | import foo.R;
12 | class Example {
13 | }""").indented()
14 |
15 | lint().files(file).issues(ISSUE_INVALID_IMPORT).run().expectClean()
16 | }
17 |
18 | @Test fun rDrawableImport() {
19 | val file = java("""
20 | package foo;
21 | import foo.R.drawable;
22 | class Example {
23 | }""").indented()
24 |
25 | val expected = """
26 | |src/foo/Example.java:2: Warning: Forbidden import [InvalidImport]
27 | |import foo.R.drawable;
28 | | ~~~~~~~~~~~~~~
29 | |0 errors, 1 warnings""".trimMargin()
30 |
31 | lint().files(file).issues(ISSUE_INVALID_IMPORT).run().expect(expected)
32 | }
33 |
34 | @Test fun internalImport() {
35 | val file = java("""
36 | package foo;
37 | import com.foo.internal.Foo;
38 | class Example {
39 | }""").indented()
40 |
41 | val expected = """
42 | |src/foo/Example.java:2: Warning: Forbidden import [InvalidImport]
43 | |import com.foo.internal.Foo;
44 | | ~~~~~~~~~~~~~~~~~~~~
45 | |0 errors, 1 warnings""".trimMargin()
46 |
47 | lint().files(file).issues(ISSUE_INVALID_IMPORT).run().expect(expected)
48 | }
49 | }
--------------------------------------------------------------------------------
/github-mvi-android/remote/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion buildConfig.compileSdk
5 | buildToolsVersion buildConfig.buildTools
6 |
7 | defaultConfig {
8 | minSdkVersion buildConfig.minSdk
9 | targetSdkVersion buildConfig.targetSdk
10 | }
11 |
12 | compileOptions {
13 | sourceCompatibility JavaVersion.VERSION_1_8
14 | targetCompatibility JavaVersion.VERSION_1_8
15 | }
16 |
17 | buildTypes {
18 | debug {
19 | minifyEnabled false
20 | }
21 | release {
22 | minifyEnabled true
23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 |
27 | sourceSets {
28 |
29 | }
30 |
31 | testOptions {
32 | reportDir "$rootDir/reports/app"
33 | resultsDir "$rootDir/test-results/app"
34 | }
35 | }
36 |
37 | dependencies {
38 | // Kotlin
39 | implementation libs.kotlin.stdlib
40 |
41 | // Dagger
42 | implementation libs.dagger.runtime
43 | kapt libs.dagger.compiler
44 |
45 | // OkHttp
46 | api libs.okhttp.client
47 | api libs.okhttp.logging
48 |
49 | // Retrofit
50 | api libs.retrofit.client
51 | api libs.retrofit.adapterRxJava2
52 | api libs.retrofit.converterMoshi
53 |
54 | // Moshi
55 | implementation libs.moshi
56 |
57 | // Kotshi
58 | implementation libs.kotshi.api
59 | kapt libs.kotshi.compiler
60 |
61 | // RxJava
62 | implementation libs.rx.java
63 |
64 | // Timber
65 | implementation libs.timber
66 |
67 | testImplementation libs.android.test.ext.junit
68 | testImplementation libs.android.test.ext.truth
69 | }
70 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/app/src/main/res/values/text_appearances.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
40 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/remote/src/main/java/{{ cookiecutter.package_dir }}/remote/NetworkModule.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.remote
2 |
3 | import com.squareup.moshi.Moshi
4 | import dagger.Lazy
5 | import dagger.Module
6 | import dagger.Provides
7 | import io.reactivex.schedulers.Schedulers
8 | import javax.inject.Singleton
9 | import okhttp3.HttpUrl
10 | import okhttp3.OkHttpClient
11 | import okhttp3.logging.HttpLoggingInterceptor
12 | import retrofit2.Retrofit
13 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
14 | import retrofit2.converter.moshi.MoshiConverterFactory
15 | import timber.log.Timber
16 |
17 | @Module(includes = [NetworkSettings::class])
18 | object NetworkModule {
19 | @Provides
20 | @JvmStatic
21 | fun providesBaseUrl(): HttpUrl? {
22 | return HttpUrl.parse("TODO: Please provide an appropriate base URL!")
23 | }
24 |
25 | @Provides
26 | @Singleton
27 | @JvmStatic
28 | fun providesOkHttpClient(level: HttpLoggingInterceptor.Level): OkHttpClient {
29 | val logging = HttpLoggingInterceptor { message -> Timber.tag("OkHttp").v(message) }
30 | .apply { this.level = level }
31 |
32 | return OkHttpClient.Builder()
33 | .addInterceptor(logging)
34 | .build()
35 | }
36 |
37 | @Provides
38 | @Singleton
39 | @JvmStatic
40 | fun providesRetrofit(url: HttpUrl?, client: Lazy): Retrofit {
41 | val moshi = Moshi.Builder().build()
42 |
43 | return Retrofit.Builder()
44 | .baseUrl(requireNotNull(url))
45 | .callFactory { client.get().newCall(it) }
46 | .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
47 | .addConverterFactory(MoshiConverterFactory.create(moshi))
48 | .build()
49 | }
50 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | addressable (2.5.2)
5 | public_suffix (>= 2.0.2, < 4.0)
6 | ansi (1.5.0)
7 | ast (2.4.0)
8 | claide (1.0.2)
9 | claide-plugins (0.9.2)
10 | cork
11 | nap
12 | open4 (~> 1.3)
13 | colored2 (3.1.2)
14 | cork (0.3.0)
15 | colored2 (~> 3.1)
16 | danger (5.8.0)
17 | claide (~> 1.0)
18 | claide-plugins (>= 0.9.2)
19 | colored2 (~> 3.1)
20 | cork (~> 0.1)
21 | faraday (~> 0.9)
22 | faraday-http-cache (~> 1.0)
23 | git (~> 1.5)
24 | kramdown (~> 1.5)
25 | no_proxy_fix
26 | octokit (~> 4.7)
27 | terminal-table (~> 1)
28 | danger-android_lint (0.0.6)
29 | danger-plugin-api (~> 1.0)
30 | oga
31 | danger-checkstyle_format (0.1.1)
32 | danger-plugin-api (~> 1.0)
33 | ox (~> 2.0)
34 | danger-plugin-api (1.0.0)
35 | danger (> 2.0)
36 | faraday (0.15.3)
37 | multipart-post (>= 1.2, < 3)
38 | faraday-http-cache (1.3.1)
39 | faraday (~> 0.8)
40 | git (1.5.0)
41 | kramdown (1.17.0)
42 | multipart-post (2.0.0)
43 | nap (1.1.0)
44 | no_proxy_fix (0.1.2)
45 | octokit (4.13.0)
46 | sawyer (~> 0.8.0, >= 0.5.3)
47 | oga (2.15)
48 | ast
49 | ruby-ll (~> 2.1)
50 | open4 (1.3.4)
51 | ox (2.10.0)
52 | public_suffix (3.0.3)
53 | ruby-ll (2.1.2)
54 | ansi
55 | ast
56 | sawyer (0.8.1)
57 | addressable (>= 2.3.5, < 2.6)
58 | faraday (~> 0.8, < 1.0)
59 | terminal-table (1.8.0)
60 | unicode-display_width (~> 1.1, >= 1.1.1)
61 | unicode-display_width (1.4.0)
62 |
63 | PLATFORMS
64 | ruby
65 |
66 | DEPENDENCIES
67 | danger
68 | danger-android_lint
69 | danger-checkstyle_format
70 |
71 | BUNDLED WITH
72 | 1.17.1
73 |
--------------------------------------------------------------------------------
/github-mvi-android/remote/src/main/java/com/rightpoint/mvi/example/remote/NetworkModule.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.remote
2 |
3 | import com.squareup.moshi.Moshi
4 | import dagger.Module
5 | import dagger.Provides
6 | import io.reactivex.schedulers.Schedulers
7 | import okhttp3.HttpUrl
8 | import okhttp3.OkHttpClient
9 | import okhttp3.logging.HttpLoggingInterceptor
10 | import retrofit2.Retrofit
11 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
12 | import retrofit2.converter.moshi.MoshiConverterFactory
13 | import timber.log.Timber
14 | import javax.inject.Singleton
15 |
16 | @Module(includes = [NetworkSettings::class])
17 | object NetworkModule {
18 | @Provides
19 | @JvmStatic
20 | fun providesBaseUrl(): HttpUrl? {
21 | return HttpUrl.parse("https://api.github.com/")
22 | }
23 |
24 | @Provides
25 | @Singleton
26 | @JvmStatic
27 | fun providesRetrofit(url: HttpUrl?, level: HttpLoggingInterceptor.Level): Retrofit {
28 | val logging = HttpLoggingInterceptor { message -> Timber.tag("OkHttp").v(message) }
29 | logging.level = level
30 |
31 | val client = OkHttpClient.Builder()
32 | .addInterceptor(logging)
33 | .build()
34 |
35 | val moshi = Moshi.Builder()
36 | .add(AppJsonAdapterFactory.INSTANCE)
37 | .build()
38 |
39 | return Retrofit.Builder()
40 | .baseUrl(requireNotNull(url))
41 | .client(client)
42 | .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
43 | .addConverterFactory(MoshiConverterFactory.create(moshi))
44 | .build()
45 | }
46 |
47 | @Provides
48 | @Singleton
49 | @JvmStatic
50 | fun providesApi(retrofit: Retrofit): GithubApi {
51 | return retrofit.create(GithubApi::class.java)
52 | }
53 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/lint/src/main/java/{{ cookiecutter.package_dir }}/lint/DefaultLayoutAttributeDetector.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.lint
2 |
3 | import com.android.SdkConstants.ATTR_TEXT_STYLE
4 | import com.android.tools.lint.detector.api.Category.Companion.CORRECTNESS
5 | import com.android.tools.lint.detector.api.Implementation
6 | import com.android.tools.lint.detector.api.Issue
7 | import com.android.tools.lint.detector.api.LayoutDetector
8 | import com.android.tools.lint.detector.api.Scope.Companion.RESOURCE_FILE_SCOPE
9 | import com.android.tools.lint.detector.api.Severity.WARNING
10 | import com.android.tools.lint.detector.api.XmlContext
11 | import org.w3c.dom.Attr
12 |
13 | private const val PRIORITY = 2
14 |
15 | val ISSUE_DEFAULT_LAYOUT_ATTRIBUTE = Issue.create("DefaultLayoutAttribute",
16 | "Default layout value",
17 | "Flags default layout values that are not needed." +
18 | " (e.g textStyle=\"normal\" can be removed).",
19 | CORRECTNESS, PRIORITY, WARNING,
20 | Implementation(DefaultLayoutAttributeDetector::class.java, RESOURCE_FILE_SCOPE)
21 | )
22 |
23 | class DefaultLayoutAttributeDetector : LayoutDetector() {
24 | override fun getApplicableAttributes() = listOf(ATTR_TEXT_STYLE)
25 |
26 | override fun visitAttribute(context: XmlContext, attribute: Attr) {
27 | if ("normal" == attribute.value) {
28 | val fix = fix()
29 | .unset(attribute.namespaceURI, attribute.localName)
30 | .name("Remove")
31 | .autoFix()
32 | .build()
33 |
34 | context.report(ISSUE_DEFAULT_LAYOUT_ATTRIBUTE, attribute,
35 | context.getValueLocation(attribute),
36 | "This is the default and hence you don't need to specify it.", fix)
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/github-mvi-android/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 /Users/priyakarambelkar/Library/Android/sdk/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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
27 | # Dagger
28 | -dontwarn com.google.errorprone.annotations.*
29 |
30 | # Kotlin
31 | -dontwarn kotlin.**
32 | -dontnote kotlin.**
33 |
34 | # Okio
35 | -keep class sun.misc.Unsafe { *; }
36 | -dontwarn java.nio.file.*
37 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
38 | -dontnote okio.**
39 |
40 | # OkHttp
41 | -keepattributes Signature
42 | -keepattributes *Annotation*
43 | -dontnote okhttp3.**
44 | -dontwarn okhttp3.**
45 | -dontwarn javax.annotation.Nullable
46 | -dontwarn javax.annotation.ParametersAreNonnullByDefault
47 | -keep class okhttp3.** { *; }
48 | -keep interface okhttp3.** { *; }
49 |
50 | # RxJava
51 | -dontnote io.reactivex.**
52 | -dontwarn io.reactivex.**
53 |
54 | # AutoDispose
55 | -dontnote com.uber.autodispose.**
56 | -keep class com.uber.autodispose.** { *; }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/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 /Users/priyakarambelkar/Library/Android/sdk/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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
27 | # Dagger
28 | -dontwarn com.google.errorprone.annotations.*
29 |
30 | # Kotlin
31 | -dontwarn kotlin.**
32 | -dontnote kotlin.**
33 |
34 | # Okio
35 | -keep class sun.misc.Unsafe { *; }
36 | -dontwarn java.nio.file.*
37 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
38 | -dontnote okio.**
39 |
40 | # OkHttp
41 | -keepattributes Signature
42 | -keepattributes *Annotation*
43 | -dontnote okhttp3.**
44 | -dontwarn okhttp3.**
45 | -dontwarn javax.annotation.Nullable
46 | -dontwarn javax.annotation.ParametersAreNonnullByDefault
47 | -keep class okhttp3.** { *; }
48 | -keep interface okhttp3.** { *; }
49 |
50 | # RxJava
51 | -dontnote io.reactivex.**
52 | -dontwarn io.reactivex.**
53 |
54 | # AutoDispose
55 | -dontnote com.uber.autodispose.**
56 | -keep class com.uber.autodispose.** { *; }
--------------------------------------------------------------------------------
/github-mvi-android/cache/src/main/java/com/rightpoint/mvi/example/cache/room/model/RepoEntity.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.cache.room.model
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Embedded
5 | import androidx.room.Entity
6 | import androidx.room.Index
7 |
8 | /**
9 | * Using name/owner_login as primary key instead of id since name/owner_login is always available
10 | * vs id is not.
11 | */
12 | @Entity(
13 | indices = [
14 | Index("id"),
15 | Index("owner_login")
16 | ],
17 | primaryKeys = ["name", "owner_login"]
18 | )
19 | data class RepoEntity(
20 | @field:ColumnInfo(name = "id") val id: Long,
21 | @field:ColumnInfo(name = "name") val name: String,
22 | @field:ColumnInfo(name = "full_name") val full_name: String,
23 | @field:Embedded(prefix = "owner_") val owner: OwnerEntity,
24 | @field:ColumnInfo(name = "is_private") val is_private: Boolean,
25 | @field:ColumnInfo(name = "html_url") val html_url: String,
26 | @field:ColumnInfo(name = "description") val description: String?,
27 | @field:ColumnInfo(name = "fork") val fork: Boolean,
28 | @field:ColumnInfo(name = "url") val url: String,
29 | @field:ColumnInfo(name = "stargazers_count") val stargazers_count: Int,
30 | @field:ColumnInfo(name = "watchers_count") val watchers_count: Int,
31 | @field:ColumnInfo(name = "language") val language: String?,
32 | @field:ColumnInfo(name = "has_issues") val has_issues: Boolean,
33 | @field:ColumnInfo(name = "has_projects") val has_projects: Boolean,
34 | @field:ColumnInfo(name = "has_downloads") val has_downloads: Boolean,
35 | @field:ColumnInfo(name = "has_wiki") val has_wiki: Boolean,
36 | @field:ColumnInfo(name = "has_pages") val has_pages: Boolean,
37 | @field:ColumnInfo(name = "forks_count") val forks_count: Int,
38 | @field:ColumnInfo(name = "open_issues_count") val open_issues_count: Int
39 | )
--------------------------------------------------------------------------------
/github-mvi-android/cache/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion buildConfig.compileSdk
5 | buildToolsVersion buildConfig.buildTools
6 |
7 | defaultConfig {
8 | minSdkVersion buildConfig.minSdk
9 | targetSdkVersion buildConfig.targetSdk
10 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
11 | }
12 |
13 | compileOptions {
14 | sourceCompatibility JavaVersion.VERSION_1_8
15 | targetCompatibility JavaVersion.VERSION_1_8
16 | }
17 |
18 | buildTypes {
19 | debug {
20 | minifyEnabled false
21 | }
22 | release {
23 | minifyEnabled true
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 |
28 | sourceSets {
29 |
30 | }
31 |
32 | testOptions {
33 | reportDir "$rootDir/reports/app"
34 | resultsDir "$rootDir/test-results/app"
35 | }
36 | }
37 |
38 | dependencies {
39 | // Testing
40 | androidTestImplementation libs.android.test.espresso.core
41 | androidTestImplementation libs.android.test.espresso.contrib
42 | androidTestImplementation libs.android.test.rules
43 | androidTestImplementation libs.android.test.runner
44 | androidTestImplementation libs.android.x.room.test
45 |
46 | // Support Database
47 | implementation libs.android.x.sqlite
48 |
49 | // Room
50 | api libs.android.x.room.runtime
51 | kapt libs.android.x.room.compiler
52 | api libs.android.x.room.adapterRxJava2
53 |
54 | // Dagger
55 | implementation libs.dagger.runtime
56 | kapt libs.dagger.compiler
57 |
58 | implementation libs.android.x.annotations
59 | implementation libs.kotlin.stdlib
60 | implementation libs.rx.java
61 |
62 | testImplementation libs.android.test.ext.junit
63 | testImplementation libs.android.test.ext.truth
64 | }
65 |
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/repo/model/RepoItem.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.repo.model
2 |
3 | import androidx.browser.customtabs.CustomTabsIntent
4 | import androidx.core.net.toUri
5 | import com.rightpoint.common.android.list.BaseViewHolder
6 | import com.rightpoint.common.android.list.Item
7 | import com.rightpoint.mvi.example.R
8 | import kotlinx.android.synthetic.main.list_item_repo_grid.view.*
9 |
10 | data class RepoItem(
11 | val id: Long,
12 | val name: String,
13 | val description: String?,
14 | val ownerAvatarUrl: String,
15 | val htmlUrl: String,
16 | val forks: Int,
17 | val stars: Int,
18 | val watchers: Int,
19 | val openIssues: Int,
20 | val onCommitClick: ((String) -> Unit)? = null
21 | ) : Item {
22 | override fun layoutId(): Int = R.layout.list_item_repo_grid
23 |
24 | override fun uniqueId(): Long = id
25 |
26 | override fun bind(holder: BaseViewHolder) {
27 | holder.itemView.repoName.text = name
28 | holder.itemView.repoDescription.text = description
29 | holder.itemView.openIssues.text = openIssues.toString()
30 | holder.itemView.setOnClickListener {
31 | CustomTabsIntent.Builder()
32 | .addDefaultShareMenuItem()
33 | .build()
34 | .launchUrl(holder.itemView.context, htmlUrl.toUri())
35 | }
36 | holder.itemView.commits.setOnClickListener {
37 | onCommitClick?.invoke(name)
38 | }
39 | holder.itemView.openIssues.setOnClickListener {
40 | CustomTabsIntent.Builder()
41 | .addDefaultShareMenuItem()
42 | .build()
43 | .launchUrl(
44 | holder.itemView.context,
45 | htmlUrl.toUri()
46 | .buildUpon()
47 | .appendPath("issues")
48 | .build()
49 | )
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/commit/CommitViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.commit
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.rightpoint.common.exhaustive
5 | import com.rightpoint.mvi.example.domain.interactor.commit.GetListOfCommits
6 | import io.reactivex.Observable
7 | import io.reactivex.ObservableTransformer
8 | import javax.inject.Inject
9 |
10 | class CommitViewModel @Inject constructor(
11 | private val getListOfCommits: GetListOfCommits
12 | ) : ViewModel() {
13 | fun model(): ObservableTransformer {
14 | return ObservableTransformer { upstream ->
15 | upstream.flatMap { action ->
16 | when (action) {
17 | is Action.LoadListOfCommits -> getLatestCommit(action)
18 | }.exhaustive
19 | }
20 | }
21 | }
22 |
23 | private fun getLatestCommit(action: Action.LoadListOfCommits): Observable {
24 | val params = GetListOfCommits.Params(action.owner, action.repo)
25 | return getListOfCommits(params)
26 | .toObservable()
27 | .map { list ->
28 | list.map { model ->
29 | CommitItem(
30 | sha = model.sha,
31 | message = model.message,
32 | commentCount = model.commentCount,
33 | url = model.url,
34 | htmlUrl = model.htmlUrl,
35 | commentsUrl = model.commentsUrl,
36 | authorLogin = model.author?.login ?: "",
37 | authorId = model.author?.id ?: -1,
38 | authorAvatarUrl = model.author?.avatarUrl ?: "",
39 | authorUrl = model.author?.url ?: "",
40 | authorHtmlUrl = model.author?.htmlUrl ?: ""
41 | )
42 | }
43 | }
44 | .map { it.first() }
45 | .map(State::Loaded)
46 | .onErrorReturn(State::Error)
47 | .startWith(State.Loading)
48 | }
49 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/lint/src/main/java/{{ cookiecutter.package_dir }}/lint/InvalidImportDetector.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.lint
2 |
3 | import com.android.tools.lint.client.api.UElementHandler
4 | import com.android.tools.lint.detector.api.Category.Companion.CORRECTNESS
5 | import com.android.tools.lint.detector.api.Detector
6 | import com.android.tools.lint.detector.api.Implementation
7 | import com.android.tools.lint.detector.api.Issue
8 | import com.android.tools.lint.detector.api.JavaContext
9 | import com.android.tools.lint.detector.api.Scope.JAVA_FILE
10 | import com.android.tools.lint.detector.api.Scope.TEST_SOURCES
11 | import com.android.tools.lint.detector.api.Severity.WARNING
12 | import org.jetbrains.uast.UImportStatement
13 | import java.util.EnumSet
14 |
15 | private const val PRIORITY = 4
16 |
17 | val ISSUE_INVALID_IMPORT = Issue.create("InvalidImport",
18 | "Flags invalid imports.",
19 | "Flags invalid imports. One example is com.foo.bar.R.drawable. Instead just the" +
20 | " generated class R should be imported and not R.drawable. Also you should never" +
21 | " import anything that's in an internal package.",
22 | CORRECTNESS, PRIORITY, WARNING,
23 | Implementation(InvalidImportDetector::class.java, EnumSet.of(JAVA_FILE, TEST_SOURCES)))
24 |
25 | private val disallowedImports = listOf(".R.", "internal.", "internaI.")
26 |
27 | class InvalidImportDetector : Detector(), Detector.UastScanner {
28 | override fun getApplicableUastTypes() = listOf(UImportStatement::class.java)
29 |
30 | override fun createUastHandler(context: JavaContext) = InvalidImportHandler(context)
31 |
32 | class InvalidImportHandler(private val context: JavaContext) : UElementHandler() {
33 | override fun visitImportStatement(node: UImportStatement) {
34 | node.importReference?.let { importReference ->
35 | if (disallowedImports.any { importReference.asSourceString().contains(it) }) {
36 | context.report(ISSUE_INVALID_IMPORT, node, context.getLocation(importReference),
37 | "Forbidden import")
38 | }
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/github-mvi-android/common-android/src/main/java/com/rightpoint/common/android/list/BindableAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.common.android.list
2 |
3 | import android.content.Context
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.BaseAdapter
8 |
9 | /** An implementation of [BaseAdapter] which uses the new/bind pattern for its views. */
10 | abstract class BindableAdapter(val context: Context) : BaseAdapter() {
11 | private val inflater: LayoutInflater = LayoutInflater.from(context)
12 |
13 | abstract override fun getItem(position: Int): T
14 |
15 | override fun getView(position: Int, view: View?, container: ViewGroup): View {
16 | val itemView = view ?: newView(inflater, position, container)
17 |
18 | checkNotNull(itemView) {
19 | "newView result must not be null."
20 | }
21 |
22 | bindView(getItem(position), position, itemView)
23 |
24 | return itemView
25 | }
26 |
27 | /** Create a new instance of a view for the specified position. */
28 | abstract fun newView(inflater: LayoutInflater, position: Int, container: ViewGroup): View
29 |
30 | /** Bind the data for the specified `position` to the view. */
31 | abstract fun bindView(item: T, position: Int, view: View)
32 |
33 | override fun getDropDownView(position: Int, view: View?, container: ViewGroup): View {
34 | val dropDownView = view ?: newDropDownView(inflater, position, container)
35 |
36 | checkNotNull(dropDownView) {
37 | "newDropDownView result must not be null."
38 | }
39 |
40 | bindDropDownView(getItem(position), position, dropDownView)
41 |
42 | return dropDownView
43 | }
44 |
45 | /** Create a new instance of a drop-down view for the specified position. */
46 | open fun newDropDownView(inflater: LayoutInflater, position: Int, container: ViewGroup): View {
47 | return newView(inflater, position, container)
48 | }
49 |
50 | /** Bind the data for the specified `position` to the drop-down view. */
51 | open fun bindDropDownView(item: T, position: Int, view: View) {
52 | bindView(item, position, view)
53 | }
54 | }
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/NetworkBoundResource.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data
2 |
3 | import androidx.annotation.WorkerThread
4 | import com.rightpoint.mvi.example.domain.executors.AppExecutors
5 | import io.reactivex.Flowable
6 | import io.reactivex.Single
7 |
8 | internal abstract class NetworkBoundResource
9 | constructor(private val executors: AppExecutors) {
10 |
11 | fun asFlowable(): Flowable {
12 | return checkDb().subscribeOn(executors.diskIo())
13 | .flatMapPublisher {
14 | if (shouldFetch(it)) {
15 | fetchFromNetworkAndSave()
16 | } else {
17 | openDbConnection().subscribeOn(executors.diskIo())
18 | }
19 | }
20 | .onErrorResumeNext(fetchFromNetworkAndSave())
21 | .observeOn(executors.mainThread())
22 | }
23 |
24 | private fun fetchFromNetworkAndSave(): Flowable {
25 | return fetchFromNetwork().subscribeOn(executors.networkIo())
26 | .map(this::mapTo)
27 | .observeOn(executors.diskIo())
28 | .doOnSuccess(this::saveToDb)
29 | .flatMapPublisher { openDbConnection() }
30 | }
31 |
32 | /**
33 | * Makes a query to the database to check if the value exists. Returns a Single of the value
34 | * if it exists or an error if it doesn't exist.
35 | */
36 | abstract fun checkDb(): Single
37 |
38 | /**
39 | * Opens a Flowable connection to the database that emits new values whenever an update to the
40 | * database occurs.
41 | */
42 | abstract fun openDbConnection(): Flowable
43 |
44 | /**
45 | * Returns a boolean which is true if the NetworkBoundResource should fetch fresh data from the
46 | * server or false if it should just return the data cached in the local database.
47 | */
48 | abstract fun shouldFetch(data: LocalType): Boolean
49 |
50 | @WorkerThread abstract fun saveToDb(data: LocalType)
51 |
52 | abstract fun fetchFromNetwork(): Single
53 |
54 | /**
55 | * Maps the remote data from the network to a type that can be stored locally in the database.
56 | */
57 | abstract fun mapTo(data: RemoteType): LocalType
58 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/data/src/main/java/{{ cookiecutter.package_dir }}/data/NetworkBoundResource.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.data
2 |
3 | import androidx.annotation.WorkerThread
4 | import {{ cookiecutter.package_name }}.domain.executors.AppExecutors
5 | import io.reactivex.Flowable
6 | import io.reactivex.Single
7 |
8 | internal abstract class NetworkBoundResource
9 | constructor(private val executors: AppExecutors) {
10 |
11 | fun asFlowable(): Flowable {
12 | return checkDb().subscribeOn(executors.diskIo())
13 | .flatMapPublisher {
14 | if (shouldFetch(it)) {
15 | fetchFromNetworkAndSave()
16 | } else {
17 | openDbConnection().subscribeOn(executors.diskIo())
18 | }
19 | }
20 | .onErrorResumeNext(fetchFromNetworkAndSave())
21 | .observeOn(executors.mainThread())
22 | }
23 |
24 | private fun fetchFromNetworkAndSave(): Flowable {
25 | return fetchFromNetwork().subscribeOn(executors.networkIo())
26 | .map(this::mapTo)
27 | .observeOn(executors.diskIo())
28 | .doOnSuccess(this::saveToDb)
29 | .flatMapPublisher { openDbConnection() }
30 | }
31 |
32 | /**
33 | * Makes a query to the database to check if the value exists. Returns a Single of the value
34 | * if it exists or an error if it doesn't exist.
35 | */
36 | abstract fun checkDb(): Single
37 |
38 | /**
39 | * Opens a Flowable connection to the database that emits new values whenever an update to the
40 | * database occurs.
41 | */
42 | abstract fun openDbConnection(): Flowable
43 |
44 | /**
45 | * Returns a boolean which is true if the NetworkBoundResource should fetch fresh data from the
46 | * server or false if it should just return the data cached in the local database.
47 | */
48 | abstract fun shouldFetch(data: LocalType): Boolean
49 |
50 | @WorkerThread abstract fun saveToDb(data: LocalType)
51 |
52 | abstract fun fetchFromNetwork(): Single
53 |
54 | /**
55 | * Maps the remote data from the network to a type that can be stored locally in the database.
56 | */
57 | abstract fun mapTo(data: RemoteType): LocalType
58 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/build.gradle:
--------------------------------------------------------------------------------
1 | import dependencies.Plugins
2 | import dependencies.Versions
3 |
4 | apply plugin: "com.github.ben-manes.versions"
5 |
6 | allprojects {
7 | repositories {
8 | google()
9 | mavenCentral()
10 | jcenter()
11 | }
12 |
13 | // Show all compile time errors
14 | // See: https://movieos.org/2017/android-room-data-binding-compile-time-errors/
15 | gradle.projectsEvaluated {
16 | tasks.withType(JavaCompile) {
17 | options.compilerArgs << "-Xmaxerrs" << "4000"
18 | options.compilerArgs << "-Xmaxwarns" << "4000"
19 | }
20 | }
21 |
22 | // Improve the build performance of Dagger by
23 | // 1. Not formatting the generated source code
24 | // 2. Turning on incremental compilation
25 | afterEvaluate {
26 | extensions.findByName("kapt")?.arguments {
27 | arg("dagger.formatGeneratedSource", "disabled")
28 | arg("dagger.gradle.incremental", "enabled")
29 | }
30 | }
31 | }
32 |
33 | subprojects { subProject ->
34 | configurations.all {
35 | resolutionStrategy {
36 | eachDependency { details ->
37 | // Force all of the Kotlin libraries to use the same version
38 | if (details.requested.group == 'org.jetbrains.kotlin') {
39 | details.useVersion Versions.kotlin
40 | }
41 | }
42 | }
43 | }
44 | }
45 |
46 | buildscript {
47 | ext.isCiBuild = System.getenv("CI") == 'true'
48 |
49 | repositories {
50 | google()
51 | mavenCentral()
52 | maven { url 'https://maven.fabric.io/public' }
53 | maven { url "https://plugins.gradle.org/m2/" }
54 | jcenter()
55 | }
56 |
57 | dependencies {
58 | classpath Plugins.android
59 | classpath Plugins.kotlin
60 | classpath Plugins.versioning
61 | }
62 |
63 | configurations.all {
64 | resolutionStrategy {
65 | eachDependency { details ->
66 | // Force all of the Kotlin libraries to use the same version
67 | if (details.requested.group == 'org.jetbrains.kotlin') {
68 | details.useVersion Versions.kotlin
69 | }
70 | }
71 | }
72 | }
73 | }
74 |
75 | def propOrEmpty(String name) {
76 | return hasProperty(name) ? getProperty(name) : ''
77 | }
78 |
79 | task clean(type: Delete) {
80 | delete rootProject.buildDir
81 | }
82 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/lint/src/main/java/{{ cookiecutter.package_dir }}/lint/TodoDetector.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.lint
2 |
3 | import com.android.tools.lint.detector.api.Category
4 | import com.android.tools.lint.detector.api.Context
5 | import com.android.tools.lint.detector.api.Detector
6 | import com.android.tools.lint.detector.api.Detector.GradleScanner
7 | import com.android.tools.lint.detector.api.Detector.OtherFileScanner
8 | import com.android.tools.lint.detector.api.Detector.XmlScanner
9 | import com.android.tools.lint.detector.api.Detector.UastScanner
10 | import com.android.tools.lint.detector.api.Implementation
11 | import com.android.tools.lint.detector.api.Issue
12 | import com.android.tools.lint.detector.api.Location
13 | import com.android.tools.lint.detector.api.Scope
14 | import com.android.tools.lint.detector.api.Severity
15 | import com.android.tools.lint.detector.api.XmlContext
16 | import org.jetbrains.uast.UClass
17 | import org.jetbrains.uast.UElement
18 | import org.w3c.dom.Document
19 | import java.util.regex.Pattern
20 |
21 | private const val PRIORITY = 6
22 | private const val COMMENT = "TODO"
23 | private val pattern = Pattern.compile("[\\t]*[//].*$COMMENT.*", Pattern.CASE_INSENSITIVE)
24 |
25 | val ISSUE_TODO = Issue.create(
26 | "UnresolvedTodo",
27 | "Unresolved todo",
28 | """This check highlights comments indicating that some part of the code is unresolved.
29 | Please address and remove all **TODO** comments before ship!
30 | """.trimIndent(),
31 | Category.CORRECTNESS,
32 | PRIORITY,
33 | Severity.WARNING,
34 | Implementation(TodoDetector::class.java, Scope.JAVA_FILE_SCOPE)
35 | )
36 |
37 | /**
38 | * Detects & warns about TODO usage in code.
39 | */
40 | class TodoDetector : Detector(), UastScanner, GradleScanner, OtherFileScanner, XmlScanner {
41 |
42 | override fun getApplicableUastTypes(): List>? = listOf(UClass::class.java)
43 |
44 | override fun visitDocument(context: XmlContext, document: Document) {
45 | // Do nothing, work done in afterCheckFile
46 | }
47 |
48 | override fun afterCheckFile(context: Context) {
49 | val source = context.getContents().toString()
50 | val matcher = pattern.matcher(source)
51 |
52 | while (matcher.find()) {
53 | val start = matcher.start()
54 | val end = matcher.end()
55 | val location = Location.create(context.file, source, start, end)
56 | context.report(ISSUE_TODO, location, "TODO comment found")
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/buildSrc/src/main/java/plugins/AppCenterConfigPlugin.kt:
--------------------------------------------------------------------------------
1 | package plugins
2 |
3 | import com.betomorrow.gradle.appcenter.extensions.AppCenterExtension
4 | import org.gradle.api.Plugin
5 | import org.gradle.api.Project
6 | import org.gradle.kotlin.dsl.getByType
7 | import utils.GitHelper
8 | import utils.propOrEmpty
9 |
10 | class AppCenterConfigPlugin : Plugin {
11 | override fun apply(target: Project) {
12 | if (System.getenv("CI") == "true") {
13 | target.plugins.apply("com.betomorrow.appcenter")
14 | val extension = target.extensions.getByType()
15 | try {
16 | extension.configure(target)
17 | } catch (e: Exception) {
18 | e.printStackTrace()
19 | }
20 | target.afterEvaluate {
21 | tasks.forEach { task ->
22 | if (task.name.contains("appCenterUpload")) {
23 | task.dependsOn(task.name.replace("appCenterUpload", "assemble"))
24 | }
25 | }
26 | }
27 | }
28 | }
29 |
30 | private fun AppCenterExtension.configure(target: Project) {
31 | val gitHelper = GitHelper(target.rootDir)
32 |
33 | apiToken = target.propOrEmpty("APPCENTER_TOKEN")
34 | ownerName = target.propOrEmpty("APPCENTER_OWNER_NAME")
35 | notifyTesters = false
36 | apps {
37 | register("developRelease") {
38 | dimension = "track"
39 | appName = "{{ cookiecutter.app_name | replace(' ', '') }}-Develop"
40 | distributionGroups = listOf("All-users-of-{{ cookiecutter.app_name | replace(' ', '') }}-Develop")
41 | releaseNotes = "${gitHelper.getDevelopReleaseNotes()}"
42 | }
43 | register("sprintRelease") {
44 | dimension = "track"
45 | appName = "{{ cookiecutter.app_name | replace(' ', '') }}-Sprint"
46 | distributionGroups = listOf("All-users-of-{{ cookiecutter.app_name | replace(' ', '') }}-Sprint")
47 | releaseNotes = "${gitHelper.getSprintReleaseNotes()}"
48 | }
49 | register("productionRelease") {
50 | dimension = "track"
51 | appName = "{{ cookiecutter.app_name | replace(' ', '') }}"
52 | distributionGroups = listOf("All-users-of-{{ cookiecutter.app_name | replace(' ', '') }}")
53 | releaseNotes = "${gitHelper.getProductionReleaseNotes()}"
54 | }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/layout/list_item_repo.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
20 |
21 |
27 |
28 |
42 |
43 |
56 |
57 |
--------------------------------------------------------------------------------
/hooks/post_gen_project.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | initialize_git() {
4 | git init
5 | git add -A
6 | }
7 |
8 | install_or_update_ktlint() {
9 | # Check for Homebrew, if it's not installed then install it
10 | which -s brew
11 | if [[ $? != 0 ]] ; then
12 | install_brew_variant
13 | brew install shyiko/ktlint/ktlint
14 | else
15 | brew update
16 | fi
17 | }
18 |
19 | install_brew_variant() {
20 | if [[ "$OSTYPE" == "linux-gnu" ]]; then
21 | install_linuxbrew
22 | elif [[ "$OSTYPE" == "darwin"* ]]; then
23 | install_homebrew
24 | else
25 | echo ERROR: Unsupported operating system
26 | exit 1
27 | fi
28 | }
29 |
30 | install_linuxbrew() {
31 | sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"
32 | test -d ~/.linuxbrew && eval $(~/.linuxbrew/bin/brew shellenv)
33 | test -d /home/linuxbrew/.linuxbrew && eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
34 | test -r ~/.bash_profile && echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.bash_profile
35 | echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.profile
36 | }
37 |
38 | install_homebrew() {
39 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
40 | }
41 |
42 | apply_ktlint_to_project() {
43 | if [[ ! -d .idea ]]; then
44 | mkdir .idea
45 | fi
46 | printf 'y' | ktlint --apply-to-idea-project --android
47 | git add -A
48 | }
49 |
50 | apply_lint_to_project() {
51 | ./gradlew lint:copyLintJar
52 | }
53 |
54 | update_project_permissions() {
55 | cd ..
56 | chmod -R 777 "{{ cookiecutter.repo_name }}"
57 | }
58 |
59 | attempt_to_launch_studio() {
60 | if [[ -z ${CI+x} ]]; then
61 | check_for_launch_flag
62 | else
63 | echo "Skipping the launching of Android Studio because we're building on CI..."
64 | fi
65 | }
66 |
67 | check_for_launch_flag() {
68 | if [[ "{{ cookiecutter.launch_studio }}" = true ]]; then
69 | launch_studio
70 | else
71 | echo "Skipping the launching of Android Studio..."
72 | fi
73 | }
74 |
75 | launch_studio() {
76 | if [[ "$OSTYPE" == "darwin"* ]]; then
77 | /Applications/Android\ Studio.app/Contents/MacOS/studio $PWD/"{{ cookiecutter.repo_name }}"
78 | echo "Android Studio should now be running, have fun with your new project!"
79 | else
80 | echo "Unsupported operating system: skipping the launching of Android Studio..."
81 | fi
82 | }
83 |
84 | initialize_git
85 | install_or_update_ktlint
86 | apply_ktlint_to_project
87 | apply_lint_to_project
88 | update_project_permissions
89 | attempt_to_launch_studio
--------------------------------------------------------------------------------
/github-mvi-android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/layout/layout_latest_commit.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
20 |
21 |
33 |
34 |
48 |
49 |
60 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/gradle/coverage.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'jacoco'
2 |
3 | def fileFilter = [
4 | '**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*', '**/*$[0-9].*',
5 | ]
6 | def mainSrc = "$project.projectDir/src/main/kotlin"
7 | def configDir = "${project.rootDir}/config"
8 | def reportDir = "${project.buildDir}/reports"
9 | jacoco.toolVersion = "0.8.4"
10 |
11 | def MINIMUM_COVERAGE = 0.60 // The minimum code-coverage percentage
12 |
13 | tasks.withType(Test) {
14 | jacoco.includeNoLocationClasses = true
15 | }
16 |
17 | project.afterEvaluate {
18 | def productFlavors = android.productFlavors.collect { flavor ->
19 | flavor.name
20 | }
21 | if (!productFlavors) productFlavors.add('')
22 |
23 | //iterate over the flavors
24 |
25 | productFlavors.each {
26 |
27 | productFlavorName ->
28 | def sourceName, sourcePath
29 | def buildTypeName = 'debug'
30 |
31 | if (!productFlavorName) {
32 | sourceName = sourcePath = "${buildTypeName}"
33 | } else {
34 | sourceName = "${productFlavorName}${buildTypeName.capitalize()}"
35 | sourcePath = "${productFlavorName}/${buildTypeName}"
36 | }
37 |
38 | def debugTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/${sourceName}", excludes: fileFilter)
39 | def testTaskName = "test${sourceName.capitalize()}UnitTest"
40 | def coverageReportTaskName = "create${sourceName.capitalize()}CoverageReport"
41 | task "${testTaskName}Coverage"(type: JacocoReport, dependsOn: ["${testTaskName}", "${coverageReportTaskName}"]) {
42 | group = 'Reporting'
43 | reports {
44 | xml.enabled = true
45 | html.enabled = true
46 | }
47 |
48 | sourceDirectories = files([mainSrc])
49 | classDirectories = files([debugTree])
50 | executionData = fileTree(dir: project.buildDir, includes: [
51 | "jacoco/${testTaskName}.exec", "outputs/code_coverage/${sourceName}AndroidTest/connected/*coverage.ec"
52 | ])
53 | }
54 |
55 | task "${testTaskName}CoverageVerification"(type: JacocoCoverageVerification, dependsOn: "${testTaskName}Coverage") {
56 | group = 'Verification'
57 | sourceDirectories = files([mainSrc])
58 | classDirectories = files([debugTree])
59 | executionData = fileTree(dir: project.buildDir, includes: [
60 | "jacoco/${testTaskName}.exec", "outputs/code_coverage/${sourceName}AndroidTest/connected/*coverage.ec"
61 | ])
62 | violationRules {
63 | failOnViolation = true
64 | rule {
65 | limit {
66 | minimum = MINIMUM_COVERAGE
67 | }
68 | }
69 | }
70 | }
71 |
72 | }
73 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/res/layout/list_item_repo_grid.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
22 |
23 |
35 |
36 |
44 |
45 |
55 |
56 |
60 |
61 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/github-mvi-android/data/src/main/java/com/rightpoint/mvi/example/data/repository/repo/DefaultRepoRepository.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.data.repository.repo
2 |
3 | import android.content.SharedPreferences
4 | import com.rightpoint.mvi.example.cache.room.AppDatabase
5 | import com.rightpoint.mvi.example.cache.room.model.RepoEntity
6 | import com.rightpoint.mvi.example.data.NetworkBoundResource
7 | import com.rightpoint.mvi.example.data.mapper.entities.RepoEntityMapper
8 | import com.rightpoint.mvi.example.data.mapper.model.RepoModelMapper
9 | import com.rightpoint.mvi.example.data.utils.getInstant
10 | import com.rightpoint.mvi.example.data.utils.putInstant
11 | import com.rightpoint.mvi.example.domain.executors.AppExecutors
12 | import com.rightpoint.mvi.example.domain.model.RepoModel
13 | import com.rightpoint.mvi.example.domain.repository.repo.RepoRepository
14 | import com.rightpoint.mvi.example.remote.GithubApi
15 | import com.rightpoint.mvi.example.remote.model.Repo
16 | import io.reactivex.Flowable
17 | import io.reactivex.Single
18 | import org.threeten.bp.Duration
19 | import org.threeten.bp.Instant
20 | import javax.inject.Inject
21 |
22 | class DefaultRepoRepository @Inject constructor(
23 | private val executors: AppExecutors,
24 | private val api: GithubApi,
25 | private val database: AppDatabase,
26 | private val entityMapper: RepoEntityMapper,
27 | private val modelMapper: RepoModelMapper,
28 | private val preferences: SharedPreferences
29 | ) : RepoRepository {
30 | override fun getListOfReposByOrg(org: String): Flowable> {
31 | val dao = database.repoDao()
32 | return object : NetworkBoundResource, List>(executors) {
33 | override fun checkDb(): Single> {
34 | return dao.checkReposByOrg(org)
35 | }
36 |
37 | override fun openDbConnection(): Flowable> {
38 | return dao.getAll()
39 | }
40 |
41 | override fun shouldFetch(data: List): Boolean {
42 | val cacheInstant = preferences.getInstant(org)
43 | return data.isEmpty() ||
44 | Duration.between(cacheInstant, Instant.now()).toMinutes() > 10
45 | }
46 |
47 | override fun saveToDb(data: List) {
48 | dao.insertAll(data)
49 | preferences.edit()
50 | .putInstant(org, Instant.now())
51 | .apply()
52 | }
53 |
54 | override fun fetchFromNetwork(): Single> {
55 | return api.getListOfReposByOrg(org)
56 | }
57 |
58 | override fun mapTo(data: List): List {
59 | return data.map(entityMapper::map)
60 | }
61 | }.asFlowable().map { it.map(modelMapper::map) }
62 | }
63 |
64 | override fun getRepo(owner: String, repo: String): Flowable {
65 | TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
66 | }
67 | }
--------------------------------------------------------------------------------
/github-mvi-android/app/src/main/java/com/rightpoint/mvi/example/repo/RepoListFragment.kt:
--------------------------------------------------------------------------------
1 | package com.rightpoint.mvi.example.repo
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.recyclerview.widget.GridLayoutManager
8 | import com.jakewharton.rxrelay2.PublishRelay
9 | import com.rightpoint.common.android.list.BaseAdapter
10 | import com.rightpoint.common.android.list.Item
11 | import com.rightpoint.common.exhaustive
12 | import com.rightpoint.mvi.example.R
13 | import com.rightpoint.mvi.example.commit.CommitBottomSheetDialog
14 | import com.uber.autodispose.android.lifecycle.scope
15 | import com.uber.autodispose.kotlin.autoDisposable
16 | import dagger.android.support.DaggerFragment
17 | import io.reactivex.android.schedulers.AndroidSchedulers
18 | import io.reactivex.functions.Consumer
19 | import io.reactivex.schedulers.Schedulers
20 | import kotlinx.android.synthetic.main.fragment_repo_list_loading.*
21 | import timber.log.Timber
22 | import javax.inject.Inject
23 |
24 | class RepoListFragment : DaggerFragment(), Consumer {
25 | private val actions = PublishRelay.create()
26 | private val adapter = BaseAdapter- ()
27 |
28 | @Inject lateinit var viewModel: RepoListViewModel
29 |
30 | val onCommitClick = { name: String ->
31 | CommitBottomSheetDialog.newInstance("Rightpoint", name)
32 | .show(childFragmentManager, "Commit")
33 | }
34 |
35 | override fun onCreate(savedInstanceState: Bundle?) {
36 | super.onCreate(savedInstanceState)
37 | actions.compose(viewModel.model())
38 | .subscribeOn(Schedulers.io())
39 | .observeOn(AndroidSchedulers.mainThread())
40 | .autoDisposable(lifecycle.scope())
41 | .subscribe(this, Consumer { Timber.e(it) })
42 | }
43 |
44 | override fun onCreateView(
45 | inflater: LayoutInflater,
46 | container: ViewGroup?,
47 | savedInstanceState: Bundle?
48 | ): View? {
49 | return inflater.inflate(R.layout.fragment_repo_list_loading, container, false)
50 | }
51 |
52 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
53 | super.onViewCreated(view, savedInstanceState)
54 | rootLayout.loadLayoutDescription(R.xml.repo_list_layout_states)
55 | setUpRecyclerView()
56 | actions.accept(Action.LoadListOfRepos("Rightpoint"))
57 | }
58 |
59 | private fun setUpRecyclerView() {
60 | repoList.layoutManager = GridLayoutManager(requireContext(), 2)
61 | repoList.adapter = adapter
62 | }
63 |
64 | override fun accept(state: State) {
65 | when (state) {
66 | State.Loading -> {
67 | rootLayout?.setState(R.id.loading, 0, 0)
68 | }
69 | is State.Loaded -> {
70 | adapter.submitList(state.data)
71 | rootLayout?.setState(R.id.content, 0, 0)
72 | }
73 | State.Empty -> {
74 | // TODO: handle the empty state
75 | }
76 | is State.Error -> {
77 | // TODO: handle the error state
78 | }
79 | }.exhaustive
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/lint/src/test/java/{{ cookiecutter.package_dir }}/lint/DefaultLayoutAttributeTest.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.lint
2 |
3 | import com.android.tools.lint.checks.infrastructure.TestFiles.xml
4 | import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
5 | import org.junit.Test
6 |
7 | class DefaultLayoutAttributeTest {
8 | @Test
9 | fun testTextStyleNormal() {
10 | val xml = xml("res/layout/ids.xml", """
11 | """).indented()
17 |
18 | val expected = """
19 | |res/layout/ids.xml:6: Warning: This is the default and hence you don't need to specify it. [DefaultLayoutAttribute]
20 | | android:textStyle="normal"/>
21 | | ~~~~~~
22 | |0 errors, 1 warnings
23 | """.trimMargin()
24 |
25 | lint().files(xml).issues(ISSUE_DEFAULT_LAYOUT_ATTRIBUTE).run().expect(expected)
26 | }
27 |
28 | @Test
29 | fun testNoAttribute() {
30 | val xml = xml("res/layout/ids.xml", """
31 |
36 | """).indented()
37 |
38 | lint().files(xml).issues(ISSUE_DEFAULT_LAYOUT_ATTRIBUTE).run().expectClean()
39 | }
40 |
41 | @Test
42 | fun testTextStyleItalic() {
43 | val xml = xml("res/layout/ids.xml", """
44 | """).indented()
50 |
51 | lint().files(xml).issues(ISSUE_DEFAULT_LAYOUT_ATTRIBUTE).run().expectClean()
52 | }
53 |
54 | // if the DefaultLayoutAttribute is ignored in the tools namespace
55 | @Test
56 | fun testTextStyleIgnored() {
57 | val xml = xml("res/layout/ids.xml", """
58 | """).indented()
65 |
66 | lint().files(xml).issues(ISSUE_DEFAULT_LAYOUT_ATTRIBUTE).run().expectClean()
67 | }
68 |
69 | @Test fun shouldNotCrashWithStyle() {
70 | lint()
71 | .files(xml("res/layout/ids.xml", """
72 | """).indented())
74 | .issues(ISSUE_DEFAULT_LAYOUT_ATTRIBUTE)
75 | .run()
76 | .expectClean()
77 | }
78 | }
--------------------------------------------------------------------------------
/{{ cookiecutter.repo_name }}/lint/src/main/java/{{ cookiecutter.package_dir }}/lint/NamingPatternDetector.kt:
--------------------------------------------------------------------------------
1 | package {{ cookiecutter.package_name }}.lint
2 |
3 | import com.android.tools.lint.client.api.UElementHandler
4 | import com.android.tools.lint.detector.api.Category.Companion.CORRECTNESS
5 | import com.android.tools.lint.detector.api.Detector
6 | import com.android.tools.lint.detector.api.Implementation
7 | import com.android.tools.lint.detector.api.Issue
8 | import com.android.tools.lint.detector.api.JavaContext
9 | import com.android.tools.lint.detector.api.Scope.JAVA_FILE
10 | import com.android.tools.lint.detector.api.Scope.TEST_SOURCES
11 | import com.android.tools.lint.detector.api.Severity.WARNING
12 | import com.intellij.psi.PsiNamedElement
13 | import org.jetbrains.kotlin.psi.KtProperty
14 | import org.jetbrains.uast.UClass
15 | import org.jetbrains.uast.UElement
16 | import org.jetbrains.uast.UEnumConstant
17 | import org.jetbrains.uast.UMethod
18 | import org.jetbrains.uast.UVariable
19 | import org.jetbrains.uast.kotlin.declarations.KotlinUMethod
20 | import java.util.EnumSet
21 |
22 | private const val PRIORITY = 3
23 |
24 | val ISSUE_NAMING_PATTERN = Issue.create("NamingPattern",
25 | "Names should be well named.",
26 | """Sometimes there is more than one reasonable way to convert an English phrase into
27 | | camel case, such as when acronyms or unusual constructs like "IPv6" or "iOS" are
28 | | present. XML HTTP request becomes XmlHttpRequest.
29 | | XMLHTTPRequest would be incorrect.""".trimMargin(),
30 | CORRECTNESS, PRIORITY, WARNING,
31 | Implementation(NamingPatternDetector::class.java, EnumSet.of(JAVA_FILE, TEST_SOURCES)))
32 |
33 | class NamingPatternDetector : Detector(), Detector.UastScanner {
34 | override fun getApplicableUastTypes() = listOf>(UVariable::class.java,
35 | UMethod::class.java, UClass::class.java)
36 |
37 | override fun createUastHandler(context: JavaContext) = NamingPatternHandler(context)
38 |
39 | class NamingPatternHandler(private val context: JavaContext) : UElementHandler() {
40 | override fun visitVariable(node: UVariable) {
41 | val isConstant = node.isFinal && node.isStatic
42 | val isEnumConstant = node is UEnumConstant
43 |
44 | if (!isConstant && !isEnumConstant) {
45 | process(node, node)
46 | }
47 | }
48 |
49 | override fun visitMethod(node: UMethod) {
50 | process(node, node)
51 | }
52 |
53 | override fun visitClass(node: UClass) {
54 | process(node, node)
55 | }
56 |
57 | private fun process(scope: UElement, declaration: PsiNamedElement) {
58 | val isPropertyBakedMethod = scope is KotlinUMethod &&
59 | (scope.sourcePsi as? KtProperty)?.isMember == true
60 |
61 | if (declaration.name?.isDefinedCamelCase() == false && !isPropertyBakedMethod) {
62 | context.report(ISSUE_NAMING_PATTERN, scope, context.getNameLocation(scope),
63 | "${declaration.name} is not named in defined camel case.")
64 | }
65 | }
66 | }
67 | }
68 |
69 | private fun String.isDefinedCamelCase(): Boolean {
70 | val toCharArray = toCharArray()
71 | return toCharArray
72 | .mapIndexed { index, current -> current to toCharArray.getOrNull(index + 1) }
73 | .none { it.first.isUpperCase() && it.second?.isUpperCase() ?: false }
74 | }
--------------------------------------------------------------------------------