├── .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 | Diagram 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() { 46 | @Override 47 | public void call(Subscriber subscriber) { 48 | tasksRepository.deleteTask(requestValues.getTaskId()); 49 | subscriber.onCompleted(); 50 | } 51 | }); 52 | } 53 | 54 | public static final class RequestValues extends UseCaseRx.RequestValues { 55 | private final String mTaskId; 56 | 57 | public RequestValues(@NonNull String taskId) { 58 | mTaskId = checkNotNull(taskId, "taskId cannot be null!"); 59 | } 60 | 61 | public String getTaskId() { 62 | return mTaskId; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/domain/usecase/GetTask.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 | 28 | import static com.google.common.base.Preconditions.checkNotNull; 29 | 30 | /** 31 | * Retrieves a {@link Task} from the {@link TasksRepository}. 32 | */ 33 | public class GetTask extends UseCaseRx { 34 | 35 | private final TasksRepository tasksRepository; 36 | 37 | 38 | public GetTask(Scheduler threadExecutor, Scheduler postExecutionThread, @NonNull TasksRepository tasksRepository) { 39 | super(threadExecutor, postExecutionThread); 40 | this.tasksRepository=checkNotNull(tasksRepository, "tasksRepository cannot be null!"); 41 | } 42 | 43 | @Override 44 | protected Observable buildUseCaseObservable(GetTask.RequestValues requestValues) { 45 | return Observable.just(tasksRepository.getTask(requestValues.getTaskId())); 46 | } 47 | 48 | public static final class RequestValues extends UseCaseRx.RequestValues { 49 | private final String mTaskId; 50 | 51 | public RequestValues(@NonNull String taskId) { 52 | mTaskId = checkNotNull(taskId, "taskId cannot be null!"); 53 | } 54 | 55 | public String getTaskId() { 56 | return mTaskId; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/domain/usecase/SaveTask.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 | * Updates or creates a new {@link Task} in the {@link TasksRepository}. 33 | */ 34 | public class SaveTask extends UseCaseRx { 35 | 36 | 37 | private TasksRepository tasksRepository; 38 | 39 | public SaveTask(Scheduler threadExecutor, Scheduler postExecutionThread, @NonNull TasksRepository tasksRepository) { 40 | super(threadExecutor, postExecutionThread); 41 | 42 | this.tasksRepository = tasksRepository; 43 | } 44 | 45 | @Override 46 | protected Observable buildUseCaseObservable(final RequestValues requestValues) { 47 | return Observable.create(new Observable.OnSubscribe() { 48 | @Override 49 | public void call(Subscriber subscriber) { 50 | Task task = requestValues.getTask(); 51 | tasksRepository.saveTask(task); 52 | subscriber.onCompleted(); 53 | } 54 | }); 55 | } 56 | 57 | public static final class RequestValues extends UseCaseRx.RequestValues { 58 | 59 | private final Task mTask; 60 | 61 | public RequestValues(@NonNull Task task) { 62 | mTask = checkNotNull(task, "task cannot be null!"); 63 | } 64 | 65 | public Task getTask() { 66 | return mTask; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/TasksDataSource.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.data.source; 18 | 19 | import android.support.annotation.NonNull; 20 | 21 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.model.Task; 22 | 23 | import java.util.ArrayList; 24 | 25 | /** 26 | * Main entry point for accessing tasks data. 27 | *

28 | * For simplicity, only getTasks() and getTask() have callbacks. Consider adding callbacks to other 29 | * methods to inform the user of network/database errors or successful operations. 30 | * For example, when a new task is created, it's synchronously stored in cache but usually every 31 | * operation on database or network should be executed in a different thread. 32 | */ 33 | public interface TasksDataSource { 34 | 35 | ArrayList getTasks(); 36 | 37 | Task getTask(@NonNull String taskId); 38 | 39 | void saveTask(@NonNull Task task); 40 | 41 | void completeTask(@NonNull Task task); 42 | 43 | void completeTask(@NonNull String taskId); 44 | 45 | void activateTask(@NonNull Task task); 46 | 47 | void activateTask(@NonNull String taskId); 48 | 49 | void clearCompletedTasks(); 50 | 51 | void refreshTasks(); 52 | 53 | void deleteAllTasks(); 54 | 55 | void deleteTask(@NonNull String taskId); 56 | } 57 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksDbHelper.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.data.source.local; 18 | 19 | import android.content.Context; 20 | import android.database.sqlite.SQLiteDatabase; 21 | import android.database.sqlite.SQLiteOpenHelper; 22 | 23 | public class TasksDbHelper extends SQLiteOpenHelper { 24 | public static final int DATABASE_VERSION = 1; 25 | 26 | public static final String DATABASE_NAME = "Tasks.db"; 27 | 28 | private static final String TEXT_TYPE = " TEXT"; 29 | 30 | private static final String BOOLEAN_TYPE = " INTEGER"; 31 | 32 | private static final String COMMA_SEP = ","; 33 | 34 | private static final String SQL_CREATE_ENTRIES = 35 | "CREATE TABLE " + TasksPersistenceContract.TaskEntry.TABLE_NAME + " (" + 36 | TasksPersistenceContract.TaskEntry._ID + TEXT_TYPE + " PRIMARY KEY," + 37 | TasksPersistenceContract.TaskEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP + 38 | TasksPersistenceContract.TaskEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP + 39 | TasksPersistenceContract.TaskEntry.COLUMN_NAME_DESCRIPTION + TEXT_TYPE + COMMA_SEP + 40 | TasksPersistenceContract.TaskEntry.COLUMN_NAME_COMPLETED + BOOLEAN_TYPE + 41 | " )"; 42 | 43 | public TasksDbHelper(Context context) { 44 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 45 | } 46 | 47 | public void onCreate(SQLiteDatabase db) { 48 | db.execSQL(SQL_CREATE_ENTRIES); 49 | } 50 | 51 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 52 | // Not required as at version 1 53 | } 54 | 55 | public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 56 | // Not required as at version 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksPersistenceContract.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.data.source.local; 18 | 19 | import android.provider.BaseColumns; 20 | 21 | /** 22 | * The contract used for the db to save the tasks locally. 23 | */ 24 | public final class TasksPersistenceContract { 25 | 26 | // To prevent someone from accidentally instantiating the contract class, 27 | // give it an empty constructor. 28 | private TasksPersistenceContract() {} 29 | 30 | /* Inner class that defines the table contents */ 31 | public static abstract class TaskEntry implements BaseColumns { 32 | public static final String TABLE_NAME = "task"; 33 | public static final String COLUMN_NAME_ENTRY_ID = "entryid"; 34 | public static final String COLUMN_NAME_TITLE = "title"; 35 | public static final String COLUMN_NAME_DESCRIPTION = "description"; 36 | public static final String COLUMN_NAME_COMPLETED = "completed"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/remote/TasksRemoteDataSource.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.data.source.remote; 18 | 19 | import android.support.annotation.NonNull; 20 | 21 | import com.example.android.architecture.blueprints.todoapp.data.source.TasksDataSource; 22 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.model.Task; 23 | import com.google.common.collect.Lists; 24 | 25 | import java.util.ArrayList; 26 | import java.util.Iterator; 27 | import java.util.LinkedHashMap; 28 | import java.util.Map; 29 | 30 | /** 31 | * Implementation of the data source that adds a latency simulating network. 32 | */ 33 | public class TasksRemoteDataSource implements TasksDataSource { 34 | 35 | private static TasksRemoteDataSource INSTANCE; 36 | 37 | private static final int SERVICE_LATENCY_IN_MILLIS = 5000; 38 | 39 | private static final Map TASKS_SERVICE_DATA; 40 | 41 | static { 42 | TASKS_SERVICE_DATA = new LinkedHashMap<>(2); 43 | addTask("Build tower in Pisa", "Ground looks good, no foundation work required."); 44 | addTask("Finish bridge in Tacoma", "Found awesome girders at half the cost!"); 45 | } 46 | 47 | public static TasksRemoteDataSource getInstance() { 48 | if (INSTANCE == null) { 49 | INSTANCE = new TasksRemoteDataSource(); 50 | } 51 | return INSTANCE; 52 | } 53 | 54 | // Prevent direct instantiation. 55 | private TasksRemoteDataSource() {} 56 | 57 | private static void addTask(String title, String description) { 58 | Task newTask = new Task(title, description); 59 | TASKS_SERVICE_DATA.put(newTask.getId(), newTask); 60 | } 61 | 62 | /** 63 | * In a real remote data 64 | * source implementation, this would be fired if the server can't be contacted or the server 65 | * returns an error. 66 | */ 67 | @Override 68 | public ArrayList getTasks() { 69 | 70 | try { 71 | Thread.sleep(SERVICE_LATENCY_IN_MILLIS); 72 | } catch (InterruptedException e) { 73 | e.printStackTrace(); 74 | } 75 | 76 | return Lists.newArrayList(TASKS_SERVICE_DATA.values()); 77 | } 78 | 79 | 80 | @Override 81 | public Task getTask(@NonNull String taskId) { 82 | try { 83 | Thread.sleep(SERVICE_LATENCY_IN_MILLIS); 84 | } catch (InterruptedException e) { 85 | e.printStackTrace(); 86 | } 87 | return TASKS_SERVICE_DATA.get(taskId); 88 | } 89 | 90 | @Override 91 | public void saveTask(@NonNull final Task task) { 92 | TASKS_SERVICE_DATA.put(task.getId(), task); 93 | } 94 | 95 | @Override 96 | public void completeTask(@NonNull final Task task) { 97 | Task completedTask = new Task(task.getTitle(), task.getDescription(), task.getId(), true); 98 | TASKS_SERVICE_DATA.put(task.getId(), completedTask); 99 | } 100 | 101 | @Override 102 | public void completeTask(@NonNull String taskId) { 103 | // Not required for the remote data source because the {@link TasksRepository} handles 104 | // converting from a {@code taskId} to a {@link task} using its cached data. 105 | } 106 | 107 | @Override 108 | public void activateTask(@NonNull final Task task) { 109 | Task activeTask = new Task(task.getTitle(), task.getDescription(), task.getId()); 110 | TASKS_SERVICE_DATA.put(task.getId(), activeTask); 111 | } 112 | 113 | @Override 114 | public void activateTask(@NonNull String taskId) { 115 | // Not required for the remote data source because the {@link TasksRepository} handles 116 | // converting from a {@code taskId} to a {@link task} using its cached data. 117 | } 118 | 119 | @Override 120 | public void clearCompletedTasks() { 121 | Iterator> it = TASKS_SERVICE_DATA.entrySet().iterator(); 122 | while (it.hasNext()) { 123 | Map.Entry entry = it.next(); 124 | if (entry.getValue().isCompleted()) { 125 | it.remove(); 126 | } 127 | } 128 | } 129 | 130 | @Override 131 | public void refreshTasks() { 132 | // Not required because the {@link TasksRepository} handles the logic of refreshing the 133 | // tasks from all the available data sources. 134 | } 135 | 136 | @Override 137 | public void deleteAllTasks() { 138 | TASKS_SERVICE_DATA.clear(); 139 | } 140 | 141 | @Override 142 | public void deleteTask(@NonNull final String taskId) { 143 | TASKS_SERVICE_DATA.remove(taskId); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsActivity.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 android.content.Intent; 20 | import android.os.Bundle; 21 | import android.support.annotation.VisibleForTesting; 22 | import android.support.design.widget.NavigationView; 23 | import android.support.test.espresso.IdlingResource; 24 | import android.support.v4.view.GravityCompat; 25 | import android.support.v4.widget.DrawerLayout; 26 | import android.support.v7.app.ActionBar; 27 | import android.support.v7.app.AppCompatActivity; 28 | import android.support.v7.widget.Toolbar; 29 | import android.view.MenuItem; 30 | 31 | import com.example.android.architecture.blueprints.todoapp.Injection; 32 | import com.example.android.architecture.blueprints.todoapp.R; 33 | import com.example.android.architecture.blueprints.todoapp.tasks.TasksActivity; 34 | import com.example.android.architecture.blueprints.todoapp.util.ActivityUtils; 35 | import com.example.android.architecture.blueprints.todoapp.util.EspressoIdlingResource; 36 | 37 | /** 38 | * Show statistics for tasks. 39 | */ 40 | public class StatisticsActivity extends AppCompatActivity { 41 | 42 | private DrawerLayout mDrawerLayout; 43 | 44 | @Override 45 | protected void onCreate(Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | 48 | setContentView(R.layout.statistics_act); 49 | 50 | // Set up the toolbar. 51 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 52 | setSupportActionBar(toolbar); 53 | ActionBar ab = getSupportActionBar(); 54 | ab.setTitle(R.string.statistics_title); 55 | ab.setHomeAsUpIndicator(R.drawable.ic_menu); 56 | ab.setDisplayHomeAsUpEnabled(true); 57 | 58 | // Set up the navigation drawer. 59 | mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); 60 | mDrawerLayout.setStatusBarBackground(R.color.colorPrimaryDark); 61 | NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); 62 | if (navigationView != null) { 63 | setupDrawerContent(navigationView); 64 | } 65 | 66 | StatisticsFragment statisticsFragment = (StatisticsFragment) getSupportFragmentManager() 67 | .findFragmentById(R.id.contentFrame); 68 | if (statisticsFragment == null) { 69 | statisticsFragment = StatisticsFragment.newInstance(); 70 | ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), 71 | statisticsFragment, R.id.contentFrame); 72 | } 73 | 74 | new StatisticsPresenter( 75 | statisticsFragment, 76 | Injection.provideGetStatistics(getApplicationContext())); 77 | } 78 | 79 | @Override 80 | public boolean onOptionsItemSelected(MenuItem item) { 81 | switch (item.getItemId()) { 82 | case android.R.id.home: 83 | // Open the navigation drawer when the home icon is selected from the toolbar. 84 | mDrawerLayout.openDrawer(GravityCompat.START); 85 | return true; 86 | } 87 | return super.onOptionsItemSelected(item); 88 | } 89 | 90 | private void setupDrawerContent(NavigationView navigationView) { 91 | navigationView.setNavigationItemSelectedListener( 92 | new NavigationView.OnNavigationItemSelectedListener() { 93 | @Override 94 | public boolean onNavigationItemSelected(MenuItem menuItem) { 95 | switch (menuItem.getItemId()) { 96 | case R.id.list_navigation_menu_item: 97 | Intent intent = 98 | new Intent(StatisticsActivity.this, TasksActivity.class); 99 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK 100 | | Intent.FLAG_ACTIVITY_CLEAR_TASK); 101 | startActivity(intent); 102 | break; 103 | case R.id.statistics_navigation_menu_item: 104 | // Do nothing, we're already on that screen 105 | break; 106 | default: 107 | break; 108 | } 109 | // Close the navigation drawer when an item is selected. 110 | menuItem.setChecked(true); 111 | mDrawerLayout.closeDrawers(); 112 | return true; 113 | } 114 | }); 115 | } 116 | 117 | @VisibleForTesting 118 | public IdlingResource getCountingIdlingResource() { 119 | return EspressoIdlingResource.getIdlingResource(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsContract.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 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 StatisticsContract { 26 | 27 | interface View extends BaseView { 28 | 29 | void setProgressIndicator(boolean active); 30 | 31 | void showStatistics(int numberOfIncompleteTasks, int numberOfCompletedTasks); 32 | 33 | void showLoadingStatisticsError(); 34 | 35 | boolean isActive(); 36 | } 37 | 38 | interface Presenter extends BasePresenter { 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsFragment.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 android.os.Bundle; 20 | import android.support.annotation.NonNull; 21 | import android.support.annotation.Nullable; 22 | import android.support.v4.app.Fragment; 23 | import android.view.LayoutInflater; 24 | import android.view.View; 25 | import android.view.ViewGroup; 26 | import android.widget.TextView; 27 | 28 | import com.example.android.architecture.blueprints.todoapp.R; 29 | 30 | import static com.google.common.base.Preconditions.checkNotNull; 31 | 32 | /** 33 | * Main UI for the statistics screen. 34 | */ 35 | public class StatisticsFragment extends Fragment implements StatisticsContract.View { 36 | 37 | private TextView mStatisticsTV; 38 | 39 | private StatisticsContract.Presenter mPresenter; 40 | 41 | public static StatisticsFragment newInstance() { 42 | return new StatisticsFragment(); 43 | } 44 | 45 | @Override 46 | public void setPresenter(@NonNull StatisticsContract.Presenter presenter) { 47 | mPresenter = checkNotNull(presenter); 48 | } 49 | 50 | @Nullable 51 | @Override 52 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 53 | Bundle savedInstanceState) { 54 | View root = inflater.inflate(R.layout.statistics_frag, container, false); 55 | mStatisticsTV = (TextView) root.findViewById(R.id.statistics); 56 | return root; 57 | } 58 | 59 | @Override 60 | public void onResume() { 61 | super.onResume(); 62 | mPresenter.start(); 63 | } 64 | 65 | @Override 66 | public void setProgressIndicator(boolean active) { 67 | if (active) { 68 | mStatisticsTV.setText(getString(R.string.loading)); 69 | } else { 70 | mStatisticsTV.setText(""); 71 | } 72 | } 73 | 74 | @Override 75 | public void showStatistics(int numberOfIncompleteTasks, int numberOfCompletedTasks) { 76 | if (numberOfCompletedTasks == 0 && numberOfIncompleteTasks == 0) { 77 | mStatisticsTV.setText(getResources().getString(R.string.statistics_no_tasks)); 78 | } else { 79 | String displayString = getResources().getString(R.string.statistics_active_tasks) + " " 80 | + numberOfIncompleteTasks + "\n" + getResources().getString( 81 | R.string.statistics_completed_tasks) + " " + numberOfCompletedTasks; 82 | mStatisticsTV.setText(displayString); 83 | } 84 | } 85 | 86 | @Override 87 | public void showLoadingStatisticsError() { 88 | mStatisticsTV.setText(getResources().getString(R.string.statistics_error)); 89 | } 90 | 91 | @Override 92 | public boolean isActive() { 93 | return isAdded(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsPresenter.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 android.support.annotation.NonNull; 20 | 21 | import com.example.android.architecture.blueprints.todoapp.statistics.domain.model.Statistics; 22 | import com.example.android.architecture.blueprints.todoapp.statistics.domain.usecase.GetStatistics; 23 | 24 | import rx.Subscriber; 25 | 26 | import static com.google.common.base.Preconditions.checkNotNull; 27 | 28 | /** 29 | * Listens to user actions from the UI ({@link StatisticsFragment}), retrieves the data and updates 30 | * the UI as required. 31 | */ 32 | public class StatisticsPresenter implements StatisticsContract.Presenter { 33 | 34 | private final StatisticsContract.View mStatisticsView; 35 | private final GetStatistics getStatistics; 36 | 37 | public StatisticsPresenter( 38 | @NonNull StatisticsContract.View statisticsView, 39 | @NonNull GetStatistics getStatistics) { 40 | mStatisticsView = checkNotNull(statisticsView, "StatisticsView cannot be null!"); 41 | this.getStatistics = checkNotNull(getStatistics,"getStatistics cannot be null!"); 42 | 43 | mStatisticsView.setPresenter(this); 44 | } 45 | 46 | @Override 47 | public void start() { 48 | loadStatistics(); 49 | } 50 | 51 | @Override 52 | public void onDestroyView() { 53 | getStatistics.unsubscribe(); 54 | } 55 | 56 | private void loadStatistics() { 57 | mStatisticsView.setProgressIndicator(true); 58 | 59 | getStatistics.execute(new GetStatistics.RequestValues(), new Subscriber() { 60 | @Override 61 | public void onCompleted() { 62 | 63 | } 64 | 65 | @Override 66 | public void onError(Throwable e) { 67 | // The view may not be able to handle UI updates anymore 68 | if (!mStatisticsView.isActive()) { 69 | return; 70 | } 71 | mStatisticsView.showLoadingStatisticsError(); 72 | } 73 | 74 | @Override 75 | public void onNext(Statistics statistics) { 76 | // The view may not be able to handle UI updates anymore 77 | if (!mStatisticsView.isActive()) { 78 | return; 79 | } 80 | mStatisticsView.setProgressIndicator(false); 81 | mStatisticsView.showStatistics(statistics.getActiveTasks(), statistics.getCompletedTasks()); 82 | } 83 | }); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/domain/model/Statistics.java: -------------------------------------------------------------------------------- 1 | package com.example.android.architecture.blueprints.todoapp.statistics.domain.model; 2 | 3 | public class Statistics { 4 | 5 | private final int completedTasks; 6 | private final int activeTasks; 7 | 8 | 9 | public Statistics(int completedTasks, int activeTasks) { 10 | this.completedTasks = completedTasks; 11 | this.activeTasks = activeTasks; 12 | } 13 | 14 | public int getCompletedTasks() { 15 | return completedTasks; 16 | } 17 | 18 | public int getActiveTasks() { 19 | return activeTasks; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/domain/usecase/GetStatistics.java: -------------------------------------------------------------------------------- 1 | package com.example.android.architecture.blueprints.todoapp.statistics.domain.usecase; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import com.example.android.architecture.blueprints.todoapp.UseCaseRx; 6 | import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository; 7 | import com.example.android.architecture.blueprints.todoapp.statistics.domain.model.Statistics; 8 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.model.Task; 9 | 10 | import java.util.ArrayList; 11 | 12 | import rx.Observable; 13 | import rx.Scheduler; 14 | import rx.functions.Func1; 15 | 16 | /** 17 | * Calculate statistics of active and completed Tasks {@link Task} in the {@link TasksRepository}. 18 | */ 19 | public class GetStatistics extends UseCaseRx { 20 | 21 | private TasksRepository tasksRepository; 22 | 23 | public GetStatistics(Scheduler threadExecutor, Scheduler postExecutionThread, @NonNull TasksRepository tasksRepository) { 24 | super(threadExecutor, postExecutionThread); 25 | this.tasksRepository = tasksRepository; 26 | } 27 | 28 | @Override 29 | protected Observable buildUseCaseObservable(RequestValues requestValues) { 30 | 31 | return Observable.just(tasksRepository.getTasks()) 32 | .map(new Func1, Statistics>() { 33 | 34 | @Override 35 | public Statistics call(ArrayList tasks) { 36 | 37 | int activeTasks = 0; 38 | int completedTasks = 0; 39 | 40 | // We calculate number of active and completed tasks 41 | for (Task task : tasks) { 42 | if (task.isCompleted()) { 43 | completedTasks += 1; 44 | } else { 45 | activeTasks += 1; 46 | } 47 | } 48 | return new Statistics(completedTasks, activeTasks); 49 | } 50 | }); 51 | } 52 | 53 | public static class RequestValues extends UseCaseRx.RequestValues { 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailActivity.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.taskdetail; 18 | 19 | import android.os.Bundle; 20 | import android.support.v7.app.ActionBar; 21 | import android.support.v7.app.AppCompatActivity; 22 | import android.support.v7.widget.Toolbar; 23 | 24 | import com.example.android.architecture.blueprints.todoapp.Injection; 25 | import com.example.android.architecture.blueprints.todoapp.R; 26 | import com.example.android.architecture.blueprints.todoapp.util.ActivityUtils; 27 | 28 | /** 29 | * Displays task details screen. 30 | */ 31 | public class TaskDetailActivity extends AppCompatActivity { 32 | 33 | public static final String EXTRA_TASK_ID = "TASK_ID"; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | 39 | setContentView(R.layout.taskdetail_act); 40 | 41 | // Set up the toolbar. 42 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 43 | setSupportActionBar(toolbar); 44 | ActionBar ab = getSupportActionBar(); 45 | ab.setDisplayHomeAsUpEnabled(true); 46 | ab.setDisplayShowHomeEnabled(true); 47 | 48 | // Get the requested task id 49 | String taskId = getIntent().getStringExtra(EXTRA_TASK_ID); 50 | 51 | TaskDetailFragment taskDetailFragment = (TaskDetailFragment) getSupportFragmentManager() 52 | .findFragmentById(R.id.contentFrame); 53 | 54 | if (taskDetailFragment == null) { 55 | taskDetailFragment = TaskDetailFragment.newInstance(taskId); 56 | 57 | ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), 58 | taskDetailFragment, R.id.contentFrame); 59 | } 60 | 61 | // Create the presenter 62 | new TaskDetailPresenter( 63 | taskId, 64 | taskDetailFragment, 65 | Injection.provideGetTask(getApplicationContext()), 66 | Injection.provideCompleteTasks(getApplicationContext()), 67 | Injection.provideActivateTask(getApplicationContext()), 68 | Injection.provideDeleteTask(getApplicationContext())); 69 | } 70 | 71 | @Override 72 | public boolean onSupportNavigateUp() { 73 | onBackPressed(); 74 | return true; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailContract.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.taskdetail; 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 TaskDetailContract { 26 | 27 | interface View extends BaseView { 28 | 29 | void setLoadingIndicator(boolean active); 30 | 31 | void showMissingTask(); 32 | 33 | void hideTitle(); 34 | 35 | void showTitle(String title); 36 | 37 | void hideDescription(); 38 | 39 | void showDescription(String description); 40 | 41 | void showCompletionStatus(boolean complete); 42 | 43 | void showEditTask(String taskId); 44 | 45 | void showTaskDeleted(); 46 | 47 | void showTaskMarkedComplete(); 48 | 49 | void showTaskMarkedActive(); 50 | 51 | boolean isActive(); 52 | } 53 | 54 | interface Presenter extends BasePresenter { 55 | 56 | void editTask(); 57 | 58 | void deleteTask(); 59 | 60 | void completeTask(); 61 | 62 | void activateTask(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/ScrollChildSwipeRefreshLayout.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 android.content.Context; 20 | import android.support.v4.view.ViewCompat; 21 | import android.support.v4.widget.SwipeRefreshLayout; 22 | import android.util.AttributeSet; 23 | import android.view.View; 24 | 25 | /** 26 | * Extends {@link SwipeRefreshLayout} to support non-direct descendant scrolling views. 27 | *

28 | * {@link SwipeRefreshLayout} works as expected when a scroll view is a direct child: it triggers 29 | * the refresh only when the view is on top. This class adds a way (@link #setScrollUpChild} to 30 | * define which view controls this behavior. 31 | */ 32 | public class ScrollChildSwipeRefreshLayout extends SwipeRefreshLayout { 33 | 34 | private View mScrollUpChild; 35 | 36 | public ScrollChildSwipeRefreshLayout(Context context) { 37 | super(context); 38 | } 39 | 40 | public ScrollChildSwipeRefreshLayout(Context context, AttributeSet attrs) { 41 | super(context, attrs); 42 | } 43 | 44 | @Override 45 | public boolean canChildScrollUp() { 46 | if (mScrollUpChild != null) { 47 | return ViewCompat.canScrollVertically(mScrollUpChild, -1); 48 | } 49 | return super.canChildScrollUp(); 50 | } 51 | 52 | public void setScrollUpChild(View view) { 53 | mScrollUpChild = view; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksContract.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 android.support.annotation.NonNull; 20 | 21 | import com.example.android.architecture.blueprints.todoapp.BaseView; 22 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.model.Task; 23 | import com.example.android.architecture.blueprints.todoapp.BasePresenter; 24 | 25 | import java.util.List; 26 | 27 | /** 28 | * This specifies the contract between the view and the presenter. 29 | */ 30 | public interface TasksContract { 31 | 32 | interface View extends BaseView { 33 | 34 | void setLoadingIndicator(boolean active); 35 | 36 | void showTasks(List tasks); 37 | 38 | void showAddTask(); 39 | 40 | void showTaskDetailsUi(String taskId); 41 | 42 | void showTaskMarkedComplete(); 43 | 44 | void showTaskMarkedActive(); 45 | 46 | void showCompletedTasksCleared(); 47 | 48 | void showLoadingTasksError(); 49 | 50 | void showNoTasks(); 51 | 52 | void showActiveFilterLabel(); 53 | 54 | void showCompletedFilterLabel(); 55 | 56 | void showAllFilterLabel(); 57 | 58 | void showNoActiveTasks(); 59 | 60 | void showNoCompletedTasks(); 61 | 62 | void showSuccessfullySavedMessage(); 63 | 64 | boolean isActive(); 65 | 66 | void showFilteringPopUpMenu(); 67 | } 68 | 69 | interface Presenter extends BasePresenter { 70 | 71 | void result(int requestCode, int resultCode); 72 | 73 | void loadTasks(boolean forceUpdate); 74 | 75 | void addNewTask(); 76 | 77 | void openTaskDetails(@NonNull Task requestedTask); 78 | 79 | void completeTask(@NonNull Task completedTask); 80 | 81 | void activateTask(@NonNull Task activeTask); 82 | 83 | void clearCompletedTasks(); 84 | 85 | void setFiltering(TasksFilterType requestType); 86 | 87 | TasksFilterType getFiltering(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksFilterType.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 | /** 20 | * Used with the filter spinner in the tasks list. 21 | */ 22 | public enum TasksFilterType { 23 | /** 24 | * Do not filter tasks. 25 | */ 26 | ALL_TASKS, 27 | 28 | /** 29 | * Filters only the active (not completed yet) tasks. 30 | */ 31 | ACTIVE_TASKS, 32 | 33 | /** 34 | * Filters only the completed tasks. 35 | */ 36 | COMPLETED_TASKS 37 | } 38 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/filter/ActiveTaskFilter.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.domain.filter; 18 | 19 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.model.Task; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | /** 25 | * Returns the active tasks from a list of {@link Task}s. 26 | */ 27 | class ActiveTaskFilter implements TaskFilter { 28 | @Override 29 | public List filter(List tasks) { 30 | List filteredTasks = new ArrayList<>(); 31 | for (Task task : tasks) { 32 | if (task.isActive()) { 33 | filteredTasks.add(task); 34 | } 35 | } 36 | return filteredTasks; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/filter/CompleteTaskFilter.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.domain.filter; 18 | 19 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.model.Task; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | /** 25 | * Returns the completed tasks from a list of {@link Task}s. 26 | */ 27 | class CompleteTaskFilter implements TaskFilter { 28 | @Override 29 | public List filter(List tasks) { 30 | List filteredTasks = new ArrayList<>(); 31 | 32 | for (Task task : tasks) { 33 | if (task.isCompleted()) { 34 | filteredTasks.add(task); 35 | } 36 | } 37 | return filteredTasks; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/filter/FilterAllTaskFilter.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.domain.filter; 18 | 19 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.model.Task; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | /** 25 | * Returns all the tasks from a list of {@link Task}s. 26 | */ 27 | class FilterAllTaskFilter implements TaskFilter { 28 | @Override 29 | public List filter(List tasks) { 30 | return new ArrayList<>(tasks); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/filter/FilterFactory.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.domain.filter; 18 | 19 | import com.example.android.architecture.blueprints.todoapp.tasks.TasksFilterType; 20 | 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | /** 25 | * Factory of {@link TaskFilter}s. 26 | */ 27 | public class FilterFactory { 28 | 29 | private static final Map mFilters = new HashMap<>(); 30 | 31 | public FilterFactory() { 32 | mFilters.put(TasksFilterType.ALL_TASKS, new FilterAllTaskFilter()); 33 | mFilters.put(TasksFilterType.ACTIVE_TASKS, new ActiveTaskFilter()); 34 | mFilters.put(TasksFilterType.COMPLETED_TASKS, new CompleteTaskFilter()); 35 | } 36 | 37 | public TaskFilter create(TasksFilterType filterType) { 38 | return mFilters.get(filterType); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/filter/TaskFilter.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.domain.filter; 18 | 19 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.model.Task; 20 | 21 | import java.util.List; 22 | 23 | public interface TaskFilter { 24 | List filter(List tasks); 25 | } 26 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/model/Task.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.domain.model; 18 | 19 | import android.support.annotation.NonNull; 20 | import android.support.annotation.Nullable; 21 | 22 | import com.google.common.base.Objects; 23 | import com.google.common.base.Strings; 24 | 25 | import java.util.UUID; 26 | 27 | /** 28 | * Immutable model class for a Task. 29 | */ 30 | public final class Task { 31 | 32 | @NonNull 33 | private final String mId; 34 | 35 | @Nullable 36 | private final String mTitle; 37 | 38 | @Nullable 39 | private final String mDescription; 40 | 41 | private final boolean mCompleted; 42 | 43 | /** 44 | * Use this constructor to create a new active Task. 45 | * 46 | * @param title title of the task 47 | * @param description description of the task 48 | */ 49 | public Task(@Nullable String title, @Nullable String description) { 50 | this(title, description, UUID.randomUUID().toString(), false); 51 | } 52 | 53 | /** 54 | * Use this constructor to create an active Task if the Task already has an id (copy of another 55 | * Task). 56 | * 57 | * @param title title of the task 58 | * @param description description of the task 59 | * @param id id of the task 60 | */ 61 | public Task(@Nullable String title, @Nullable String description, @NonNull String id) { 62 | this(title, description, id, false); 63 | } 64 | 65 | /** 66 | * Use this constructor to create a new completed Task. 67 | * 68 | * @param title title of the task 69 | * @param description description of the task 70 | * @param completed true if the task is completed, false if it's active 71 | */ 72 | public Task(@Nullable String title, @Nullable String description, boolean completed) { 73 | this(title, description, UUID.randomUUID().toString(), completed); 74 | } 75 | 76 | /** 77 | * Use this constructor to specify a completed Task if the Task already has an id (copy of 78 | * another Task). 79 | * 80 | * @param title title of the task 81 | * @param description description of the task 82 | * @param id id of the task 83 | * @param completed true if the task is completed, false if it's active 84 | */ 85 | public Task(@Nullable String title, @Nullable String description, 86 | @NonNull String id, boolean completed) { 87 | mId = id; 88 | mTitle = title; 89 | mDescription = description; 90 | mCompleted = completed; 91 | } 92 | 93 | @NonNull 94 | public String getId() { 95 | return mId; 96 | } 97 | 98 | @Nullable 99 | public String getTitle() { 100 | return mTitle; 101 | } 102 | 103 | @Nullable 104 | public String getTitleForList() { 105 | if (!Strings.isNullOrEmpty(mTitle)) { 106 | return mTitle; 107 | } else { 108 | return mDescription; 109 | } 110 | } 111 | 112 | @Nullable 113 | public String getDescription() { 114 | return mDescription; 115 | } 116 | 117 | public boolean isCompleted() { 118 | return mCompleted; 119 | } 120 | 121 | public boolean isActive() { 122 | return !mCompleted; 123 | } 124 | 125 | public boolean isEmpty() { 126 | return Strings.isNullOrEmpty(mTitle) && 127 | Strings.isNullOrEmpty(mDescription); 128 | } 129 | 130 | @Override 131 | public boolean equals(Object o) { 132 | if (this == o) return true; 133 | if (o == null || getClass() != o.getClass()) return false; 134 | Task task = (Task) o; 135 | return Objects.equal(mId, task.mId) && 136 | Objects.equal(mTitle, task.mTitle) && 137 | Objects.equal(mDescription, task.mDescription); 138 | } 139 | 140 | @Override 141 | public int hashCode() { 142 | return Objects.hashCode(mId, mTitle, mDescription); 143 | } 144 | 145 | @Override 146 | public String toString() { 147 | return "Task with title " + mTitle; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/usecase/ActivateTask.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.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 | 24 | import rx.Observable; 25 | import rx.Scheduler; 26 | import rx.Subscriber; 27 | 28 | import static com.google.common.base.Preconditions.checkNotNull; 29 | 30 | /** 31 | * Marks a task as active (not completed yet). 32 | */ 33 | public class ActivateTask extends UseCaseRx { 34 | 35 | 36 | private TasksRepository tasksRepository; 37 | 38 | public ActivateTask(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 | 46 | return Observable.create(new Observable.OnSubscribe() { 47 | @Override 48 | public void call(Subscriber subscriber) { 49 | String activeTask = requestValues.getActivateTask(); 50 | tasksRepository.activateTask(activeTask); 51 | } 52 | }); 53 | 54 | } 55 | 56 | public static final class RequestValues extends UseCaseRx.RequestValues { 57 | 58 | private final String mActivateTask; 59 | 60 | public RequestValues(@NonNull String activateTask) { 61 | mActivateTask = checkNotNull(activateTask, "activateTask cannot be null!"); 62 | } 63 | 64 | public String getActivateTask() { 65 | return mActivateTask; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/usecase/ClearCompleteTasks.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.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 | 24 | import rx.Observable; 25 | import rx.Scheduler; 26 | import rx.Subscriber; 27 | 28 | /** 29 | * Deletes tasks marked as completed. 30 | */ 31 | public class ClearCompleteTasks extends UseCaseRx { 32 | 33 | private TasksRepository tasksRepository; 34 | 35 | public ClearCompleteTasks(Scheduler threadExecutor, Scheduler postExecutionThread, @NonNull TasksRepository tasksRepository) { 36 | super(threadExecutor, postExecutionThread); 37 | this.tasksRepository = tasksRepository; 38 | } 39 | 40 | @Override 41 | protected Observable buildUseCaseObservable(RequestValues requestValues) { 42 | 43 | return Observable.create(new Observable.OnSubscribe() { 44 | @Override 45 | public void call(Subscriber subscriber) { 46 | tasksRepository.clearCompletedTasks(); 47 | subscriber.onCompleted(); 48 | } 49 | }); 50 | } 51 | 52 | public static class RequestValues extends UseCaseRx.RequestValues { } 53 | 54 | } -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/usecase/CompleteTask.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.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 | 24 | import rx.Observable; 25 | import rx.Scheduler; 26 | import rx.Subscriber; 27 | 28 | import static com.google.common.base.Preconditions.checkNotNull; 29 | 30 | /** 31 | * Marks a task as completed. 32 | */ 33 | public class CompleteTask extends UseCaseRx { 34 | 35 | private final TasksRepository mTasksRepository; 36 | 37 | public CompleteTask(Scheduler threadExecutor, Scheduler postExecutionThread, @NonNull TasksRepository tasksRepository) { 38 | super(threadExecutor, postExecutionThread); 39 | mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!"); 40 | 41 | } 42 | 43 | @Override 44 | protected Observable buildUseCaseObservable(final RequestValues requestValues) { 45 | 46 | return Observable.create(new Observable.OnSubscribe() { 47 | @Override 48 | public void call(Subscriber subscriber) { 49 | mTasksRepository.completeTask(requestValues.getCompletedTask()); 50 | subscriber.onCompleted(); 51 | } 52 | }); 53 | } 54 | 55 | public static final class RequestValues extends UseCaseRx.RequestValues { 56 | 57 | private final String mCompletedTask; 58 | 59 | public RequestValues(@NonNull String completedTask) { 60 | mCompletedTask = checkNotNull(completedTask, "completedTask cannot be null!"); 61 | } 62 | 63 | public String getCompletedTask() { 64 | return mCompletedTask; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/domain/usecase/GetTasks.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.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.TasksFilterType; 24 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.filter.FilterFactory; 25 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.filter.TaskFilter; 26 | import com.example.android.architecture.blueprints.todoapp.tasks.domain.model.Task; 27 | 28 | import java.util.List; 29 | 30 | import rx.Observable; 31 | import rx.Scheduler; 32 | import rx.functions.Func1; 33 | 34 | import static com.google.common.base.Preconditions.checkNotNull; 35 | 36 | /** 37 | * Fetches the list of tasks. 38 | */ 39 | public class GetTasks extends UseCaseRx { 40 | 41 | private final TasksRepository mTasksRepository; 42 | private final FilterFactory filterFactory; 43 | 44 | 45 | public GetTasks(Scheduler threadExecutor, 46 | Scheduler postExecutionThread, @NonNull TasksRepository tasksRepository, @NonNull FilterFactory filterFactory, boolean mForceUpdate, TasksFilterType currentFiltering) { 47 | super(threadExecutor, postExecutionThread); 48 | mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!"); 49 | this.filterFactory = checkNotNull(filterFactory, "filterFactory cannot be null!"); 50 | } 51 | 52 | 53 | @Override 54 | protected Observable buildUseCaseObservable(final GetTasks.RequestValues requestValues) { 55 | if (requestValues.isForceUpdate()) { 56 | mTasksRepository.refreshTasks(); 57 | } 58 | 59 | 60 | return Observable.just(mTasksRepository.getTasks()) 61 | .map(new Func1, List>() { 62 | @Override 63 | public List call(List tasks) { 64 | TaskFilter taskFilter = filterFactory.create(requestValues.currentFiltering); 65 | List tasksFiltered = taskFilter.filter(tasks); 66 | return tasksFiltered; 67 | } 68 | }); 69 | } 70 | 71 | public static final class RequestValues extends UseCaseRx.RequestValues { 72 | private boolean forceUpdate; 73 | private TasksFilterType currentFiltering; 74 | 75 | public RequestValues(boolean mForceUpdate, TasksFilterType currentFiltering) { 76 | this.forceUpdate = mForceUpdate; 77 | this.currentFiltering = currentFiltering; 78 | } 79 | 80 | public boolean isForceUpdate() { 81 | return forceUpdate; 82 | } 83 | } 84 | 85 | 86 | } 87 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/util/ActivityUtils.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.util; 18 | 19 | import android.support.annotation.NonNull; 20 | import android.support.v4.app.Fragment; 21 | import android.support.v4.app.FragmentManager; 22 | import android.support.v4.app.FragmentTransaction; 23 | 24 | import static com.google.common.base.Preconditions.checkNotNull; 25 | 26 | /** 27 | * This provides methods to help Activities load their UI. 28 | */ 29 | public class ActivityUtils { 30 | 31 | /** 32 | * The {@code fragment} is added to the container view with id {@code frameId}. The operation is 33 | * performed by the {@code fragmentManager}. 34 | * 35 | */ 36 | public static void addFragmentToActivity (@NonNull FragmentManager fragmentManager, 37 | @NonNull Fragment fragment, int frameId) { 38 | checkNotNull(fragmentManager); 39 | checkNotNull(fragment); 40 | FragmentTransaction transaction = fragmentManager.beginTransaction(); 41 | transaction.add(frameId, fragment); 42 | transaction.commit(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/util/EspressoIdlingResource.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.util; 18 | 19 | import android.support.test.espresso.IdlingResource; 20 | 21 | /** 22 | * Contains a static reference to {@link IdlingResource}, only available in the 'mock' build type. 23 | */ 24 | public class EspressoIdlingResource { 25 | 26 | private static final String RESOURCE = "GLOBAL"; 27 | 28 | private static final SimpleCountingIdlingResource DEFAULT_INSTANCE = 29 | new SimpleCountingIdlingResource(RESOURCE); 30 | 31 | public static void increment() { 32 | DEFAULT_INSTANCE.increment(); 33 | } 34 | 35 | public static void decrement() { 36 | DEFAULT_INSTANCE.decrement(); 37 | } 38 | 39 | public static IdlingResource getIdlingResource() { 40 | return DEFAULT_INSTANCE; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/util/SimpleCountingIdlingResource.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.util; 18 | 19 | import android.support.annotation.NonNull; 20 | import android.support.test.espresso.IdlingResource; 21 | 22 | import java.util.concurrent.atomic.AtomicInteger; 23 | 24 | import static com.google.common.base.Preconditions.checkNotNull; 25 | 26 | /** 27 | * An simple counter implementation of {@link IdlingResource} that determines idleness by 28 | * maintaining an internal counter. When the counter is 0 - it is considered to be idle, when it is 29 | * non-zero it is not idle. This is very similar to the way a {@link java.util.concurrent.Semaphore} 30 | * behaves. 31 | *

32 | * This class can then be used to wrap up operations that while in progress should block tests from 33 | * accessing the UI. 34 | */ 35 | public final class SimpleCountingIdlingResource implements IdlingResource { 36 | 37 | private final String mResourceName; 38 | 39 | private final AtomicInteger counter = new AtomicInteger(0); 40 | 41 | // written from main thread, read from any thread. 42 | private volatile ResourceCallback resourceCallback; 43 | 44 | /** 45 | * Creates a SimpleCountingIdlingResource 46 | * 47 | * @param resourceName the resource name this resource should report to Espresso. 48 | */ 49 | public SimpleCountingIdlingResource(@NonNull String resourceName) { 50 | mResourceName = checkNotNull(resourceName); 51 | } 52 | 53 | @Override 54 | public String getName() { 55 | return mResourceName; 56 | } 57 | 58 | @Override 59 | public boolean isIdleNow() { 60 | return counter.get() == 0; 61 | } 62 | 63 | @Override 64 | public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { 65 | this.resourceCallback = resourceCallback; 66 | } 67 | 68 | /** 69 | * Increments the count of in-flight transactions to the resource being monitored. 70 | */ 71 | public void increment() { 72 | counter.getAndIncrement(); 73 | } 74 | 75 | /** 76 | * Decrements the count of in-flight transactions to the resource being monitored. 77 | * 78 | * If this operation results in the counter falling below 0 - an exception is raised. 79 | * 80 | * @throws IllegalStateException if the counter is below 0. 81 | */ 82 | public void decrement() { 83 | int counterVal = counter.decrementAndGet(); 84 | if (counterVal == 0) { 85 | // we've gone from non-zero to zero. That means we're idle now! Tell espresso. 86 | if (null != resourceCallback) { 87 | resourceCallback.onTransitionToIdle(); 88 | } 89 | } 90 | 91 | if (counterVal < 0) { 92 | throw new IllegalArgumentException("Counter has been corrupted!"); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable-hdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbaena/android-architecture/6d11fc058e46385fa522602fc5fb378ad7a9d8a5/todoapp/app/src/main/res/drawable-hdpi/logo.png -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable-mdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbaena/android-architecture/6d11fc058e46385fa522602fc5fb378ad7a9d8a5/todoapp/app/src/main/res/drawable-mdpi/logo.png -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbaena/android-architecture/6d11fc058e46385fa522602fc5fb378ad7a9d8a5/todoapp/app/src/main/res/drawable-xhdpi/logo.png -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbaena/android-architecture/6d11fc058e46385fa522602fc5fb378ad7a9d8a5/todoapp/app/src/main/res/drawable-xxhdpi/logo.png -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable-xxxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbaena/android-architecture/6d11fc058e46385fa522602fc5fb378ad7a9d8a5/todoapp/app/src/main/res/drawable-xxxhdpi/logo.png -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/ic_assignment_turned_in_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/ic_check_circle_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/ic_done.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/ic_edit.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 25 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/ic_filter_list.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/ic_list.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/ic_menu.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/ic_statistics.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/ic_statistics_100dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/ic_statistics_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/ic_verified_user_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/list_completed_touch_feedback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/drawable/touch_feedback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/layout/addtask_act.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 24 | 25 | 29 | 30 | 33 | 34 | 42 | 43 | 44 | 48 | 49 | 53 | 54 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/layout/addtask_frag.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 21 | 29 | 30 | 37 | 38 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/layout/nav_header.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 25 | 26 | 31 | 32 | 38 | 39 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/layout/statistics_act.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 25 | 26 | 30 | 31 | 34 | 35 | 43 | 44 | 45 | 49 | 50 | 51 | 52 | 53 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/layout/statistics_frag.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 25 | 26 | 32 | 33 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/layout/task_item.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 26 | 27 | 32 | 33 | 40 | 41 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/layout/taskdetail_act.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 24 | 25 | 28 | 29 | 37 | 38 | 39 | 43 | 44 | 48 | 49 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/layout/taskdetail_frag.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 25 | 26 | 32 | 33 | 39 | 40 | 48 | 49 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/layout/tasks_act.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 25 | 26 | 30 | 31 | 34 | 35 | 43 | 44 | 45 | 49 | 50 | 54 | 55 | 64 | 65 | 66 | 67 | 68 | 69 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/layout/tasks_frag.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 21 | 22 | 27 | 28 | 33 | 34 | 44 | 45 | 49 | 50 | 51 | 57 | 58 | 64 | 65 | 72 | 73 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/menu/drawer_actions.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 |

18 | 19 | 23 | 27 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/menu/filter_tasks.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 23 | 26 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/menu/taskdetail_fragment_menu.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 23 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/menu/tasks_fragment_menu.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 24 | 28 | 32 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbaena/android-architecture/6d11fc058e46385fa522602fc5fb378ad7a9d8a5/todoapp/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /todoapp/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbaena/android-architecture/6d11fc058e46385fa522602fc5fb378ad7a9d8a5/todoapp/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /todoapp/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbaena/android-architecture/6d11fc058e46385fa522602fc5fb378ad7a9d8a5/todoapp/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /todoapp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbaena/android-architecture/6d11fc058e46385fa522602fc5fb378ad7a9d8a5/todoapp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /todoapp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbaena/android-architecture/6d11fc058e46385fa522602fc5fb378ad7a9d8a5/todoapp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /todoapp/app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 27 | 28 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 21 | 64dp 22 | 23 | 24dp 24 | 25 | 26 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | #455A64 19 | #263238 20 | #D50000 21 | 22 | #CCCCCC 23 | 24 | #CFD8DC 25 | 26 | 27 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 16dp 20 | 16dp 21 | 22 | 8dp 23 | 24 | 16dp 25 | 26 | 27 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | TO-DO-MVP-CLEAN 19 | New TO-DO 20 | Edit TO-DO 21 | Task marked complete 22 | Task marked active 23 | Error while loading tasks 24 | Completed tasks cleared 25 | Filter 26 | Clear completed 27 | Delete task 28 | TO-DOs 29 | Title 30 | Enter your TO-DO here. 31 | TO DOs cannot be empty 32 | TO-DO saved 33 | TO-DO List 34 | Statistics 35 | You have no tasks. 36 | Active tasks: 37 | Completed tasks: 38 | Error loading statistics. 39 | No data 40 | LOADING 41 | 42 | 43 | @string/nav_all 44 | @string/nav_active 45 | @string/nav_completed 46 | 47 | All 48 | Active 49 | Completed 50 | All TO-DOs 51 | Active TO-DOs 52 | Completed TO-DOs 53 | You have no TO-DOs! 54 | You have no active TO-DOs! 55 | You have no completed TO-DOs! 56 | Add a TO-DO item + 57 | Refresh 58 | 59 | 60 | -------------------------------------------------------------------------------- /todoapp/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 28 | 29 |