├── .travis.yml
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
├── LICENSE
├── README.md
├── code-of-conduct.md
└── todoapp
├── .gitignore
├── CONTRIBUTING.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── proguardTest-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── android
│ │ └── architecture
│ │ └── blueprints
│ │ └── todoapp
│ │ ├── TestUtils.java
│ │ ├── custom
│ │ └── action
│ │ │ └── NavigationViewActions.java
│ │ ├── data
│ │ └── TasksLocalDataSourceTest.java
│ │ └── tasks
│ │ ├── AppNavigationTest.java
│ │ └── TasksScreenTest.java
│ ├── androidTestMock
│ └── java
│ │ └── com
│ │ └── example
│ │ └── android
│ │ └── architecture
│ │ └── blueprints
│ │ └── todoapp
│ │ ├── addedittask
│ │ └── AddEditTaskScreenTest.java
│ │ ├── statistics
│ │ └── StatisticsScreenTest.java
│ │ └── taskdetail
│ │ └── TaskDetailScreenTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── android
│ │ │ └── architecture
│ │ │ └── blueprints
│ │ │ └── todoapp
│ │ │ ├── BasePresenter.java
│ │ │ ├── BaseView.java
│ │ │ ├── UseCaseRx.java
│ │ │ ├── addedittask
│ │ │ ├── AddEditTaskActivity.java
│ │ │ ├── AddEditTaskContract.java
│ │ │ ├── AddEditTaskFragment.java
│ │ │ ├── AddEditTaskPresenter.java
│ │ │ └── domain
│ │ │ │ └── usecase
│ │ │ │ ├── DeleteTask.java
│ │ │ │ ├── GetTask.java
│ │ │ │ └── SaveTask.java
│ │ │ ├── data
│ │ │ └── source
│ │ │ │ ├── TasksDataSource.java
│ │ │ │ ├── TasksRepository.java
│ │ │ │ ├── local
│ │ │ │ ├── TasksDbHelper.java
│ │ │ │ ├── TasksLocalDataSource.java
│ │ │ │ └── TasksPersistenceContract.java
│ │ │ │ └── remote
│ │ │ │ └── TasksRemoteDataSource.java
│ │ │ ├── statistics
│ │ │ ├── StatisticsActivity.java
│ │ │ ├── StatisticsContract.java
│ │ │ ├── StatisticsFragment.java
│ │ │ ├── StatisticsPresenter.java
│ │ │ └── domain
│ │ │ │ ├── model
│ │ │ │ └── Statistics.java
│ │ │ │ └── usecase
│ │ │ │ └── GetStatistics.java
│ │ │ ├── taskdetail
│ │ │ ├── TaskDetailActivity.java
│ │ │ ├── TaskDetailContract.java
│ │ │ ├── TaskDetailFragment.java
│ │ │ └── TaskDetailPresenter.java
│ │ │ ├── tasks
│ │ │ ├── ScrollChildSwipeRefreshLayout.java
│ │ │ ├── TasksActivity.java
│ │ │ ├── TasksContract.java
│ │ │ ├── TasksFilterType.java
│ │ │ ├── TasksFragment.java
│ │ │ ├── TasksPresenter.java
│ │ │ └── domain
│ │ │ │ ├── filter
│ │ │ │ ├── ActiveTaskFilter.java
│ │ │ │ ├── CompleteTaskFilter.java
│ │ │ │ ├── FilterAllTaskFilter.java
│ │ │ │ ├── FilterFactory.java
│ │ │ │ └── TaskFilter.java
│ │ │ │ ├── model
│ │ │ │ └── Task.java
│ │ │ │ └── usecase
│ │ │ │ ├── ActivateTask.java
│ │ │ │ ├── ClearCompleteTasks.java
│ │ │ │ ├── CompleteTask.java
│ │ │ │ └── GetTasks.java
│ │ │ └── util
│ │ │ ├── ActivityUtils.java
│ │ │ ├── EspressoIdlingResource.java
│ │ │ └── SimpleCountingIdlingResource.java
│ └── res
│ │ ├── drawable-hdpi
│ │ └── logo.png
│ │ ├── drawable-mdpi
│ │ └── logo.png
│ │ ├── drawable-xhdpi
│ │ └── logo.png
│ │ ├── drawable-xxhdpi
│ │ └── logo.png
│ │ ├── drawable-xxxhdpi
│ │ └── logo.png
│ │ ├── drawable
│ │ ├── ic_add.xml
│ │ ├── ic_assignment_turned_in_24dp.xml
│ │ ├── ic_check_circle_24dp.xml
│ │ ├── ic_done.xml
│ │ ├── ic_edit.xml
│ │ ├── ic_filter_list.xml
│ │ ├── ic_list.xml
│ │ ├── ic_menu.xml
│ │ ├── ic_statistics.xml
│ │ ├── ic_statistics_100dp.xml
│ │ ├── ic_statistics_24dp.xml
│ │ ├── ic_verified_user_24dp.xml
│ │ ├── list_completed_touch_feedback.xml
│ │ └── touch_feedback.xml
│ │ ├── layout
│ │ ├── addtask_act.xml
│ │ ├── addtask_frag.xml
│ │ ├── nav_header.xml
│ │ ├── statistics_act.xml
│ │ ├── statistics_frag.xml
│ │ ├── task_item.xml
│ │ ├── taskdetail_act.xml
│ │ ├── taskdetail_frag.xml
│ │ ├── tasks_act.xml
│ │ └── tasks_frag.xml
│ │ ├── menu
│ │ ├── drawer_actions.xml
│ │ ├── filter_tasks.xml
│ │ ├── taskdetail_fragment_menu.xml
│ │ └── tasks_fragment_menu.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-v21
│ │ └── styles.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── mock
│ └── java
│ │ └── com
│ │ └── example
│ │ └── android
│ │ └── architecture
│ │ └── blueprints
│ │ └── todoapp
│ │ ├── Injection.java
│ │ └── data
│ │ └── FakeTasksRemoteDataSource.java
│ ├── prod
│ └── java
│ │ └── com
│ │ └── example
│ │ └── android
│ │ └── architecture
│ │ └── blueprints
│ │ └── todoapp
│ │ └── Injection.java
│ └── test
│ └── java
│ └── com
│ └── example
│ └── android
│ └── architecture
│ └── blueprints
│ └── todoapp
│ ├── addedittask
│ └── AddEditTaskPresenterTest.java
│ ├── data
│ └── source
│ │ └── TasksRepositoryTest.java
│ ├── statistics
│ └── StatisticsPresenterTest.java
│ ├── taskdetail
│ └── TaskDetailPresenterTest.java
│ └── tasks
│ └── TasksPresenterTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | android:
3 | components:
4 | - tools
5 | - platform-tools
6 | - build-tools-24.0.2
7 | - android-24
8 | - extra-android-m2repository
9 | jdk:
10 | - oraclejdk8
11 | script:
12 | - cd todoapp
13 | - ./gradlew test
14 | before_cache:
15 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
16 | cache:
17 | directories:
18 | - $HOME/.m2
19 | - $HOME/.gradle/caches/
20 | - $HOME/.gradle/wrapper/
21 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to become a contributor and submit your own code
2 |
3 | To contribute with a small fix, simply create a pull request against the appropriate branch. This branch is usually [todo-mvp](https://github.com/googlesamples/android-architecture/tree/todo-mvp) unless the change is related to a variant's implementation. See [Development branches](https://github.com/googlesamples/android-architecture/wiki/Development-branches) for more information or create an [issue](https://github.com/googlesamples/android-architecture/issues) if you're not sure what branch to target.
4 |
5 | Before starting work on new sample intended for submission, please open an issue to discuss it with the team. This will allow us to review the architecture and frameworks used to determine if a spec-compatible app is likely to be accepted.
6 |
7 | Please note that this project is released with a [Contributor Code of Conduct](https://github.com/googlesamples/android-architecture/blob/master/code-of-conduct.md). By participating in this project you agree to abide by its terms.
8 |
9 |
10 | ## Code style and structure
11 |
12 | Please check out the [Code Style for Contributors](https://source.android.com/source/code-style.html) section in AOSP. Also, check out the rest of the samples and maintain as much consistency with them as possible.
13 |
14 | ## Contributor License Agreements
15 |
16 | We'd love to accept your sample apps and patches! Before we can take them, we
17 | have to jump a couple of legal hurdles.
18 |
19 | Please fill out either the individual or corporate Contributor License Agreement (CLA).
20 |
21 | * If you are an individual writing original source code and you're sure you
22 | own the intellectual property, then you'll need to sign an [individual CLA]
23 | (https://cla.developers.google.com).
24 | * If you work for a company that wants to allow you to contribute your work,
25 | then you'll need to sign a [corporate CLA]
26 | (https://cla.developers.google.com).
27 | * Please make sure you sign both, Android and Google CLA
28 |
29 | Follow either of the two links above to access the appropriate CLA and
30 | instructions for how to sign and return it. Once we receive it, we'll be able to
31 | accept your pull requests.
32 |
33 | ## Contributing A Patch
34 |
35 | 1. Submit an issue describing your proposed change to the repo in question.
36 | 1. The repo owner will respond to your issue promptly.
37 | 1. If your proposed change is accepted, and you haven't already done so, sign a
38 | Contributor License Agreement (see details above).
39 | 1. Fork the desired repo, develop and test your code changes.
40 | 1. Ensure that your code adheres to the existing style in the sample to which
41 | you are contributing. Refer to the
42 | [Android Code Style Guide]
43 | (https://source.android.com/source/code-style.html) for the
44 | recommended coding standards for this organization.
45 | 1. Ensure that your code has an appropriate set of tests which all pass.
46 | 1. Submit a pull request.
47 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | /* Remove this comment
2 | Please add the branch/sample ("all", "todo-mvp", "todo-mvp-dagger", etc.)
3 | and include it in the title if it applies.
4 | */
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android Architecture Blueprints [beta] - MVP + Clean Architecture
2 |
3 | Project owner: Jorge J. Barroso ([Karumi](http://github.com/Karumi))
4 |
5 | ### Summary
6 | This sample stands on the principles of [Clean Architecture](https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html).
7 |
8 | It's based on the [MVP sample](https://github.com/googlesamples/android-architecture/tree/todo-mvp), adding a domain layer between the presentation layer and repositories, splitting the app in three layers:
9 |
10 |
11 |
12 | * **MVP**: Model View Presenter pattern from the base sample.
13 | * **Domain**: Holds all business logic. The domain layer starts with classes named *use cases* or *interactors* used by the application presenters. These *use cases* represent all the possible actions a developer can perform from the presentation layer.
14 | * **Repository**: Repository pattern from the base sample.
15 |
16 | ### Key concepts
17 | The big difference with base MVP sample is the use of the Domain layer and *use cases*. Moving the domain layer from the presenters will help to avoid code repetition on presenters (e.g. [Task filters](https://github.com/googlesamples/android-architecture/tree/todo-mvp-clean/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/filter)).
18 |
19 | *Use cases* define the operations that the app needs. This increases readability since the names of the classes make the purpose obvious (see [tasks/domain/usecase/](https://github.com/googlesamples/android-architecture/tree/todo-mvp-clean/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/usecase)).
20 |
21 | *Use cases* are good for operation reuse over our domain code. [`CompleteTask`] (https://github.com/googlesamples/android-architecture/blob/todo-mvp-clean/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/usecase/CompleteTask.java) is a good example of this as it's used from both the [`TaskDetailPresenter`](https://github.com/googlesamples/android-architecture/blob/todo-mvp-clean/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailPresenter.java) and the [`TasksPresenter`](https://github.com/googlesamples/android-architecture/blob/todo-mvp-clean/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksPresenter.java).
22 |
23 | The execution of these *use cases* is done in a background thread using the [command pattern](http://www.oodesign.com/command-pattern.html). The domain layer is completely decoupled from the Android SDK or other third party libraries.
24 |
25 | ### Issues/notes
26 | *Use cases* run off the main thread, which is a good solution for Android apps. This is done as soon as possible to avoid blocking the UI thread. We decided to use a command pattern and execute each use case with a thread pool, but we can implement the same with RxJava or Promises.
27 |
28 | We are using asynchronous repositories, but there's no need to do this any more because use cases execute off the main thread. This is kept to maintain the sample as similar as possible to the original one.
29 |
30 | We recommend using different models for View, domain and API layers, but in this case all models are immutable so there's no need to duplicate them. If View models contained any Android-related fields, we would use two models, one for domain and other for View and a mapper class that converts between them.
31 |
32 | Callbacks have an `onError` method that in a real app should contain information about the problem.
33 |
34 | ### Testability
35 |
36 | With this approach, all domain code is tested with unit tests. This can be extended with integration tests, that cover from Use Cases to the boundaries of the view and repository.
37 |
38 | ### Dependencies
39 |
40 | Apart from support and testing libraries, none.
41 |
42 | ## Features
43 |
44 | ### Complexity - understandability
45 |
46 | #### Use of architectural frameworks/libraries/tools:
47 |
48 | None
49 |
50 | #### Conceptual complexity
51 |
52 | Medium-Low, it's an MVP approach with a new layer that handles domain logic.
53 |
54 | ### Code metrics
55 |
56 |
57 | Adding a domain layer produces more classes and Java code.
58 |
59 | ```
60 | -------------------------------------------------------------------------------
61 | Language files blank comment code
62 | -------------------------------------------------------------------------------
63 | Java 64 1278 1777 4121 (3450 in MVP)
64 | XML 34 97 337 601
65 | -------------------------------------------------------------------------------
66 | SUM: 98 1375 2114 4722
67 | -------------------------------------------------------------------------------
68 |
69 | ```
70 | ### Maintainability
71 |
72 | #### Ease of amending or adding a feature / Learning cost
73 | Very easy. This approach is more verbose, making the maintenance tasks more obvious.
74 |
75 |
76 |
77 |
78 |
79 | >>>>>>> todo-mvp-clean
80 |
--------------------------------------------------------------------------------
/code-of-conduct.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/todoapp/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 | local.properties
4 | build
5 | .gradle
6 | # Eclipse project files
7 | .project
8 | .settings/
9 | .classpath
10 |
--------------------------------------------------------------------------------
/todoapp/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to become a contributor and submit your own code
2 |
3 | ## Contributor License Agreements
4 |
5 | We'd love to accept your patches! Before we can take them, we
6 | have to jump a couple of legal hurdles.
7 |
8 | ### Before you contribute
9 | Before we can use your code, you must sign the
10 | [Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual)
11 | (CLA), which you can do online. The CLA is necessary mainly because you own the
12 | copyright to your changes, even after your contribution becomes part of our
13 | codebase, so we need your permission to use and distribute your code. We also
14 | need to be sure of various other things—for instance that you'll tell us if you
15 | know that your code infringes on other people's patents. You don't have to sign
16 | the CLA until after you've submitted your code for review and a member has
17 | approved it, but you must do it before we can put your code into our codebase.
18 | Before you start working on a larger contribution, you should get in touch with
19 | us first through the issue tracker with your idea so that we can help out and
20 | possibly guide you. Coordinating up front makes it much easier to avoid
21 | frustration later on.
22 |
23 | ### Code reviews
24 | All submissions, including submissions by project members, require review. We
25 | use Github pull requests for this purpose.
26 |
27 | ### The small print
28 | Contributions made by corporations are covered by a different agreement than
29 | the one above, the
30 | [Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate).
31 |
--------------------------------------------------------------------------------
/todoapp/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/todoapp/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 | buildToolsVersion rootProject.ext.buildToolsVersion
6 |
7 | defaultConfig {
8 | applicationId "com.example.android.architecture.blueprints.todomvpclean"
9 | minSdkVersion rootProject.ext.minSdkVersion
10 | targetSdkVersion rootProject.ext.targetSdkVersion
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
15 | }
16 |
17 | buildTypes {
18 | debug {
19 | testCoverageEnabled = true
20 | minifyEnabled true
21 | // Uses new built-in shrinker http://tools.android.com/tech-docs/new-build-system/built-in-shrinker
22 | useProguard false
23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
24 | testProguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguardTest-rules.pro'
25 | }
26 |
27 | release {
28 | minifyEnabled true
29 | useProguard true
30 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
31 | testProguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguardTest-rules.pro'
32 | }
33 | }
34 |
35 | // If you need to add more flavors, consider using flavor dimensions.
36 | productFlavors {
37 | mock {
38 | applicationIdSuffix = ".mock"
39 | }
40 | prod {
41 |
42 | }
43 | }
44 |
45 | // Remove mockRelease as it's not needed.
46 | android.variantFilter { variant ->
47 | if(variant.buildType.name.equals('release')
48 | && variant.getFlavors().get(0).name.equals('mock')) {
49 | variant.setIgnore(true);
50 | }
51 | }
52 |
53 | // Always show the result of every unit test, even if it passes.
54 | testOptions.unitTests.all {
55 | testLogging {
56 | events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
57 | }
58 | }
59 | }
60 |
61 | /*
62 | Dependency versions are defined in the top level build.gradle file. This helps keeping track of
63 | all versions in a single place. This improves readability and helps managing project complexity.
64 | */
65 | dependencies {
66 | // App's dependencies, including test
67 | compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
68 | compile "com.android.support:cardview-v7:$rootProject.supportLibraryVersion"
69 | compile "com.android.support:design:$rootProject.supportLibraryVersion"
70 | compile "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion"
71 | compile "com.android.support:support-v4:$rootProject.supportLibraryVersion"
72 | compile "com.android.support.test.espresso:espresso-idling-resource:$rootProject.espressoVersion"
73 | compile "com.google.guava:guava:$rootProject.guavaVersion"
74 | compile "io.reactivex:rxjava:$rootProject.rxjavaVersion"
75 | compile "io.reactivex:rxandroid:$rootProject.rxAndroidVersion"
76 |
77 |
78 | // Dependencies for local unit tests
79 | testCompile "junit:junit:$rootProject.ext.junitVersion"
80 | testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
81 | testCompile "org.hamcrest:hamcrest-all:$rootProject.ext.hamcrestVersion"
82 |
83 | // Android Testing Support Library's runner and rules
84 | androidTestCompile "com.android.support.test:runner:$rootProject.ext.runnerVersion"
85 | androidTestCompile "com.android.support.test:rules:$rootProject.ext.runnerVersion"
86 |
87 | // Dependencies for Android unit tests
88 | androidTestCompile "junit:junit:$rootProject.ext.junitVersion"
89 | androidTestCompile "org.mockito:mockito-core:$rootProject.ext.mockitoVersion"
90 | androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
91 | androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
92 |
93 | // Espresso UI Testing
94 | androidTestCompile "com.android.support.test.espresso:espresso-core:$rootProject.espressoVersion"
95 | androidTestCompile "com.android.support.test.espresso:espresso-contrib:$rootProject.espressoVersion"
96 | androidTestCompile "com.android.support.test.espresso:espresso-intents:$rootProject.espressoVersion"
97 |
98 | // Resolve conflicts between main and test APK:
99 | androidTestCompile "com.android.support:support-annotations:$rootProject.supportLibraryVersion"
100 | androidTestCompile "com.android.support:support-v4:$rootProject.supportLibraryVersion"
101 | androidTestCompile "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion"
102 | androidTestCompile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
103 | androidTestCompile "com.android.support:design:$rootProject.supportLibraryVersion"
104 | }
105 |
--------------------------------------------------------------------------------
/todoapp/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Some methods are only called from tests, so make sure the shrinker keeps them.
2 | -keep class com.example.android.architecture.blueprints.** { *; }
3 |
4 | -keep class android.support.v4.widget.DrawerLayout { *; }
5 | -keep class android.support.test.espresso.IdlingResource { *; }
6 | -keep class com.google.common.base.Preconditions { *; }
7 |
8 | # For Guava:
9 | -dontwarn javax.annotation.**
10 | -dontwarn javax.inject.**
11 | -dontwarn sun.misc.Unsafe
12 |
13 | # Proguard rules that are applied to your test apk/code.
14 | -ignorewarnings
15 |
16 | -keepattributes *Annotation*
17 |
18 | -dontnote junit.framework.**
19 | -dontnote junit.runner.**
20 |
21 | -dontwarn android.test.**
22 | -dontwarn android.support.test.**
23 | -dontwarn org.junit.**
24 | -dontwarn org.hamcrest.**
25 | -dontwarn com.squareup.javawriter.JavaWriter
26 | # Uncomment this if you use Mockito
27 | -dontwarn org.mockito.**
28 |
29 |
30 | -keep class rx.** { *; }
31 | -dontwarn rx.**
--------------------------------------------------------------------------------
/todoapp/app/proguardTest-rules.pro:
--------------------------------------------------------------------------------
1 | # Proguard rules that are applied to your test apk/code.
2 | -ignorewarnings
3 |
4 | -keepattributes *Annotation*
5 |
6 | -dontnote junit.framework.**
7 | -dontnote junit.runner.**
8 |
9 | -dontwarn android.test.**
10 | -dontwarn android.support.test.**
11 | -dontwarn org.junit.**
12 | -dontwarn org.hamcrest.**
13 | -dontwarn com.squareup.javawriter.JavaWriter
14 | # Uncomment this if you use Mockito
15 | -dontwarn org.mockito.**
16 |
17 |
18 | -keep class rx.** { *; }
19 | -dontwarn rx.**
--------------------------------------------------------------------------------
/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/TestUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.android.architecture.blueprints.todoapp;
18 |
19 | import static android.support.test.InstrumentationRegistry.getInstrumentation;
20 | import static android.support.test.runner.lifecycle.Stage.RESUMED;
21 |
22 | import android.app.Activity;
23 | import android.content.pm.ActivityInfo;
24 | import android.content.res.Configuration;
25 | import android.support.annotation.IdRes;
26 | import android.support.annotation.NonNull;
27 | import android.support.test.runner.lifecycle.ActivityLifecycleMonitor;
28 | import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
29 | import android.support.v7.widget.Toolbar;
30 |
31 | import java.util.Collection;
32 |
33 | /**
34 | * Useful test methods common to all activities
35 | */
36 | public class TestUtils {
37 |
38 | private static void rotateToLandscape(Activity activity) {
39 | activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
40 | }
41 |
42 | private static void rotateToPortrait(Activity activity) {
43 | activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
44 | }
45 |
46 | public static void rotateOrientation(Activity activity) {
47 | int currentOrientation = activity.getResources().getConfiguration().orientation;
48 |
49 | switch (currentOrientation) {
50 | case Configuration.ORIENTATION_LANDSCAPE:
51 | rotateToPortrait(activity);
52 | break;
53 | case Configuration.ORIENTATION_PORTRAIT:
54 | rotateToLandscape(activity);
55 | break;
56 | default:
57 | rotateToLandscape(activity);
58 | }
59 | }
60 |
61 | /**
62 | * Returns the content description for the navigation button view in the toolbar.
63 | */
64 | public static String getToolbarNavigationContentDescription(
65 | @NonNull Activity activity, @IdRes int toolbar1) {
66 | Toolbar toolbar = (Toolbar) activity.findViewById(toolbar1);
67 | if (toolbar != null) {
68 | return (String) toolbar.getNavigationContentDescription();
69 | } else {
70 | throw new RuntimeException("No toolbar found.");
71 | }
72 | }
73 |
74 | /**
75 | * Gets an Activity in the RESUMED stage.
76 | *
77 | * This method should never be called from the Main thread. In certain situations there might
78 | * be more than one Activities in RESUMED stage, but only one is returned.
79 | * See {@link ActivityLifecycleMonitor}.
80 | */
81 | public static Activity getCurrentActivity() throws IllegalStateException {
82 | // The array is just to wrap the Activity and be able to access it from the Runnable.
83 | final Activity[] resumedActivity = new Activity[1];
84 |
85 | getInstrumentation().runOnMainSync(new Runnable() {
86 | public void run() {
87 | Collection resumedActivities = ActivityLifecycleMonitorRegistry.getInstance()
88 | .getActivitiesInStage(RESUMED);
89 | if (resumedActivities.iterator().hasNext()) {
90 | resumedActivity[0] = (Activity) resumedActivities.iterator().next();
91 | } else {
92 | throw new IllegalStateException("No Activity in stage RESUMED");
93 | }
94 | }
95 | });
96 | return resumedActivity[0];
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/AppNavigationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.android.architecture.blueprints.todoapp.tasks;
18 |
19 | import static android.support.test.espresso.Espresso.onView;
20 | import static android.support.test.espresso.action.ViewActions.click;
21 | import static android.support.test.espresso.assertion.ViewAssertions.matches;
22 | import static android.support.test.espresso.contrib.DrawerActions.open;
23 | import static android.support.test.espresso.contrib.DrawerMatchers.isClosed;
24 | import static android.support.test.espresso.contrib.DrawerMatchers.isOpen;
25 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
26 | import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
27 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
28 |
29 | import static com.example.android.architecture.blueprints.todoapp.TestUtils.getToolbarNavigationContentDescription;
30 | import static com.example.android.architecture.blueprints.todoapp.custom.action.NavigationViewActions.navigateTo;
31 |
32 | import android.support.test.rule.ActivityTestRule;
33 | import android.support.test.runner.AndroidJUnit4;
34 | import android.support.v4.widget.DrawerLayout;
35 | import android.test.suitebuilder.annotation.LargeTest;
36 | import android.view.Gravity;
37 |
38 | import com.example.android.architecture.blueprints.todoapp.R;
39 |
40 | import org.junit.Rule;
41 | import org.junit.Test;
42 | import org.junit.runner.RunWith;
43 |
44 | /**
45 | * Tests for the {@link DrawerLayout} layout component in {@link TasksActivity} which manages
46 | * navigation within the app.
47 | */
48 | @RunWith(AndroidJUnit4.class)
49 | @LargeTest
50 | public class AppNavigationTest {
51 |
52 | /**
53 | * {@link ActivityTestRule} is a JUnit {@link Rule @Rule} to launch your activity under test.
54 | *
55 | *
56 | * Rules are interceptors which are executed for each test method and are important building
57 | * blocks of Junit tests.
58 | */
59 | @Rule
60 | public ActivityTestRule mActivityTestRule =
61 | new ActivityTestRule<>(TasksActivity.class);
62 |
63 | @Test
64 | public void clickOnStatisticsNavigationItem_ShowsStatisticsScreen() {
65 | // Open Drawer to click on navigation.
66 | onView(withId(R.id.drawer_layout))
67 | .check(matches(isClosed(Gravity.LEFT))) // Left Drawer should be closed.
68 | .perform(open()); // Open Drawer
69 |
70 | // Start statistics screen.
71 | onView(withId(R.id.nav_view))
72 | .perform(navigateTo(R.id.statistics_navigation_menu_item));
73 |
74 | // Check that statistics Activity was opened.
75 | onView(withId(R.id.statistics)).check(matches(isDisplayed()));
76 | }
77 |
78 | @Test
79 | public void clickOnListNavigationItem_ShowsListScreen() {
80 | // Open Drawer to click on navigation.
81 | onView(withId(R.id.drawer_layout))
82 | .check(matches(isClosed(Gravity.LEFT))) // Left Drawer should be closed.
83 | .perform(open()); // Open Drawer
84 |
85 | // Start statistics screen.
86 | onView(withId(R.id.nav_view))
87 | .perform(navigateTo(R.id.statistics_navigation_menu_item));
88 |
89 | // Open Drawer to click on navigation.
90 | onView(withId(R.id.drawer_layout))
91 | .check(matches(isClosed(Gravity.LEFT))) // Left Drawer should be closed.
92 | .perform(open()); // Open Drawer
93 |
94 | // Start tasks list screen.
95 | onView(withId(R.id.nav_view))
96 | .perform(navigateTo(R.id.list_navigation_menu_item));
97 |
98 | // Check that Tasks Activity was opened.
99 | onView(withId(R.id.tasksContainer)).check(matches(isDisplayed()));
100 | }
101 |
102 | @Test
103 | public void clickOnAndroidHomeIcon_OpensNavigation() {
104 | // Check that left drawer is closed at startup
105 | onView(withId(R.id.drawer_layout))
106 | .check(matches(isClosed(Gravity.LEFT))); // Left Drawer should be closed.
107 |
108 | // Open Drawer
109 | onView(withContentDescription(getToolbarNavigationContentDescription(
110 | mActivityTestRule.getActivity(), R.id.toolbar))).perform(click());
111 |
112 | // Check if drawer is open
113 | onView(withId(R.id.drawer_layout))
114 | .check(matches(isOpen(Gravity.LEFT))); // Left drawer is open open.
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/todoapp/app/src/androidTestMock/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskScreenTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.android.architecture.blueprints.todoapp.addedittask;
18 |
19 | import static android.support.test.espresso.Espresso.onView;
20 | import static android.support.test.espresso.action.ViewActions.clearText;
21 | import static android.support.test.espresso.action.ViewActions.click;
22 | import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;
23 | import static android.support.test.espresso.action.ViewActions.typeText;
24 | import static android.support.test.espresso.assertion.ViewAssertions.matches;
25 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
26 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
27 |
28 | import android.support.test.espresso.Espresso;
29 | import android.support.test.espresso.intent.rule.IntentsTestRule;
30 | import android.support.test.rule.ActivityTestRule;
31 | import android.support.test.runner.AndroidJUnit4;
32 | import android.test.suitebuilder.annotation.LargeTest;
33 |
34 | import com.example.android.architecture.blueprints.todoapp.R;
35 |
36 | import org.junit.After;
37 | import org.junit.Before;
38 | import org.junit.Rule;
39 | import org.junit.Test;
40 | import org.junit.runner.RunWith;
41 |
42 | /**
43 | * Tests for the add task screen.
44 | */
45 | @RunWith(AndroidJUnit4.class)
46 | @LargeTest
47 | public class AddEditTaskScreenTest {
48 |
49 | /**
50 | * {@link IntentsTestRule} is an {@link ActivityTestRule} which inits and releases Espresso
51 | * Intents before and after each test run.
52 | *
53 | *
54 | * Rules are interceptors which are executed for each test method and are important building
55 | * blocks of Junit tests.
56 | */
57 | @Rule
58 | public IntentsTestRule mAddTaskIntentsTestRule =
59 | new IntentsTestRule<>(AddEditTaskActivity.class);
60 |
61 | /**
62 | * Prepare your test fixture for this test. In this case we register an IdlingResources with
63 | * Espresso. IdlingResource resource is a great way to tell Espresso when your app is in an
64 | * idle state. This helps Espresso to synchronize your test actions, which makes tests significantly
65 | * more reliable.
66 | */
67 | @Before
68 | public void registerIdlingResource() {
69 | Espresso.registerIdlingResources(
70 | mAddTaskIntentsTestRule.getActivity().getCountingIdlingResource());
71 | }
72 |
73 | @Test
74 | public void emptyTask_isNotSaved() {
75 | // Add invalid title and description combination
76 | onView(withId(R.id.add_task_title)).perform(clearText());
77 | onView(withId(R.id.add_task_description)).perform(clearText());
78 | // Try to save the task
79 | onView(withId(R.id.fab_edit_task_done)).perform(click());
80 |
81 | // Verify that the activity is still displayed (a correct task would close it).
82 | onView(withId(R.id.add_task_title)).check(matches(isDisplayed()));
83 | }
84 |
85 | /**
86 | * Unregister your Idling Resource so it can be garbage collected and does not leak any memory.
87 | */
88 | @After
89 | public void unregisterIdlingResource() {
90 | Espresso.unregisterIdlingResources(
91 | mAddTaskIntentsTestRule.getActivity().getCountingIdlingResource());
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/todoapp/app/src/androidTestMock/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsScreenTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.android.architecture.blueprints.todoapp.statistics;
18 |
19 | import static android.support.test.espresso.Espresso.onView;
20 | import static android.support.test.espresso.assertion.ViewAssertions.matches;
21 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
22 | import static android.support.test.espresso.matcher.ViewMatchers.withText;
23 |
24 | import static org.hamcrest.Matchers.containsString;
25 |
26 | import android.content.Intent;
27 | import android.support.test.InstrumentationRegistry;
28 | import android.support.test.espresso.Espresso;
29 | import android.support.test.rule.ActivityTestRule;
30 | import android.support.test.runner.AndroidJUnit4;
31 | import android.test.suitebuilder.annotation.LargeTest;
32 |
33 | import com.example.android.architecture.blueprints.todoapp.R;
34 | import com.example.android.architecture.blueprints.todoapp.data.FakeTasksRemoteDataSource;
35 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.model.Task;
36 | import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository;
37 | import com.example.android.architecture.blueprints.todoapp.taskdetail.TaskDetailActivity;
38 |
39 | import org.junit.Before;
40 | import org.junit.Rule;
41 | import org.junit.Test;
42 | import org.junit.runner.RunWith;
43 |
44 | /**
45 | * Tests for the statistics screen.
46 | */
47 | @RunWith(AndroidJUnit4.class)
48 | @LargeTest
49 | public class StatisticsScreenTest {
50 |
51 | /**
52 | * {@link ActivityTestRule} is a JUnit {@link Rule @Rule} to launch your activity under test.
53 | *
54 | *
55 | * Rules are interceptors which are executed for each test method and are important building
56 | * blocks of Junit tests.
57 | */
58 | @Rule
59 | public ActivityTestRule mStatisticsActivityTestRule =
60 | new ActivityTestRule<>(StatisticsActivity.class, true, false);
61 |
62 | /**
63 | * Setup your test fixture with a fake task id. The {@link TaskDetailActivity} is started with
64 | * a particular task id, which is then loaded from the service API.
65 | *
66 | *
67 | * Note that this test runs hermetically and is fully isolated using a fake implementation of
68 | * the service API. This is a great way to make your tests more reliable and faster at the same
69 | * time, since they are isolated from any outside dependencies.
70 | */
71 | @Before
72 | public void intentWithStubbedTaskId() {
73 | // Given some tasks
74 | TasksRepository.destroyInstance();
75 | FakeTasksRemoteDataSource.getInstance().addTasks(new Task("Title1", "", false));
76 | FakeTasksRemoteDataSource.getInstance().addTasks(new Task("Title2", "", true));
77 |
78 | // Lazily start the Activity from the ActivityTestRule
79 | Intent startIntent = new Intent();
80 | mStatisticsActivityTestRule.launchActivity(startIntent);
81 | /**
82 | * Prepare your test fixture for this test. In this case we register an IdlingResources with
83 | * Espresso. IdlingResource resource is a great way to tell Espresso when your app is in an
84 | * idle state. This helps Espresso to synchronize your test actions, which makes tests significantly
85 | * more reliable.
86 | */
87 | Espresso.registerIdlingResources(
88 | mStatisticsActivityTestRule.getActivity().getCountingIdlingResource());
89 | }
90 |
91 | @Test
92 | public void Tasks_ShowsNonEmptyMessage() throws Exception {
93 | // Check that the active and completed tasks text is displayed
94 | String expectedActiveTaskText = InstrumentationRegistry.getTargetContext()
95 | .getString(R.string.statistics_active_tasks);
96 | onView(withText(containsString(expectedActiveTaskText))).check(matches(isDisplayed()));
97 | String expectedCompletedTaskText = InstrumentationRegistry.getTargetContext()
98 | .getString(R.string.statistics_completed_tasks);
99 | onView(withText(containsString(expectedCompletedTaskText))).check(matches(isDisplayed()));
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/todoapp/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 |
22 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/BasePresenter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.android.architecture.blueprints.todoapp;
18 |
19 | public interface BasePresenter {
20 |
21 | void start();
22 | void onDestroyView();
23 | }
24 |
--------------------------------------------------------------------------------
/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/BaseView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.android.architecture.blueprints.todoapp;
18 |
19 | public interface BaseView {
20 |
21 | void setPresenter(T presenter);
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/UseCaseRx.java:
--------------------------------------------------------------------------------
1 | package com.example.android.architecture.blueprints.todoapp;
2 |
3 | import rx.Observable;
4 | import rx.Scheduler;
5 | import rx.Subscriber;
6 | import rx.Subscription;
7 | import rx.subscriptions.Subscriptions;
8 |
9 | public abstract class UseCaseRx {
10 |
11 | private final Scheduler threadExecutor;
12 | private final Scheduler postExecutionThread;
13 |
14 | private Subscription subscription = Subscriptions.empty();
15 |
16 | protected UseCaseRx(Scheduler threadExecutor,
17 | Scheduler postExecutionThread) {
18 | this.threadExecutor = threadExecutor;
19 | this.postExecutionThread = postExecutionThread;
20 | }
21 |
22 | /**
23 | * Builds an {@link rx.Observable} which will be used when executing the current {@link UseCaseRx}.
24 | */
25 | protected abstract Observable buildUseCaseObservable(R requestValues);
26 |
27 | /**
28 | * Executes the current use case.
29 | *
30 | * @param useCaseSubscriber The guy who will be listen to the observable build
31 | * with {@link #buildUseCaseObservable(R requestValues)}.
32 | */
33 | @SuppressWarnings("unchecked")
34 | public void execute(R requestValues, Subscriber useCaseSubscriber) {
35 | this.subscription = this.buildUseCaseObservable(requestValues)
36 | .subscribeOn(threadExecutor)
37 | .observeOn(postExecutionThread)
38 | .subscribe(useCaseSubscriber);
39 |
40 | }
41 |
42 | /**
43 | * Unsubscribes from current {@link rx.Subscription}.
44 | */
45 | public void unsubscribe() {
46 | if (!subscription.isUnsubscribed()) {
47 | subscription.unsubscribe();
48 | }
49 | }
50 |
51 | /**
52 | * Data passed to a request.
53 | */
54 | public static abstract class RequestValues {
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.android.architecture.blueprints.todoapp.addedittask;
18 |
19 | import android.os.Bundle;
20 | import android.support.annotation.VisibleForTesting;
21 | import android.support.test.espresso.IdlingResource;
22 | import android.support.v7.app.ActionBar;
23 | import android.support.v7.app.AppCompatActivity;
24 | import android.support.v7.widget.Toolbar;
25 |
26 | import com.example.android.architecture.blueprints.todoapp.Injection;
27 | import com.example.android.architecture.blueprints.todoapp.R;
28 | import com.example.android.architecture.blueprints.todoapp.util.ActivityUtils;
29 | import com.example.android.architecture.blueprints.todoapp.util.EspressoIdlingResource;
30 |
31 | /**
32 | * Displays an add or edit task screen.
33 | */
34 | public class AddEditTaskActivity extends AppCompatActivity {
35 |
36 | public static final int REQUEST_ADD_TASK = 1;
37 |
38 | @Override
39 | protected void onCreate(Bundle savedInstanceState) {
40 | super.onCreate(savedInstanceState);
41 | setContentView(R.layout.addtask_act);
42 |
43 | // Set up the toolbar.
44 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
45 | setSupportActionBar(toolbar);
46 | ActionBar actionBar = getSupportActionBar();
47 | actionBar.setDisplayHomeAsUpEnabled(true);
48 | actionBar.setDisplayShowHomeEnabled(true);
49 |
50 | AddEditTaskFragment addEditTaskFragment =
51 | (AddEditTaskFragment) getSupportFragmentManager().findFragmentById(
52 | R.id.contentFrame);
53 |
54 | String taskId = getIntent().getStringExtra(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID);
55 |
56 | if (addEditTaskFragment == null) {
57 | addEditTaskFragment = AddEditTaskFragment.newInstance();
58 |
59 | if (getIntent().hasExtra(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID)) {
60 | actionBar.setTitle(R.string.edit_task);
61 | Bundle bundle = new Bundle();
62 | bundle.putString(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID, taskId);
63 | addEditTaskFragment.setArguments(bundle);
64 | } else {
65 | actionBar.setTitle(R.string.add_task);
66 | }
67 |
68 | ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
69 | addEditTaskFragment, R.id.contentFrame);
70 | }
71 |
72 | // Create the presenter
73 | new AddEditTaskPresenter(
74 | taskId,
75 | addEditTaskFragment,
76 | Injection.provideGetTask(getApplicationContext()),
77 | Injection.provideSaveTask(getApplicationContext())
78 | );
79 | }
80 |
81 | @Override
82 | public boolean onSupportNavigateUp() {
83 | onBackPressed();
84 | return true;
85 | }
86 |
87 | @VisibleForTesting
88 | public IdlingResource getCountingIdlingResource() {
89 | return EspressoIdlingResource.getIdlingResource();
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskContract.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.android.architecture.blueprints.todoapp.addedittask;
18 |
19 | import com.example.android.architecture.blueprints.todoapp.BasePresenter;
20 | import com.example.android.architecture.blueprints.todoapp.BaseView;
21 |
22 | /**
23 | * This specifies the contract between the view and the presenter.
24 | */
25 | public interface AddEditTaskContract {
26 |
27 | interface View extends BaseView {
28 |
29 | void showEmptyTaskError();
30 |
31 | void showTasksList();
32 |
33 | void setTitle(String title);
34 |
35 | void setDescription(String description);
36 |
37 | boolean isActive();
38 | }
39 |
40 | interface Presenter extends BasePresenter {
41 |
42 | void saveTask(String title, String description);
43 |
44 | void populateTask();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.android.architecture.blueprints.todoapp.addedittask;
18 |
19 | import android.app.Activity;
20 | import android.os.Bundle;
21 | import android.support.annotation.NonNull;
22 | import android.support.annotation.Nullable;
23 | import android.support.design.widget.FloatingActionButton;
24 | import android.support.design.widget.Snackbar;
25 | import android.support.v4.app.Fragment;
26 | import android.view.LayoutInflater;
27 | import android.view.View;
28 | import android.view.ViewGroup;
29 | import android.widget.TextView;
30 |
31 | import com.example.android.architecture.blueprints.todoapp.R;
32 |
33 | import static com.google.common.base.Preconditions.checkNotNull;
34 |
35 | /**
36 | * Main UI for the add task screen. Users can enter a task title and description.
37 | */
38 | public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View {
39 |
40 | public static final String ARGUMENT_EDIT_TASK_ID = "EDIT_TASK_ID";
41 |
42 | private AddEditTaskContract.Presenter mPresenter;
43 |
44 | private TextView mTitle;
45 |
46 | private TextView mDescription;
47 |
48 | public static AddEditTaskFragment newInstance() {
49 | return new AddEditTaskFragment();
50 | }
51 |
52 | public AddEditTaskFragment() {
53 | // Required empty public constructor
54 | }
55 |
56 | @Override
57 | public void onResume() {
58 | super.onResume();
59 | mPresenter.start();
60 | }
61 |
62 | @Override
63 | public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) {
64 | mPresenter = checkNotNull(presenter);
65 | }
66 |
67 | @Override
68 | public void onActivityCreated(Bundle savedInstanceState) {
69 | super.onActivityCreated(savedInstanceState);
70 |
71 | FloatingActionButton fab =
72 | (FloatingActionButton) getActivity().findViewById(R.id.fab_edit_task_done);
73 | fab.setImageResource(R.drawable.ic_done);
74 | fab.setOnClickListener(new View.OnClickListener() {
75 | @Override
76 | public void onClick(View v) {
77 | mPresenter.saveTask(mTitle.getText().toString(), mDescription.getText().toString());
78 | }
79 | });
80 | }
81 |
82 | @Nullable
83 | @Override
84 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
85 | Bundle savedInstanceState) {
86 | View root = inflater.inflate(R.layout.addtask_frag, container, false);
87 | mTitle = (TextView) root.findViewById(R.id.add_task_title);
88 | mDescription = (TextView) root.findViewById(R.id.add_task_description);
89 |
90 | setHasOptionsMenu(true);
91 | setRetainInstance(true);
92 | return root;
93 | }
94 |
95 | @Override
96 | public void showEmptyTaskError() {
97 | Snackbar.make(mTitle, getString(R.string.empty_task_message), Snackbar.LENGTH_LONG).show();
98 | }
99 |
100 | @Override
101 | public void showTasksList() {
102 | getActivity().setResult(Activity.RESULT_OK);
103 | getActivity().finish();
104 | }
105 |
106 | @Override
107 | public void setTitle(String title) {
108 | mTitle.setText(title);
109 | }
110 |
111 | @Override
112 | public void setDescription(String description) {
113 | mDescription.setText(description);
114 | }
115 |
116 | @Override
117 | public boolean isActive() {
118 | return isAdded();
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/domain/usecase/DeleteTask.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.android.architecture.blueprints.todoapp.addedittask.domain.usecase;
18 |
19 | import android.support.annotation.NonNull;
20 |
21 | import com.example.android.architecture.blueprints.todoapp.UseCaseRx;
22 | import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository;
23 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.model.Task;
24 |
25 | import rx.Observable;
26 | import rx.Scheduler;
27 | import rx.Subscriber;
28 |
29 | import static com.google.common.base.Preconditions.checkNotNull;
30 |
31 | /**
32 | * Deletes a {@link Task} from the {@link TasksRepository}.
33 | */
34 | public class DeleteTask extends UseCaseRx {
35 |
36 | private TasksRepository tasksRepository;
37 |
38 | public DeleteTask(Scheduler threadExecutor, Scheduler postExecutionThread, @NonNull TasksRepository tasksRepository) {
39 | super(threadExecutor, postExecutionThread);
40 | this.tasksRepository = tasksRepository;
41 | }
42 |
43 | @Override
44 | protected Observable buildUseCaseObservable(final RequestValues requestValues) {
45 | return Observable.create(new Observable.OnSubscribe