├── .gitignore ├── README.md ├── app ├── .gitignore ├── adb_all.sh ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── codepath │ │ └── testingdemo │ │ ├── ApplicationTest.java │ │ ├── actions │ │ ├── CustomViewActions.java │ │ └── OrientationChangeAction.java │ │ ├── activities │ │ └── BaseInstrumentationTest.java │ │ ├── helpers │ │ ├── DecoratedCustomHttpClient.java │ │ └── EspressoHelpers.java │ │ ├── matchers │ │ └── CustomViewMatchers.java │ │ └── rules │ │ └── DisableAnimationsRule.java │ ├── debug │ └── AndroidManifest.xml │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── codepath │ │ │ └── testingdemo │ │ │ ├── activities │ │ │ ├── AsyncTaskActivity.java │ │ │ ├── CameraActivity.java │ │ │ ├── CustomNetworkRequestActivity.java │ │ │ ├── DateTimePickerActivity.java │ │ │ ├── EspressoDemoActivity.java │ │ │ ├── GameLevelActivity.java │ │ │ ├── IntentOneActivity.java │ │ │ ├── IntentTwoActivity.java │ │ │ ├── LevelCompleteActivity.java │ │ │ ├── ListViewActivity.java │ │ │ ├── LocalizedActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── NetworkRequestActivity.java │ │ │ ├── PasswordActivity.java │ │ │ ├── RecyclerViewActivity.java │ │ │ ├── ScrollActivity.java │ │ │ ├── SearchHistoryActivity.java │ │ │ └── ViewPagerActivity.java │ │ │ ├── adapters │ │ │ ├── PostsListViewAdapter.java │ │ │ └── PostsRecyclerViewAdapter.java │ │ │ ├── data │ │ │ ├── Data.java │ │ │ └── GameLevel.java │ │ │ ├── fragments │ │ │ ├── DatePickerFragment.java │ │ │ ├── EmptyFragment.java │ │ │ └── TimePickerFragment.java │ │ │ ├── models │ │ │ ├── Post.java │ │ │ └── User.java │ │ │ └── networking │ │ │ └── DemoHttpClient.java │ └── res │ │ ├── layout │ │ ├── activity_async_task.xml │ │ ├── activity_camera.xml │ │ ├── activity_custom_network_request.xml │ │ ├── activity_date_time_picker.xml │ │ ├── activity_espresso_demo.xml │ │ ├── activity_intent_one.xml │ │ ├── activity_intent_two.xml │ │ ├── activity_level_complete.xml │ │ ├── activity_list_view.xml │ │ ├── activity_localized.xml │ │ ├── activity_main.xml │ │ ├── activity_network_request.xml │ │ ├── activity_password.xml │ │ ├── activity_recycler_view.xml │ │ ├── activity_scroll.xml │ │ ├── activity_search_history.xml │ │ ├── activity_view_pager.xml │ │ ├── fragment_empty.xml │ │ └── layout_item_post.xml │ │ ├── menu │ │ ├── menu_camera.xml │ │ ├── menu_date_time_picker.xml │ │ ├── menu_espresso_demo.xml │ │ ├── menu_first.xml │ │ ├── menu_level_complete.xml │ │ ├── menu_list_view.xml │ │ ├── menu_listener.xml │ │ ├── menu_localized.xml │ │ ├── menu_main.xml │ │ ├── menu_network_request.xml │ │ ├── menu_password.xml │ │ ├── menu_scrolling.xml │ │ ├── menu_search_history.xml │ │ ├── menu_second.xml │ │ └── menu_view_pager.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── values-es │ │ └── strings.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com.codepath.testingdemo │ ├── activities │ ├── AsyncTaskActivityTest.java │ ├── BaseActivityTest.java │ ├── IntentOneActivityTest.java │ ├── LocalizedActivityTest.java │ └── SearchHistoryActivityTest.java │ ├── adapters │ └── PostsAdapterTest.java │ └── models │ ├── PostTest.java │ └── PostTestParameterized.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | *.iml 9 | .idea 10 | .env 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # android-testing-demo 2 | Starter Code for Android Robolectric and Espresso Testing Unit 3 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/adb_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script adb+ 3 | # Usage 4 | # You can run any command adb provides on all your currently connected devices 5 | # ./adb+ is the equivalent of ./adb -s 6 | # 7 | # Examples 8 | # ./adb+ version 9 | # ./adb+ install apidemo.apk 10 | # ./adb+ uninstall com.example.android.apis 11 | 12 | adb devices | while read line 13 | do 14 | if [ ! "$line" = "" ] && [ `echo $line | awk '{print $2}'` = "device" ] 15 | then 16 | device=`echo $line | awk '{print $1}'` 17 | echo "$device $@ ..." 18 | adb -s $device $@ 19 | fi 20 | done 21 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.codepath.testingdemo" 9 | minSdkVersion 16 10 | targetSdkVersion 22 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | } 16 | 17 | packagingOptions { 18 | exclude 'LICENSE.txt' 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | 28 | productFlavors { 29 | dev { 30 | applicationId "com.codepath.testingdemo" 31 | } 32 | 33 | production { 34 | applicationId "com.codepath.testingdemo" 35 | } 36 | } 37 | } 38 | 39 | // Get the path to ADB. Required when running tests directly from Android Studio. 40 | // Source: http://stackoverflow.com/a/26771087/112705 41 | def adb = android.getAdbExe().toString() 42 | 43 | // Source: http://stackoverflow.com/q/29908110/112705 44 | afterEvaluate { 45 | task grantAnimationPermissionDev(type: Exec, dependsOn: 'installDevDebug') { 46 | commandLine "$adb shell pm grant $android.productFlavors.dev.applicationId android.permission.SET_ANIMATION_SCALE".split(' ') 47 | } 48 | 49 | task grantAnimationPermissionProduction(type: Exec, dependsOn: 'installProductionDebug') { 50 | commandLine "$adb shell pm grant $android.productFlavors.production.applicationId android.permission.SET_ANIMATION_SCALE".split(' ') 51 | } 52 | 53 | // When launching individual tests from Android Studio, it seems that only the assemble tasks 54 | // get called directly, not the install* versions 55 | tasks.each { task -> 56 | if (task.name.startsWith('assembleDevDebugAndroidTest')) { 57 | task.dependsOn grantAnimationPermissionDev 58 | } else if (task.name.startsWith('assembleProductionDebugAndroidTest')) { 59 | task.dependsOn grantAnimationPermissionProduction 60 | } 61 | } 62 | } 63 | 64 | dependencies { 65 | compile fileTree(dir: 'libs', include: ['*.jar']) 66 | compile 'com.android.support:appcompat-v7:22.2.0' 67 | compile 'com.android.support:recyclerview-v7:21.0.0' 68 | compile 'com.loopj.android:android-async-http:1.4.7' 69 | compile group: 'com.google.guava', name: 'guava', version: '18.0' 70 | testCompile 'junit:junit:4.12' 71 | testCompile 'org.robolectric:robolectric:3.0' 72 | testCompile('com.squareup.assertj:assertj-android:1.0.0') { 73 | exclude group: 'com.android.support', module: 'support-annotations' 74 | } 75 | testCompile('com.squareup.assertj:assertj-android-recyclerview-v7:1.0.0') { 76 | exclude group: 'com.android.support', module: 'recyclerview-v7' 77 | exclude group: 'com.android.support', module: 'support-annotations' 78 | } 79 | androidTestCompile 'org.mockito:mockito-core:1.10.19' 80 | androidTestCompile "com.google.dexmaker:dexmaker:1.2" 81 | androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2" 82 | androidTestCompile 'com.android.support.test:runner:0.3' 83 | androidTestCompile 'com.android.support.test:rules:0.3' 84 | androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2' 85 | androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2' 86 | androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2') { 87 | exclude group: 'com.android.support', module: 'appcompat' 88 | exclude group: 'com.android.support', module: 'support-v4' 89 | exclude module: 'recyclerview-v7' 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /opt/android_sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/codepath/testingdemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/codepath/testingdemo/actions/CustomViewActions.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.actions; 2 | 3 | import android.support.test.espresso.UiController; 4 | import android.support.test.espresso.ViewAction; 5 | import android.view.View; 6 | 7 | import org.hamcrest.Matcher; 8 | 9 | public class CustomViewActions { 10 | 11 | public static ViewAction clickChildViewWithId(final int id) { 12 | return new ViewAction() { 13 | @Override 14 | public Matcher getConstraints() { 15 | return null; 16 | } 17 | 18 | @Override 19 | public String getDescription() { 20 | return "Click on a child view with the specified id"; 21 | } 22 | 23 | @Override 24 | public void perform(UiController uiController, View view) { 25 | View viewToClick = view.findViewById(id); 26 | if (viewToClick != null) { 27 | viewToClick.performClick(); 28 | } 29 | } 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/codepath/testingdemo/actions/OrientationChangeAction.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.actions; 2 | 3 | /* 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2015 - Nathan Barraille 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | */ 27 | 28 | import android.app.Activity; 29 | import android.content.pm.ActivityInfo; 30 | import android.support.test.espresso.UiController; 31 | import android.support.test.espresso.ViewAction; 32 | import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; 33 | import android.support.test.runner.lifecycle.Stage; 34 | import android.view.View; 35 | 36 | import org.hamcrest.Matcher; 37 | 38 | import java.util.Collection; 39 | 40 | import static android.support.test.espresso.matcher.ViewMatchers.isRoot; 41 | 42 | /** 43 | * An Espresso ViewAction that changes the orientation of the screen 44 | */ 45 | public class OrientationChangeAction implements ViewAction { 46 | private final int orientation; 47 | 48 | private OrientationChangeAction(int orientation) { 49 | this.orientation = orientation; 50 | } 51 | 52 | @Override 53 | public Matcher getConstraints() { 54 | return isRoot(); 55 | } 56 | 57 | @Override 58 | public String getDescription() { 59 | return "change orientation to " + orientation; 60 | } 61 | 62 | @Override 63 | public void perform(UiController uiController, View view) { 64 | uiController.loopMainThreadUntilIdle(); 65 | final Activity activity = (Activity) view.getContext(); 66 | activity.setRequestedOrientation(orientation); 67 | 68 | Collection resumedActivities = ActivityLifecycleMonitorRegistry 69 | .getInstance().getActivitiesInStage(Stage.RESUMED); 70 | if (resumedActivities.isEmpty()) { 71 | throw new RuntimeException("Could not change orientation"); 72 | } 73 | } 74 | 75 | public static ViewAction orientationLandscape() { 76 | return new OrientationChangeAction(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 77 | } 78 | 79 | public static ViewAction orientationPortrait() { 80 | return new OrientationChangeAction(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/codepath/testingdemo/activities/BaseInstrumentationTest.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import com.codepath.testingdemo.rules.DisableAnimationsRule; 4 | 5 | import org.junit.ClassRule; 6 | 7 | public class BaseInstrumentationTest { 8 | 9 | @ClassRule 10 | public static DisableAnimationsRule disableAnimationsRule = new DisableAnimationsRule(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/codepath/testingdemo/helpers/DecoratedCustomHttpClient.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.helpers; 2 | 3 | import android.support.test.espresso.contrib.CountingIdlingResource; 4 | 5 | import com.codepath.testingdemo.activities.CustomNetworkRequestActivity; 6 | 7 | // Wrapper around CustomHttpClient that uses CountingIdlingResource to let Espresso know 8 | // when it is idle 9 | public class DecoratedCustomHttpClient implements CustomNetworkRequestActivity.CustomHttpClient { 10 | private final CustomNetworkRequestActivity.CustomHttpClient customHttpClient; 11 | private final CountingIdlingResource customHttpClientIdlingResource; 12 | 13 | public DecoratedCustomHttpClient(CustomNetworkRequestActivity.CustomHttpClient customHttpClient, 14 | CountingIdlingResource customHttpClientIdlingResource) { 15 | this.customHttpClient = customHttpClient; 16 | this.customHttpClientIdlingResource = customHttpClientIdlingResource; 17 | } 18 | 19 | @Override 20 | public void makeRequest() { 21 | // Use CountingIdlingResource to track in-flight network requests. 22 | // Whenever the count goes to zero, Espresso will be notified that this resource is idle 23 | // and the test will be able to proceed. 24 | customHttpClientIdlingResource.increment(); 25 | try { 26 | customHttpClient.makeRequest(); 27 | } finally { 28 | customHttpClientIdlingResource.decrement(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/codepath/testingdemo/helpers/EspressoHelpers.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.helpers; 2 | 3 | import android.app.Activity; 4 | import android.support.annotation.IdRes; 5 | import android.support.annotation.StringRes; 6 | import android.support.test.InstrumentationRegistry; 7 | import android.support.test.espresso.Espresso; 8 | import android.support.test.espresso.ViewInteraction; 9 | import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.view.View; 12 | 13 | import org.hamcrest.Matcher; 14 | 15 | import java.util.Collection; 16 | 17 | import static android.support.test.espresso.Espresso.onView; 18 | import static android.support.test.espresso.action.ViewActions.click; 19 | import static android.support.test.espresso.action.ViewActions.scrollTo; 20 | import static android.support.test.espresso.action.ViewActions.typeText; 21 | import static android.support.test.espresso.assertion.LayoutAssertions.noEllipsizedText; 22 | import static android.support.test.espresso.assertion.LayoutAssertions.noMultilineButtons; 23 | import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA; 24 | import static android.support.test.espresso.matcher.ViewMatchers.withChild; 25 | import static android.support.test.espresso.matcher.ViewMatchers.withHint; 26 | import static android.support.test.espresso.matcher.ViewMatchers.withId; 27 | import static android.support.test.espresso.matcher.ViewMatchers.withParent; 28 | import static android.support.test.runner.lifecycle.Stage.RESUMED; 29 | import static com.codepath.testingdemo.matchers.CustomViewMatchers.nthChildOf; 30 | import static com.codepath.testingdemo.matchers.CustomViewMatchers.withRecyclerView; 31 | import static org.hamcrest.Matchers.allOf; 32 | 33 | public class EspressoHelpers { 34 | 35 | // Text Entry 36 | 37 | public static ViewInteraction enterTextIntoViewWithHint(String textToEnter, @StringRes int hintResourceId) { 38 | return onView(withHint(hintResourceId)).perform(typeText(textToEnter)); 39 | } 40 | 41 | public static ViewInteraction enterTextIntoViewWithId(String textToEnter, @IdRes int viewResourceId) { 42 | return onView(withId(viewResourceId)).perform(typeText(textToEnter)); 43 | } 44 | 45 | // Scrolling 46 | 47 | public static ViewInteraction scrollToViewWithId(@IdRes int viewResourceId) { 48 | return onView(withId(viewResourceId)).perform(scrollTo()); 49 | } 50 | 51 | // Tapping 52 | 53 | public static ViewInteraction tapViewWithId(@IdRes int viewResourceId) { 54 | return onView(withId(viewResourceId)).perform(click()); 55 | } 56 | 57 | // RecyclerView 58 | 59 | public static ViewInteraction onRecyclerViewItemAtPosition(@IdRes int recyclerViewResourceId, 60 | int position) { 61 | 62 | 63 | return onView(nthChildOf(withRecyclerView(recyclerViewResourceId), position)); 64 | } 65 | 66 | // Requires unique identifier for each row to be useful 67 | public static ViewInteraction onRecyclerViewItem(@IdRes int recyclerViewResourceId, 68 | @IdRes int identifyingViewResourceId, 69 | Matcher identifyingMatcher, 70 | Matcher childMatcher) { 71 | Matcher itemView = 72 | allOf( 73 | withParent(withRecyclerView(recyclerViewResourceId)), 74 | withChild( 75 | allOf(withId(identifyingViewResourceId), identifyingMatcher))); 76 | 77 | return Espresso.onView(allOf(isDescendantOfA(itemView), childMatcher)); 78 | } 79 | 80 | // Test that there are no ellipsized texts. 81 | public static ViewInteraction checkNoEllipsizedTextInViewHierarchy(@IdRes int parentViewId) { 82 | return onView(withId(parentViewId)).check(noEllipsizedText()); 83 | } 84 | 85 | 86 | // Test that there are no multiline buttons. 87 | public static ViewInteraction checkNoMultilineButtonsInViewHierarchy(@IdRes int parentViewId) { 88 | return onView(withId(parentViewId)).check(noMultilineButtons()); 89 | } 90 | 91 | // Get activity instance for multi-activity Espresso tests 92 | public static Activity getActivityInstance() { 93 | final Activity[] activity = new Activity[1]; 94 | InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 95 | public void run() { 96 | Collection resumedActivities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(RESUMED); 97 | if (resumedActivities.iterator().hasNext()) { 98 | activity[0] = (AppCompatActivity)resumedActivities.iterator().next(); 99 | } 100 | } 101 | }); 102 | 103 | return activity[0]; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/codepath/testingdemo/matchers/CustomViewMatchers.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.matchers; 2 | 3 | import android.support.annotation.IdRes; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import org.hamcrest.Description; 9 | import org.hamcrest.Matcher; 10 | import org.hamcrest.TypeSafeMatcher; 11 | 12 | import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; 13 | import static android.support.test.espresso.matcher.ViewMatchers.withId; 14 | import static org.hamcrest.Matchers.allOf; 15 | 16 | public class CustomViewMatchers { 17 | 18 | public static Matcher withRecyclerView(@IdRes int viewId) { 19 | return allOf(isAssignableFrom(RecyclerView.class), withId(viewId)); 20 | } 21 | 22 | // Pulled from http://stackoverflow.com/a/30073528/681493 23 | public static Matcher nthChildOf(final Matcher parentMatcher, final int childPosition) { 24 | return new TypeSafeMatcher() { 25 | 26 | @Override 27 | public void describeTo(Description description) { 28 | description.appendText("with " + childPosition + " child view of type parentMatcher"); 29 | } 30 | 31 | @Override 32 | public boolean matchesSafely(View view) { 33 | if (!(view.getParent() instanceof ViewGroup)) { 34 | return parentMatcher.matches(view.getParent()); 35 | } 36 | 37 | ViewGroup viewGroup = (ViewGroup) view.getParent(); 38 | 39 | return parentMatcher.matches(view.getParent()) && 40 | viewGroup.getChildAt(childPosition).equals(view); 41 | } 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/codepath/testingdemo/rules/DisableAnimationsRule.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.rules; 2 | 3 | import android.os.IBinder; 4 | 5 | import org.junit.rules.TestRule; 6 | import org.junit.runner.Description; 7 | import org.junit.runners.model.Statement; 8 | 9 | import java.lang.reflect.Method; 10 | import java.util.Arrays; 11 | 12 | public class DisableAnimationsRule implements TestRule { 13 | private Method mSetAnimationScalesMethod; 14 | private Method mGetAnimationScalesMethod; 15 | private Object mWindowManagerObject; 16 | 17 | public DisableAnimationsRule() { 18 | try { 19 | Class windowManagerStubClazz = Class.forName("android.view.IWindowManager$Stub"); 20 | Method asInterface = windowManagerStubClazz.getDeclaredMethod("asInterface", IBinder.class); 21 | 22 | Class serviceManagerClazz = Class.forName("android.os.ServiceManager"); 23 | Method getService = serviceManagerClazz.getDeclaredMethod("getService", String.class); 24 | 25 | Class windowManagerClazz = Class.forName("android.view.IWindowManager"); 26 | 27 | mSetAnimationScalesMethod = windowManagerClazz.getDeclaredMethod("setAnimationScales", float[].class); 28 | mGetAnimationScalesMethod = windowManagerClazz.getDeclaredMethod("getAnimationScales"); 29 | 30 | IBinder windowManagerBinder = (IBinder) getService.invoke(null, "window"); 31 | mWindowManagerObject = asInterface.invoke(null, windowManagerBinder); 32 | } 33 | catch (Exception e) { 34 | throw new RuntimeException("Failed to access animation methods", e); 35 | } 36 | } 37 | 38 | @Override 39 | public Statement apply(final Statement statement, Description description) { 40 | return new Statement() { 41 | @Override 42 | public void evaluate() throws Throwable { 43 | setAnimationScaleFactors(0.0f); 44 | try { 45 | statement.evaluate(); 46 | } 47 | finally { 48 | setAnimationScaleFactors(1.0f); 49 | } 50 | } 51 | }; 52 | } 53 | 54 | private void setAnimationScaleFactors(float scaleFactor) throws Exception { 55 | float[] scaleFactors = (float[]) mGetAnimationScalesMethod.invoke(mWindowManagerObject); 56 | Arrays.fill(scaleFactors, scaleFactor); 57 | mSetAnimationScalesMethod.invoke(mWindowManagerObject, scaleFactors); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 30 | 31 | 34 | 35 | 38 | 39 | 42 | 43 | 46 | 47 | 50 | 51 | 54 | 55 | 58 | 59 | 62 | 63 | 66 | 67 | 70 | 71 | 74 | 75 | 78 | 79 | 82 | 83 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/AsyncTaskActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.os.AsyncTask; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.Button; 8 | import android.widget.Toast; 9 | 10 | import com.codepath.testingdemo.R; 11 | 12 | /* 13 | * Single button that kicks off an AsyncTask (simulating a network request) and updates 14 | * the button text once "request" is complete. 15 | */ 16 | public class AsyncTaskActivity extends AppCompatActivity { 17 | 18 | private Button btnSubmit; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_async_task); 24 | 25 | btnSubmit = (Button)findViewById(R.id.btnSubmit); 26 | } 27 | 28 | public void runAsyncTask() { 29 | new AsyncTask() { 30 | protected Void doInBackground(Void... params) { 31 | try { 32 | Thread.sleep(5000); // 5 seconds 33 | } catch (InterruptedException e) { 34 | e.printStackTrace(); 35 | } 36 | return null; 37 | } 38 | 39 | protected void onPostExecute(Void result) { 40 | Toast.makeText(AsyncTaskActivity.this, "Done", Toast.LENGTH_SHORT).show(); 41 | btnSubmit.setText("Done"); 42 | } 43 | }.execute(); 44 | } 45 | 46 | public void handleSubmit(View view) { 47 | Toast.makeText(this, "Sending request...", Toast.LENGTH_SHORT).show(); 48 | runAsyncTask(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/CameraActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Bitmap; 5 | import android.os.Bundle; 6 | import android.provider.MediaStore; 7 | import android.view.View; 8 | import android.widget.Button; 9 | import android.widget.ImageView; 10 | 11 | import com.codepath.testingdemo.R; 12 | 13 | public class CameraActivity extends GameLevelActivity { 14 | 15 | static final int REQUEST_IMAGE_CAPTURE = 1; 16 | ImageView ivThumbnail; 17 | Button btnCompleteLevel; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_camera); 23 | ivThumbnail = (ImageView)findViewById(R.id.ivThumbnail); 24 | btnCompleteLevel = (Button)findViewById(R.id.btnCompleteLevel); 25 | } 26 | 27 | private void dispatchTakePictureIntent() { 28 | Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 29 | if (takePictureIntent.resolveActivity(getPackageManager()) != null) { 30 | startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); 31 | } 32 | } 33 | 34 | public void onTakePictureClicked(View view) { 35 | dispatchTakePictureIntent(); 36 | } 37 | 38 | public void onCompleteLevelClicked(View view) { 39 | launchLevelPassedActivity(); 40 | } 41 | 42 | @Override 43 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 44 | if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { 45 | Bundle extras = data.getExtras(); 46 | Bitmap imageBitmap = (Bitmap) extras.get("data"); 47 | ivThumbnail.setImageBitmap(imageBitmap); 48 | btnCompleteLevel.setEnabled(true); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/CustomNetworkRequestActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.os.Bundle; 4 | import android.os.SystemClock; 5 | import android.support.annotation.VisibleForTesting; 6 | import android.view.View; 7 | import android.widget.Button; 8 | 9 | import com.codepath.testingdemo.R; 10 | 11 | public class CustomNetworkRequestActivity extends GameLevelActivity { 12 | 13 | public interface CustomHttpClient { 14 | void makeRequest(); 15 | } 16 | 17 | private CustomHttpClient customHttpClient; 18 | private Button btnCompleteLevel; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_custom_network_request); 24 | 25 | btnCompleteLevel = (Button)findViewById(R.id.btnCompleteLevel); 26 | 27 | setCustomHttpClient(new CustomHttpClient() { 28 | @Override 29 | public void makeRequest() { 30 | SystemClock.sleep(5000); // 5 seconds 31 | } 32 | }); 33 | } 34 | 35 | public void onMakeRequestClick(View view) { 36 | Thread t = new Thread() { 37 | @Override 38 | public void run() { 39 | customHttpClient.makeRequest(); 40 | runOnUiThread(new Runnable() { 41 | @Override 42 | public void run() { 43 | btnCompleteLevel.setEnabled(true); 44 | } 45 | }); 46 | } 47 | }; 48 | t.start(); 49 | } 50 | 51 | public void onCompleteLevelClicked(View view) { 52 | launchLevelPassedActivity(); 53 | } 54 | 55 | @VisibleForTesting 56 | public CustomHttpClient getCustomHttpClient() { 57 | return customHttpClient; 58 | } 59 | 60 | @VisibleForTesting 61 | public void setCustomHttpClient(CustomHttpClient customHttpClient) { 62 | this.customHttpClient = customHttpClient; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/DateTimePickerActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.DialogFragment; 5 | import android.view.View; 6 | import android.widget.Button; 7 | 8 | import com.codepath.testingdemo.R; 9 | import com.codepath.testingdemo.fragments.DatePickerFragment; 10 | import com.codepath.testingdemo.fragments.TimePickerFragment; 11 | 12 | import java.util.Calendar; 13 | import java.util.GregorianCalendar; 14 | 15 | public class DateTimePickerActivity extends GameLevelActivity 16 | implements DatePickerFragment.OnDateSetListener, TimePickerFragment.OnTimeSetListener { 17 | 18 | private Button btnCompleteLevel; 19 | private final GregorianCalendar expectedCalendar = new GregorianCalendar(1985, 9, 26, 1, 20); 20 | private GregorianCalendar guessCalendar; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_date_time_picker); 26 | 27 | btnCompleteLevel = (Button)findViewById(R.id.btnCompleteLevel); 28 | guessCalendar = new GregorianCalendar(); 29 | } 30 | 31 | public void onDatePickerClicked(View view) { 32 | DialogFragment newFragment = new DatePickerFragment(); 33 | newFragment.show(getSupportFragmentManager(), "datePicker"); 34 | } 35 | 36 | public void onTimePickerClicked(View view) { 37 | DialogFragment newFragment = new TimePickerFragment(); 38 | newFragment.show(getSupportFragmentManager(), "timePicker"); 39 | } 40 | 41 | private void checkIfLevelPassed() { 42 | btnCompleteLevel.setEnabled(expectedCalendar.equals(guessCalendar)); 43 | } 44 | 45 | public void onCompleteLevelClicked(View view) { 46 | launchLevelPassedActivity(); 47 | } 48 | 49 | @Override 50 | public void onDateSet(int year, int month, int day) { 51 | guessCalendar.set(year, month, day); 52 | checkIfLevelPassed(); 53 | } 54 | 55 | @Override 56 | public void onTimeSet(int hour, int minute) { 57 | guessCalendar.set(Calendar.HOUR, hour); 58 | guessCalendar.set(Calendar.HOUR_OF_DAY, hour); 59 | guessCalendar.set(Calendar.MINUTE, minute); 60 | guessCalendar.set(Calendar.SECOND, 0); 61 | guessCalendar.set(Calendar.MILLISECOND, 0); 62 | checkIfLevelPassed(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/EspressoDemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.widget.Button; 7 | import android.widget.EditText; 8 | import android.widget.TextView; 9 | 10 | import com.codepath.testingdemo.R; 11 | import com.codepath.testingdemo.models.User; 12 | 13 | public class EspressoDemoActivity extends AppCompatActivity { 14 | 15 | TextView tvSearchText; 16 | EditText etSearch; 17 | Button btnSubmit; 18 | 19 | TextView tvCurrentUser; 20 | 21 | Button btnDisplayCurrentUser; 22 | 23 | User currentUser; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_espresso_demo); 29 | 30 | etSearch = (EditText)findViewById(R.id.etSearch); 31 | btnSubmit = (Button)findViewById(R.id.btnSubmit); 32 | tvSearchText = (TextView)findViewById(R.id.tvSearchText); 33 | 34 | tvCurrentUser = (TextView)findViewById(R.id.tvCurrentUser); 35 | btnDisplayCurrentUser = (Button)findViewById(R.id.btnDisplayCurrentUser); 36 | 37 | } 38 | 39 | public void onSubmitClicked(View view) { 40 | tvSearchText.setText(etSearch.getText().toString()); 41 | etSearch.setText(""); 42 | } 43 | 44 | public void setCurrentUser(User user) { 45 | this.currentUser = user; 46 | } 47 | 48 | public void onDisplayCurrentUserClicked(View view) { 49 | if (currentUser == null) { 50 | return; 51 | } 52 | tvCurrentUser.setText(currentUser.getUserName()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/GameLevelActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | public abstract class GameLevelActivity extends AppCompatActivity { 8 | public static final String EXTRA_LEVEL = "level"; 9 | 10 | protected int currentLevel; 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | this.currentLevel = getIntent().getIntExtra(EXTRA_LEVEL, 1); 16 | } 17 | 18 | public void launchLevelPassedActivity() { 19 | Intent intent = new Intent(this, LevelCompleteActivity.class); 20 | intent.putExtra(LevelCompleteActivity.EXTRA_LEVEL_NUMBER, this.currentLevel); 21 | startActivity(intent); 22 | finish(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/IntentOneActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.TextView; 8 | import android.widget.Toast; 9 | 10 | import com.codepath.testingdemo.R; 11 | 12 | /* 13 | * Take in a "message" as an intent extra. Use the message to set the TextView text. 14 | * Allow user to launch IntentTwoActivity by hitting "Next" button. 15 | */ 16 | public class IntentOneActivity extends AppCompatActivity { 17 | 18 | public static final String EXTRA_MESSAGE = "message"; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_intent_one); 24 | 25 | String message = getIntent().getStringExtra(EXTRA_MESSAGE); 26 | 27 | TextView tvMessage = (TextView)findViewById(R.id.tvMessage); 28 | tvMessage.setText(message); 29 | 30 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); 31 | } 32 | 33 | public void onNextTapped(View view) { 34 | Intent intent = new Intent(this, IntentTwoActivity.class); 35 | intent.putExtra(IntentTwoActivity.EXTRA_MESSAGE, "Second Activity"); 36 | 37 | startActivity(intent); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/IntentTwoActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.widget.TextView; 6 | import android.widget.Toast; 7 | 8 | import com.codepath.testingdemo.R; 9 | 10 | /* 11 | * Take in a "message" as an intent extra. Use the message to set the TextView text. 12 | */ 13 | public class IntentTwoActivity extends AppCompatActivity { 14 | 15 | public static final String EXTRA_MESSAGE = "message"; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_intent_two); 21 | 22 | String message = getIntent().getStringExtra(EXTRA_MESSAGE); 23 | 24 | TextView tvMessage = (TextView)findViewById(R.id.tvMessage); 25 | tvMessage.setText(message); 26 | 27 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/LevelCompleteActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.AsyncTask; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.widget.TextView; 8 | 9 | import com.codepath.testingdemo.R; 10 | import com.codepath.testingdemo.data.GameLevel; 11 | 12 | public class LevelCompleteActivity extends AppCompatActivity { 13 | 14 | public static final String EXTRA_LEVEL_NUMBER = "levelNumber"; 15 | 16 | private static final int PAUSE_TIME_BETWEEN_LEVELS = 4000; // 4 seconds 17 | 18 | int levelNumber; 19 | TextView tvLevelComplete; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_level_complete); 25 | tvLevelComplete = (TextView) findViewById(R.id.tvLevelComplete); 26 | levelNumber = getIntent().getIntExtra(EXTRA_LEVEL_NUMBER, 0); 27 | tvLevelComplete.setText(getResources().getString(R.string.level_complete_message, levelNumber)); 28 | runAsyncTask(); 29 | } 30 | 31 | public void runAsyncTask() { 32 | new AsyncTask() { 33 | protected Void doInBackground(Void... params) { 34 | try { 35 | Thread.sleep(PAUSE_TIME_BETWEEN_LEVELS); 36 | } catch (InterruptedException e) { 37 | e.printStackTrace(); 38 | } 39 | return null; 40 | } 41 | 42 | protected void onPostExecute(Void result) { 43 | // Launch next level 44 | launchNextLevel(levelNumber + 1); 45 | } 46 | }.execute(); 47 | } 48 | 49 | 50 | private void launchNextLevel(int levelNumber) { 51 | if (levelNumber > GameLevel.getNumberOfLevels()) { 52 | return; 53 | } 54 | Class klass = GameLevel.getClassForLevel(levelNumber); 55 | Intent intent = new Intent(this, klass); 56 | intent.putExtra(GameLevelActivity.EXTRA_LEVEL, levelNumber); 57 | startActivity(intent); 58 | finish(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/ListViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | import android.widget.AdapterView; 6 | import android.widget.ListView; 7 | 8 | import com.codepath.testingdemo.R; 9 | import com.codepath.testingdemo.adapters.PostsListViewAdapter; 10 | import com.codepath.testingdemo.data.Data; 11 | import com.codepath.testingdemo.models.Post; 12 | 13 | import java.util.ArrayList; 14 | 15 | public class ListViewActivity extends GameLevelActivity { 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_list_view); 21 | 22 | ArrayList posts = Data.POSTS; 23 | 24 | PostsListViewAdapter adapter = new PostsListViewAdapter(this, posts); 25 | ListView lvPosts = (ListView) findViewById(R.id.lvPosts); 26 | lvPosts.setAdapter(adapter); 27 | lvPosts.setOnItemClickListener(new AdapterView.OnItemClickListener() { 28 | @Override 29 | public void onItemClick(AdapterView parent, View view, int position, long id) { 30 | if (position == 2) { 31 | launchLevelPassedActivity(); 32 | } 33 | } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/LocalizedActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import com.codepath.testingdemo.R; 7 | 8 | /* 9 | * Display a TextView with "Hello World!" in the user's native language (as long as that language 10 | * is English, French, or Spanish). 11 | */ 12 | public class LocalizedActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_localized); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import com.codepath.testingdemo.R; 8 | 9 | /* 10 | * Launch other activities from this activity 11 | */ 12 | public class MainActivity extends GameLevelActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_main); 18 | } 19 | 20 | public void launchAsyncTaskActivity(View view) { 21 | launchActivity(AsyncTaskActivity.class); 22 | } 23 | 24 | public void launchRecyclerViewActivity(View view) { 25 | launchActivity(RecyclerViewActivity.class); 26 | } 27 | 28 | public void launchSearchHistoryActivity(View view) { 29 | launchActivity(SearchHistoryActivity.class); 30 | } 31 | 32 | public void launchLocalizedActivity(View view) { 33 | launchActivity(LocalizedActivity.class); 34 | } 35 | 36 | 37 | private void launchActivity(Class klass) { 38 | Intent intent = new Intent(this, klass); 39 | startActivity(intent); 40 | } 41 | 42 | public void launchFirstActivity(View view) { 43 | Intent intent = new Intent(this, IntentOneActivity.class); 44 | intent.putExtra(IntentOneActivity.EXTRA_MESSAGE, "First Activity"); 45 | startActivity(intent); 46 | } 47 | 48 | public void launchDemo(View view) { 49 | launchActivity(EspressoDemoActivity.class); 50 | } 51 | 52 | public void launchGame(View view) { 53 | launchLevelPassedActivity(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/NetworkRequestActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | import android.widget.Button; 6 | import android.widget.Toast; 7 | 8 | import com.codepath.testingdemo.R; 9 | import com.codepath.testingdemo.data.Data; 10 | import com.codepath.testingdemo.models.Post; 11 | import com.codepath.testingdemo.networking.DemoHttpClient; 12 | 13 | import java.util.List; 14 | 15 | public class NetworkRequestActivity extends GameLevelActivity { 16 | 17 | public DemoHttpClient demoHttpClient; 18 | private Button btnCompleteLevel; 19 | 20 | public void setDemoHttpClient(DemoHttpClient demoHttpClient) { 21 | this.demoHttpClient = demoHttpClient; 22 | } 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_network_request); 28 | btnCompleteLevel = (Button)findViewById(R.id.btnCompleteLevel); 29 | } 30 | 31 | public void onSendNetworkRequestClicked(View view) { 32 | 33 | if (demoHttpClient == null) { 34 | demoHttpClient = new DemoHttpClient(); 35 | } 36 | 37 | demoHttpClient.getAsynchronously("http://www.yahoo.com", new DemoHttpClient.HttpResponseCallback() { 38 | @Override 39 | public void onSuccess(List posts) { 40 | Toast.makeText(NetworkRequestActivity.this, "Network request success!", Toast.LENGTH_SHORT).show(); 41 | btnCompleteLevel.setEnabled(posts != null && posts.size() == Data.POSTS.size()); 42 | } 43 | 44 | @Override 45 | public void onFailure(int httpStatusCode) { 46 | Toast.makeText(NetworkRequestActivity.this, "Network request failed!", Toast.LENGTH_SHORT).show(); 47 | } 48 | }); 49 | } 50 | 51 | public void onCompleteLevelClicked(View view) { 52 | launchLevelPassedActivity(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/PasswordActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.os.Bundle; 4 | import android.text.Editable; 5 | import android.text.TextWatcher; 6 | import android.view.View; 7 | import android.widget.Button; 8 | import android.widget.EditText; 9 | 10 | import com.codepath.testingdemo.R; 11 | 12 | public class PasswordActivity extends GameLevelActivity { 13 | 14 | EditText etPassword; 15 | Button btnCompleteLevel; 16 | 17 | private static final String SUPER_SECRET_PASSWORD = "42"; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_password); 23 | 24 | btnCompleteLevel = (Button)findViewById(R.id.btnCompleteLevel); 25 | 26 | etPassword = (EditText)findViewById(R.id.etPassword); 27 | etPassword.addTextChangedListener(new TextWatcher() { 28 | @Override 29 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 30 | 31 | } 32 | 33 | @Override 34 | public void onTextChanged(CharSequence s, int start, int before, int count) { 35 | 36 | } 37 | 38 | @Override 39 | public void afterTextChanged(Editable s) { 40 | if (SUPER_SECRET_PASSWORD.equals(s.toString())) { 41 | btnCompleteLevel.setEnabled(true); 42 | } 43 | } 44 | }); 45 | } 46 | 47 | public void onCompleteLevelClicked(View view) { 48 | // do something 49 | launchLevelPassedActivity(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/RecyclerViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.View; 7 | 8 | import com.codepath.testingdemo.R; 9 | import com.codepath.testingdemo.adapters.PostsRecyclerViewAdapter; 10 | import com.codepath.testingdemo.data.Data; 11 | import com.codepath.testingdemo.models.Post; 12 | 13 | import java.util.List; 14 | 15 | /* 16 | * Simple RecyclerView that shows a userName / caption in each item 17 | */ 18 | public class RecyclerViewActivity extends GameLevelActivity { 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_recycler_view); 24 | RecyclerView rvPosts = (RecyclerView) findViewById(R.id.rvPosts); 25 | 26 | LinearLayoutManager layoutManager = new LinearLayoutManager(this); 27 | rvPosts.setLayoutManager(layoutManager); 28 | rvPosts.setHasFixedSize(true); 29 | 30 | List posts = Data.POSTS; 31 | 32 | PostsRecyclerViewAdapter adapter = new PostsRecyclerViewAdapter(posts); 33 | 34 | adapter.setOnItemClickListener(new PostsRecyclerViewAdapter.OnItemClickListener() { 35 | @Override 36 | public void onItemClick(View view, int position) { 37 | 38 | if (position == 2) { 39 | launchLevelPassedActivity(); 40 | } 41 | } 42 | }); 43 | rvPosts.setAdapter(adapter); 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/ScrollActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | 6 | import com.codepath.testingdemo.R; 7 | 8 | public class ScrollActivity extends GameLevelActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_scroll); 14 | } 15 | 16 | public void onCompleteLevelClicked(View view) { 17 | // Launch next level 18 | launchLevelPassedActivity(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/SearchHistoryActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.EditText; 8 | import android.widget.TextView; 9 | 10 | import com.codepath.testingdemo.R; 11 | import com.google.common.base.Joiner; 12 | 13 | import java.util.ArrayList; 14 | 15 | /* 16 | * Keep track of recent searches and allow user to share thesee 17 | * with a friend. 18 | */ 19 | public class SearchHistoryActivity extends AppCompatActivity { 20 | 21 | ArrayList previousSearches; 22 | 23 | EditText etSearch; 24 | TextView tvSearchHistory; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_search_history); 30 | 31 | tvSearchHistory = (TextView)findViewById(R.id.tvSearchHistory); 32 | etSearch = (EditText)findViewById(R.id.etSearch); 33 | } 34 | 35 | // User tapped Submit button 36 | public void handleSubmit(View view) { 37 | String searchText = etSearch.getText().toString(); 38 | 39 | // Clear search box 40 | etSearch.setText(""); 41 | 42 | // Add search to search history 43 | tvSearchHistory.append(searchText); 44 | tvSearchHistory.append("\n"); 45 | 46 | // Lazy initialization of previous searches 47 | if (previousSearches == null) { 48 | previousSearches = new ArrayList<>(); 49 | } 50 | 51 | // Record previous searches for sharing purposes 52 | previousSearches.add(searchText); 53 | } 54 | 55 | // User tapped Search History button 56 | public void onShareSearchHistory(View view) { 57 | String searchesToShare = Joiner.on(", ").join(previousSearches); 58 | 59 | Intent sharingIntent = new Intent(Intent.ACTION_SEND); 60 | sharingIntent.setType("text/plain"); 61 | sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, searchesToShare); 62 | 63 | startActivity(Intent.createChooser(sharingIntent, "Share using")); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/activities/ViewPagerActivity.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.activities; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.app.FragmentManager; 6 | import android.support.v4.app.FragmentPagerAdapter; 7 | import android.support.v4.view.ViewPager; 8 | 9 | import com.codepath.testingdemo.R; 10 | import com.codepath.testingdemo.fragments.EmptyFragment; 11 | 12 | public class ViewPagerActivity extends GameLevelActivity implements EmptyFragment.OnAnswerClickedListener { 13 | 14 | 15 | FragmentPagerAdapter adapterViewPager; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_view_pager); 21 | ViewPager vpPager = (ViewPager) findViewById(R.id.vpPager); 22 | adapterViewPager = new PagerAdapter(getSupportFragmentManager()); 23 | vpPager.setAdapter(adapterViewPager); 24 | } 25 | 26 | @Override 27 | public void onAnswerClicked() { 28 | launchLevelPassedActivity(); 29 | } 30 | 31 | 32 | public class PagerAdapter extends FragmentPagerAdapter { 33 | private final int NUM_ITEMS = 3; 34 | 35 | public PagerAdapter(FragmentManager fragmentManager) { 36 | super(fragmentManager); 37 | } 38 | 39 | // Returns total number of pages 40 | @Override 41 | public int getCount() { 42 | return NUM_ITEMS; 43 | } 44 | 45 | // Returns the fragment to display for that page 46 | @Override 47 | public Fragment getItem(int position) { 48 | switch (position) { 49 | case 0: 50 | return EmptyFragment.newInstance( 51 | ViewPagerActivity.this.getResources().getColor(android.R.color.holo_red_light), 52 | false); 53 | case 1: 54 | return EmptyFragment.newInstance( 55 | ViewPagerActivity.this.getResources().getColor(android.R.color.holo_orange_dark), 56 | false); 57 | case 2: 58 | return EmptyFragment.newInstance( 59 | ViewPagerActivity.this.getResources().getColor(android.R.color.holo_blue_bright), 60 | true); 61 | default: 62 | return null; 63 | } 64 | } 65 | 66 | // Returns the page title for the top indicator 67 | @Override 68 | public CharSequence getPageTitle(int position) { 69 | return "Page " + position; 70 | } 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/adapters/PostsListViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.adapters; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ArrayAdapter; 8 | import android.widget.TextView; 9 | 10 | import com.codepath.testingdemo.R; 11 | import com.codepath.testingdemo.models.Post; 12 | 13 | import java.util.ArrayList; 14 | 15 | public class PostsListViewAdapter extends ArrayAdapter { 16 | public PostsListViewAdapter(Context context, ArrayList posts) { 17 | super(context, 0, posts); 18 | } 19 | 20 | @Override 21 | public View getView(int position, View convertView, ViewGroup parent) { 22 | Post post = getItem(position); 23 | 24 | ViewHolder viewHolder; 25 | if (convertView == null) { 26 | viewHolder = new ViewHolder(); 27 | 28 | convertView = LayoutInflater.from(getContext()).inflate(R.layout.layout_item_post, parent, false); 29 | viewHolder.tvCaption = (TextView)convertView.findViewById(R.id.tvCaption); 30 | convertView.setTag(viewHolder); 31 | } else { 32 | viewHolder = (ViewHolder)convertView.getTag(); 33 | } 34 | 35 | viewHolder.tvCaption.setText(String.format("%s: %s", post.userName, post.caption)); 36 | 37 | return convertView; 38 | } 39 | 40 | private static class ViewHolder { 41 | TextView tvCaption; 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/adapters/PostsRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.adapters; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.text.TextUtils; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import com.codepath.testingdemo.R; 11 | import com.codepath.testingdemo.models.Post; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /* 17 | * Simple Adapter that populates a single textView that shows a userName / caption in each item 18 | */ 19 | public class PostsRecyclerViewAdapter extends RecyclerView.Adapter { 20 | private static final String TAG = "InstagramPostsAdapter"; 21 | 22 | private List posts; 23 | 24 | private OnItemClickListener listener; 25 | 26 | 27 | public PostsRecyclerViewAdapter(List posts) { 28 | this.posts = (posts == null ? new ArrayList() : posts); 29 | } 30 | 31 | @Override 32 | public PostItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) { 33 | View itemView = LayoutInflater.from(viewGroup.getContext()) 34 | .inflate(R.layout.layout_item_post, viewGroup, false); 35 | 36 | return new PostItemViewHolder(itemView); 37 | } 38 | 39 | @Override 40 | public void onBindViewHolder(PostItemViewHolder postItemViewHolder, int position) { 41 | final Post post = posts.get(position); 42 | 43 | if (!TextUtils.isEmpty(post.caption)) { 44 | postItemViewHolder.tvCaption.setText( 45 | String.format("%s: %s", post.userName, post.caption)); 46 | } else { 47 | postItemViewHolder.tvCaption.setVisibility(View.GONE); 48 | } 49 | } 50 | 51 | @Override 52 | public int getItemCount() { 53 | return posts == null ? 0 : posts.size(); 54 | } 55 | 56 | public final class PostItemViewHolder extends RecyclerView.ViewHolder { 57 | TextView tvCaption; 58 | 59 | public PostItemViewHolder(final View itemView) { 60 | super(itemView); 61 | 62 | tvCaption = (TextView) itemView.findViewById(R.id.tvCaption); 63 | itemView.setOnClickListener(new View.OnClickListener() { 64 | @Override 65 | public void onClick(View v) { 66 | // Triggers click upwards to the adapter on click 67 | if (listener != null) 68 | listener.onItemClick(itemView, PostItemViewHolder.this.getPosition()); 69 | } 70 | }); 71 | } 72 | } 73 | 74 | public interface OnItemClickListener { 75 | void onItemClick(View itemView, int position); 76 | } 77 | 78 | public void setOnItemClickListener(OnItemClickListener listener) { 79 | this.listener = listener; 80 | } 81 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/data/Data.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.data; 2 | 3 | import com.codepath.testingdemo.models.Post; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | 8 | public class Data { 9 | public static final ArrayList POSTS = new ArrayList<>(Arrays.asList( 10 | new Post("Andre", "Yes!"), 11 | new Post("Klay", "We did it!!!"), 12 | new Post("Steph", "We Won!!!"), 13 | new Post("Lebron", "No comment"), 14 | new Post("Harrison", "Haha") 15 | )); 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/data/GameLevel.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.data; 2 | 3 | import com.codepath.testingdemo.activities.CameraActivity; 4 | import com.codepath.testingdemo.activities.CustomNetworkRequestActivity; 5 | import com.codepath.testingdemo.activities.DateTimePickerActivity; 6 | import com.codepath.testingdemo.activities.ListViewActivity; 7 | import com.codepath.testingdemo.activities.MainActivity; 8 | import com.codepath.testingdemo.activities.NetworkRequestActivity; 9 | import com.codepath.testingdemo.activities.PasswordActivity; 10 | import com.codepath.testingdemo.activities.RecyclerViewActivity; 11 | import com.codepath.testingdemo.activities.ScrollActivity; 12 | import com.codepath.testingdemo.activities.ViewPagerActivity; 13 | import com.google.common.collect.ImmutableMap; 14 | 15 | import java.util.Map; 16 | 17 | public class GameLevel { 18 | 19 | static final Map LEVELS = ImmutableMap.builder() 20 | .put(1, MainActivity.class) 21 | .put(2, PasswordActivity.class) 22 | .put(3, ScrollActivity.class) 23 | .put(4, ViewPagerActivity.class) 24 | .put(5, DateTimePickerActivity.class) 25 | .put(6, ListViewActivity.class) 26 | .put(7, RecyclerViewActivity.class) 27 | .put(8, CameraActivity.class) 28 | .put(9, NetworkRequestActivity.class) 29 | .put(10, CustomNetworkRequestActivity.class) 30 | .build(); 31 | 32 | public static Class getClassForLevel(int levelNumber) { 33 | return LEVELS.get(levelNumber); 34 | } 35 | public static int getNumberOfLevels() { 36 | return LEVELS.size(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/fragments/DatePickerFragment.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.fragments; 2 | 3 | import android.app.Activity; 4 | import android.app.DatePickerDialog; 5 | import android.app.Dialog; 6 | import android.os.Bundle; 7 | import android.support.annotation.NonNull; 8 | import android.support.v4.app.DialogFragment; 9 | import android.widget.DatePicker; 10 | 11 | import java.util.Calendar; 12 | 13 | public class DatePickerFragment extends DialogFragment 14 | implements DatePickerDialog.OnDateSetListener { 15 | 16 | private OnDateSetListener mListener; 17 | 18 | @Override 19 | @NonNull 20 | public Dialog onCreateDialog(Bundle savedInstanceState) { 21 | // Use the current date as the default date in the picker 22 | final Calendar c = Calendar.getInstance(); 23 | int year = c.get(Calendar.YEAR); 24 | int month = c.get(Calendar.MONTH); 25 | int day = c.get(Calendar.DAY_OF_MONTH); 26 | 27 | // Create a new instance of DatePickerDialog and return it 28 | return new DatePickerDialog(getActivity(), this, year, month, day); 29 | } 30 | 31 | public void onDateSet(DatePicker view, int year, int month, int day) { 32 | if (mListener != null) { 33 | mListener.onDateSet(year, month, day); 34 | } 35 | } 36 | 37 | @Override 38 | public void onAttach(Activity activity) { 39 | super.onAttach(activity); 40 | try { 41 | mListener = (OnDateSetListener) activity; 42 | } catch (ClassCastException e) { 43 | throw new ClassCastException(activity.toString() 44 | + " must implement OnFragmentInteractionListener"); 45 | } 46 | } 47 | 48 | @Override 49 | public void onDetach() { 50 | super.onDetach(); 51 | mListener = null; 52 | } 53 | 54 | public interface OnDateSetListener { 55 | void onDateSet(int year, int month, int day); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/fragments/EmptyFragment.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.fragments; 2 | 3 | 4 | import android.app.Activity; 5 | import android.os.Bundle; 6 | import android.support.annotation.IdRes; 7 | import android.support.v4.app.Fragment; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.Button; 12 | 13 | import com.codepath.testingdemo.R; 14 | 15 | public class EmptyFragment extends Fragment { 16 | private static final String ARG_BACKGROUND_COLOR = "backgroundColor"; 17 | private static final String ARG_IS_ANSWER_FRAGMENT = "isAnswerFragment"; 18 | private int colorResourceId; 19 | private boolean isAnswerFragment; 20 | Button btnCompleteLevel; 21 | private OnAnswerClickedListener mListener; 22 | 23 | 24 | public static EmptyFragment newInstance(@IdRes int colorResourceId, boolean isAnswerFragment) { 25 | EmptyFragment fragment = new EmptyFragment(); 26 | Bundle args = new Bundle(); 27 | args.putInt(ARG_BACKGROUND_COLOR, colorResourceId); 28 | args.putBoolean(ARG_IS_ANSWER_FRAGMENT, isAnswerFragment); 29 | fragment.setArguments(args); 30 | return fragment; 31 | } 32 | 33 | public EmptyFragment() { 34 | // Required empty public constructor 35 | } 36 | 37 | @Override 38 | public void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | if (getArguments() != null) { 41 | colorResourceId = getArguments().getInt(ARG_BACKGROUND_COLOR); 42 | isAnswerFragment = getArguments().getBoolean(ARG_IS_ANSWER_FRAGMENT); 43 | } 44 | } 45 | 46 | @Override 47 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 48 | Bundle savedInstanceState) { 49 | View view = inflater.inflate(R.layout.fragment_empty, container, false); 50 | view.setBackgroundColor(this.colorResourceId); 51 | btnCompleteLevel = (Button)view.findViewById(R.id.btnCompleteLevel); 52 | btnCompleteLevel.setVisibility(isAnswerFragment ? View.VISIBLE : View.GONE); 53 | btnCompleteLevel.setOnClickListener(new View.OnClickListener() { 54 | @Override 55 | public void onClick(View v) { 56 | if (mListener != null) { 57 | mListener.onAnswerClicked(); 58 | } 59 | 60 | } 61 | }); 62 | return view; 63 | } 64 | 65 | @Override 66 | public void onAttach(Activity activity) { 67 | super.onAttach(activity); 68 | try { 69 | mListener = (OnAnswerClickedListener) activity; 70 | } catch (ClassCastException e) { 71 | throw new ClassCastException(activity.toString() 72 | + " must implement OnFragmentInteractionListener"); 73 | } 74 | } 75 | 76 | @Override 77 | public void onDetach() { 78 | super.onDetach(); 79 | mListener = null; 80 | } 81 | 82 | public interface OnAnswerClickedListener { 83 | void onAnswerClicked(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/fragments/TimePickerFragment.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.fragments; 2 | 3 | 4 | import android.app.Activity; 5 | import android.app.Dialog; 6 | import android.app.TimePickerDialog; 7 | import android.os.Bundle; 8 | import android.support.annotation.NonNull; 9 | import android.support.v4.app.DialogFragment; 10 | import android.widget.TimePicker; 11 | 12 | import java.util.Calendar; 13 | 14 | public class TimePickerFragment extends DialogFragment 15 | implements TimePickerDialog.OnTimeSetListener { 16 | 17 | private OnTimeSetListener mListener; 18 | 19 | @Override 20 | @NonNull 21 | public Dialog onCreateDialog(Bundle savedInstanceState) { 22 | // Use the current time as the default values for the picker 23 | final Calendar c = Calendar.getInstance(); 24 | int hour = c.get(Calendar.HOUR_OF_DAY); 25 | int minute = c.get(Calendar.MINUTE); 26 | 27 | // Create a new instance of TimePickerDialog and return it 28 | return new TimePickerDialog(getActivity(), this, hour, minute, true); 29 | } 30 | 31 | public void onTimeSet(TimePicker view, int hourOfDay, int minute) { 32 | if (mListener != null) { 33 | mListener.onTimeSet(hourOfDay, minute); 34 | } 35 | } 36 | 37 | @Override 38 | public void onAttach(Activity activity) { 39 | super.onAttach(activity); 40 | try { 41 | mListener = (OnTimeSetListener) activity; 42 | } catch (ClassCastException e) { 43 | throw new ClassCastException(activity.toString() 44 | + " must implement OnFragmentInteractionListener"); 45 | } 46 | } 47 | 48 | @Override 49 | public void onDetach() { 50 | super.onDetach(); 51 | mListener = null; 52 | } 53 | 54 | public interface OnTimeSetListener { 55 | void onTimeSet(int hour, int minute); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/models/Post.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.models; 2 | 3 | import com.google.common.base.Joiner; 4 | 5 | import java.io.Serializable; 6 | import java.text.NumberFormat; 7 | import java.util.List; 8 | 9 | public class Post implements Serializable { 10 | 11 | public String userName; 12 | public String caption; 13 | 14 | public Post() { 15 | // do nothing 16 | } 17 | 18 | public Post(String userName, String caption) { 19 | this.userName = userName; 20 | this.caption = caption; 21 | } 22 | 23 | // Names of people that have liked this post 24 | public List likers; 25 | 26 | /** 27 | * Returns a string representation of the likers 28 | * that can be used for display. 29 | * 30 | * Rules: 31 | * 0 likers => Empty string 32 | * 1 - 5 likers => Comma separated userNames (i.e. Bob, Nancy, Joe) 33 | * > 5 likers => Comma separated number plus numericSuffix (i.e. 1,234 likes) 34 | */ 35 | public String formatLikersForDisplay(String numericSuffix) { 36 | String result; 37 | 38 | if (likers == null || likers.isEmpty()) { 39 | result = ""; 40 | } else if (likers.size() <= 5) { 41 | result = Joiner.on(", ").join(likers); 42 | } else { 43 | if (numericSuffix == null) { 44 | throw new IllegalArgumentException("No numericSuffix specified for likers format string"); 45 | } 46 | NumberFormat numberFormat = NumberFormat.getInstance(); 47 | numberFormat.setGroupingUsed(true); 48 | result = String.format("%s %s", numberFormat.format(likers.size()), numericSuffix); 49 | } 50 | return result; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return String.format("User: %s, caption: %s", userName, caption); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/models/User.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.models; 2 | 3 | public class User { 4 | private String userName; 5 | 6 | public User() { 7 | // do nothing 8 | } 9 | 10 | public User(String userName) { 11 | this.userName = userName; 12 | } 13 | 14 | public String getUserName() { 15 | return userName; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/codepath/testingdemo/networking/DemoHttpClient.java: -------------------------------------------------------------------------------- 1 | package com.codepath.testingdemo.networking; 2 | 3 | import android.os.AsyncTask; 4 | 5 | import com.codepath.testingdemo.models.Post; 6 | 7 | import java.util.List; 8 | 9 | public class DemoHttpClient { 10 | 11 | public interface HttpResponseCallback { 12 | void onSuccess(List posts); 13 | void onFailure(int httpStatusCode); 14 | } 15 | 16 | public void getAsynchronously(String url, final HttpResponseCallback callback) { 17 | new AsyncTask() { 18 | protected Void doInBackground(Void... params) { 19 | try { 20 | Thread.sleep(5000); // 5 seconds 21 | } catch (InterruptedException e) { 22 | e.printStackTrace(); 23 | } 24 | return null; 25 | } 26 | 27 | protected void onPostExecute(Void result) { 28 | callback.onFailure(404); // Not Found 29 | } 30 | }.execute(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_async_task.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |