├── .gitignore ├── LICENSE ├── LICENSE-CODE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── microsoft │ │ └── sample │ │ ├── TestUtils.java │ │ ├── custom │ │ └── action │ │ │ └── NavigationViewActions.java │ │ ├── data │ │ └── source │ │ │ └── local │ │ │ └── TasksLocalDataSourceTest.java │ │ └── tasks │ │ ├── AppNavigationTest.java │ │ └── TasksScreenTest.java │ ├── androidTestMock │ └── java │ │ └── com │ │ └── microsoft │ │ └── sample │ │ ├── addedittask │ │ └── AddEditTaskScreenTest.java │ │ ├── statistics │ │ └── StatisticsScreenTest.java │ │ └── taskdetail │ │ └── TaskDetailScreenTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── microsoft │ │ │ └── sample │ │ │ ├── BasePresenter.java │ │ │ ├── BaseView.java │ │ │ ├── addedittask │ │ │ ├── AddEditTaskActivity.java │ │ │ ├── AddEditTaskContract.java │ │ │ ├── AddEditTaskFragment.java │ │ │ └── AddEditTaskPresenter.java │ │ │ ├── data │ │ │ ├── Task.java │ │ │ └── source │ │ │ │ ├── TasksDataSource.java │ │ │ │ ├── TasksRepository.java │ │ │ │ ├── local │ │ │ │ ├── TasksDbHelper.java │ │ │ │ ├── TasksLocalDataSource.java │ │ │ │ └── TasksPersistenceContract.java │ │ │ │ └── remote │ │ │ │ └── TasksRemoteDataSource.java │ │ │ ├── statistics │ │ │ ├── StatisticsActivity.java │ │ │ ├── StatisticsContract.java │ │ │ ├── StatisticsFragment.java │ │ │ └── StatisticsPresenter.java │ │ │ ├── taskdetail │ │ │ ├── TaskDetailActivity.java │ │ │ ├── TaskDetailContract.java │ │ │ ├── TaskDetailFragment.java │ │ │ └── TaskDetailPresenter.java │ │ │ ├── tasks │ │ │ ├── ScrollChildSwipeRefreshLayout.java │ │ │ ├── TasksActivity.java │ │ │ ├── TasksContract.java │ │ │ ├── TasksFilterType.java │ │ │ ├── TasksFragment.java │ │ │ └── TasksPresenter.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 │ │ └── microsoft │ │ └── sample │ │ ├── Injection.java │ │ └── data │ │ └── FakeTasksRemoteDataSource.java │ ├── prod │ └── java │ │ └── com │ │ └── microsoft │ │ └── sample │ │ └── Injection.java │ └── test │ └── java │ └── com │ └── microsoft │ └── sample │ ├── addedittask │ └── AddEditTaskPresenterTest.java │ ├── data │ └── source │ │ └── TasksRepositoryTest.java │ ├── statistics │ └── StatisticsPresenterTest.java │ ├── taskdetail │ └── TaskDetailPresenterTest.java │ └── tasks │ └── TasksPresenterTest.java ├── azure-pipelines.yml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/assetWizardSettings.xml 41 | .idea/dictionaries 42 | .idea/libraries 43 | .idea/caches 44 | 45 | # Keystore files 46 | # Uncomment the following line if you do not want to check your keystore files in. 47 | #*.jks 48 | 49 | # External native build folder generated in Android Studio 2.2 and later 50 | .externalNativeBuild 51 | 52 | # Google Services (e.g. APIs or Firebase) 53 | google-services.json 54 | 55 | # Freeline 56 | freeline.py 57 | freeline/ 58 | freeline_project_description.json 59 | 60 | # fastlane 61 | fastlane/report.xml 62 | fastlane/Preview.html 63 | fastlane/screenshots 64 | fastlane/test_output 65 | fastlane/readme.md 66 | -------------------------------------------------------------------------------- /LICENSE-CODE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) Microsoft Corporation 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 5 | associated documentation files (the "Software"), to deal in the Software without restriction, 6 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial 11 | portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 14 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 15 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 16 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 17 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 5 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 6 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 7 | 8 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 9 | a CLA and decorate the PR appropriately (e.g., label, comment, documentation). Simply follow the instructions 10 | provided by the bot. You will only need to do this once across all repos using our CLA. 11 | 12 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 13 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 14 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 15 | 16 | # Legal Notices 17 | 18 | Microsoft and any contributors grant you a license to the Microsoft documentation and other content 19 | in this repository under the [Creative Commons Attribution 4.0 International Public License](https://creativecommons.org/licenses/by/4.0/legalcode), 20 | see the [LICENSE](LICENSE) file, and grant you a license to any code in the repository under the [MIT License](https://opensource.org/licenses/MIT), see the 21 | [LICENSE-CODE](LICENSE-CODE) file. 22 | 23 | Microsoft, Windows, Microsoft Azure and/or other Microsoft products and services referenced in the documentation 24 | may be either trademarks or registered trademarks of Microsoft in the United States and/or other countries. 25 | The licenses for this project do not grant you rights to use any Microsoft names, logos, or trademarks. 26 | Microsoft's general trademark guidelines can be found at http://go.microsoft.com/fwlink/?LinkID=254653. 27 | 28 | Privacy information can be found at https://privacy.microsoft.com/en-us/ 29 | 30 | Microsoft and any contributors reserve all others rights, whether under their respective copyrights, patents, 31 | or trademarks, whether by implication, estoppel or otherwise. 32 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /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.todomvp" 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 | minifyEnabled true 20 | // Uses new built-in shrinker http://tools.android.com/tech-docs/new-build-system/built-in-shrinker 21 | useProguard false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | testProguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguardTest-rules.pro' 24 | } 25 | 26 | release { 27 | minifyEnabled true 28 | useProguard true 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 30 | testProguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguardTest-rules.pro' 31 | } 32 | } 33 | 34 | // If you need to add more flavors, consider using flavor dimensions. 35 | productFlavors { 36 | mock { 37 | applicationIdSuffix = ".mock" 38 | } 39 | prod { 40 | 41 | } 42 | } 43 | 44 | // Remove mockRelease as it's not needed. 45 | android.variantFilter { variant -> 46 | if(variant.buildType.name.equals('release') 47 | && variant.getFlavors().get(0).name.equals('mock')) { 48 | variant.setIgnore(true); 49 | } 50 | } 51 | 52 | // Always show the result of every unit test, even if it passes. 53 | testOptions.unitTests.all { 54 | testLogging { 55 | events 'passed', 'skipped', 'failed', 'standardOut', 'standardError' 56 | } 57 | } 58 | } 59 | 60 | /* 61 | Dependency versions are defined in the top level build.gradle file. This helps keeping track of 62 | all versions in a single place. This improves readability and helps managing project complexity. 63 | */ 64 | dependencies { 65 | // App's dependencies, including test 66 | compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion" 67 | compile "com.android.support:cardview-v7:$rootProject.supportLibraryVersion" 68 | compile "com.android.support:design:$rootProject.supportLibraryVersion" 69 | compile "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion" 70 | compile "com.android.support:support-v4:$rootProject.supportLibraryVersion" 71 | compile "com.android.support.test.espresso:espresso-idling-resource:$rootProject.espressoVersion" 72 | compile "com.google.guava:guava:$rootProject.guavaVersion" 73 | 74 | // Dependencies for local unit tests 75 | testCompile "junit:junit:$rootProject.ext.junitVersion" 76 | testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion" 77 | testCompile "org.hamcrest:hamcrest-all:$rootProject.ext.hamcrestVersion" 78 | 79 | // Android Testing Support Library's runner and rules 80 | androidTestCompile "com.android.support.test:runner:$rootProject.ext.runnerVersion" 81 | androidTestCompile "com.android.support.test:rules:$rootProject.ext.runnerVersion" 82 | 83 | // Dependencies for Android unit tests 84 | androidTestCompile "junit:junit:$rootProject.ext.junitVersion" 85 | androidTestCompile "org.mockito:mockito-core:$rootProject.ext.mockitoVersion" 86 | androidTestCompile 'com.google.dexmaker:dexmaker:1.2' 87 | androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2' 88 | 89 | // Espresso UI Testing 90 | androidTestCompile "com.android.support.test.espresso:espresso-core:$rootProject.espressoVersion" 91 | androidTestCompile "com.android.support.test.espresso:espresso-contrib:$rootProject.espressoVersion" 92 | androidTestCompile "com.android.support.test.espresso:espresso-intents:$rootProject.espressoVersion" 93 | 94 | // Resolve conflicts between main and test APK: 95 | androidTestCompile "com.android.support:support-annotations:$rootProject.supportLibraryVersion" 96 | androidTestCompile "com.android.support:support-v4:$rootProject.supportLibraryVersion" 97 | androidTestCompile "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion" 98 | androidTestCompile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion" 99 | androidTestCompile "com.android.support:design:$rootProject.supportLibraryVersion" 100 | } 101 | -------------------------------------------------------------------------------- /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.** -------------------------------------------------------------------------------- /app/src/androidTest/java/com/microsoft/sample/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.microsoft.sample; 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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/microsoft/sample/custom/action/NavigationViewActions.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.microsoft.sample.custom.action; 18 | 19 | import org.hamcrest.Matcher; 20 | 21 | import android.content.res.Resources.NotFoundException; 22 | import android.support.design.widget.NavigationView; 23 | import android.support.test.espresso.PerformException; 24 | import android.support.test.espresso.UiController; 25 | import android.support.test.espresso.ViewAction; 26 | import android.support.test.espresso.matcher.ViewMatchers; 27 | import android.support.test.espresso.util.HumanReadables; 28 | import android.support.v4.widget.DrawerLayout; 29 | import android.view.Menu; 30 | import android.view.MenuItem; 31 | import android.view.View; 32 | 33 | import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; 34 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast; 35 | import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; 36 | import static org.hamcrest.Matchers.allOf; 37 | 38 | /** 39 | * View actions for interacting with {@link NavigationView} 40 | */ 41 | public final class NavigationViewActions { 42 | 43 | private NavigationViewActions() { 44 | // no Instance 45 | } 46 | 47 | /** 48 | * Returns a {@link ViewAction} that navigates to a menu item in {@link NavigationView} using a 49 | * menu item resource id. 50 | * 51 | *

52 | * View constraints: 53 | *