1/* 2 * Copyright (C) 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 */ 16package android.support.design.testutils; 17 18import android.support.annotation.Nullable; 19import android.support.design.widget.Snackbar; 20import android.support.test.espresso.Espresso; 21import android.support.test.espresso.IdlingResource; 22import android.support.test.espresso.UiController; 23import android.support.test.espresso.ViewAction; 24import android.view.View; 25import org.hamcrest.Matcher; 26 27import static android.support.design.testutils.TestUtilsActions.waitUntilIdle; 28import static android.support.test.espresso.Espresso.onView; 29import static android.support.test.espresso.matcher.ViewMatchers.isRoot; 30 31public class SnackbarUtils { 32 private static class SnackbarShownCallback extends Snackbar.Callback 33 implements IdlingResource { 34 private boolean mIsShown = false; 35 36 @Nullable 37 private IdlingResource.ResourceCallback mCallback; 38 39 private boolean mNeedsIdle = false; 40 41 @Override 42 public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { 43 mCallback = resourceCallback; 44 } 45 46 @Override 47 public String getName() { 48 return "Snackbar shown callback"; 49 } 50 51 @Override 52 public boolean isIdleNow() { 53 if (!mNeedsIdle) { 54 return true; 55 } else { 56 return mIsShown; 57 } 58 } 59 60 @Override 61 public void onShown(Snackbar snackbar) { 62 mIsShown = true; 63 if (mCallback != null) { 64 mCallback.onTransitionToIdle(); 65 } 66 } 67 } 68 69 private static class SnackbarDismissedCallback extends Snackbar.Callback 70 implements IdlingResource { 71 private boolean mIsDismissed = false; 72 73 @Nullable 74 private IdlingResource.ResourceCallback mCallback; 75 76 private boolean mNeedsIdle = false; 77 78 @Override 79 public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { 80 mCallback = resourceCallback; 81 } 82 83 @Override 84 public String getName() { 85 return "Snackbar dismissed callback"; 86 } 87 88 @Override 89 public boolean isIdleNow() { 90 if (!mNeedsIdle) { 91 return true; 92 } else { 93 return mIsDismissed; 94 } 95 } 96 97 @Override 98 public void onDismissed(Snackbar snackbar, @DismissEvent int event) { 99 mIsDismissed = true; 100 if (mCallback != null) { 101 mCallback.onTransitionToIdle(); 102 } 103 } 104 } 105 106 /** 107 * Helper method that shows that specified {@link Snackbar} and waits until 108 * it has been fully shown. Note that calling this method will reset the currently 109 * set {@link Snackbar.Callback}. 110 */ 111 public static void showSnackbarAndWaitUntilFullyShown(Snackbar snackbar) { 112 SnackbarShownCallback snackbarCallback = new SnackbarShownCallback(); 113 snackbar.setCallback(snackbarCallback); 114 try { 115 // Register our listener as idling resource so that Espresso waits until the 116 // the snackbar has been fully shown 117 Espresso.registerIdlingResources(snackbarCallback); 118 // Show the snackbar 119 snackbar.show(); 120 // Mark the callback to require waiting for idle state 121 snackbarCallback.mNeedsIdle = true; 122 // Perform a dummy Espresso action that loops until the UI thread is idle. This 123 // effectively blocks us until the Snackbar has completed its sliding animation. 124 onView(isRoot()).perform(waitUntilIdle()); 125 snackbarCallback.mNeedsIdle = false; 126 } finally { 127 // Unregister our idling resource 128 Espresso.unregisterIdlingResources(snackbarCallback); 129 // And remove our tracker listener from Snackbar 130 snackbar.setCallback(null); 131 } 132 } 133 134 /** 135 * Helper method that dismissed that specified {@link Snackbar} and waits until 136 * it has been fully dismissed. Note that calling this method will reset the currently 137 * set {@link Snackbar.Callback}. 138 */ 139 public static void dismissSnackbarAndWaitUntilFullyDismissed(Snackbar snackbar) { 140 SnackbarDismissedCallback snackbarCallback = new SnackbarDismissedCallback(); 141 snackbar.setCallback(snackbarCallback); 142 try { 143 // Register our listener as idling resource so that Espresso waits until the 144 // the snackbar has been fully dismissed 145 Espresso.registerIdlingResources(snackbarCallback); 146 // Dismiss the snackbar 147 snackbar.dismiss(); 148 // Mark the callback to require waiting for idle state 149 snackbarCallback.mNeedsIdle = true; 150 // Perform a dummy Espresso action that loops until the UI thread is idle. This 151 // effectively blocks us until the Snackbar has completed its sliding animation. 152 onView(isRoot()).perform(waitUntilIdle()); 153 snackbarCallback.mNeedsIdle = false; 154 } finally { 155 // Unregister our idling resource 156 Espresso.unregisterIdlingResources(snackbarCallback); 157 // And remove our tracker listener from Snackbar 158 snackbar.setCallback(null); 159 } 160 } 161 162 /** 163 * Helper method that waits until the given Snackbar has been fully dismissed. Note that 164 * calling this method will reset the currently set {@link Snackbar.Callback}. 165 */ 166 public static void waitUntilFullyDismissed(Snackbar snackbar) { 167 SnackbarDismissedCallback snackbarCallback = new SnackbarDismissedCallback(); 168 snackbar.setCallback(snackbarCallback); 169 try { 170 // Register our listener as idling resource so that Espresso waits until the 171 // the snackbar has been fully dismissed 172 Espresso.registerIdlingResources(snackbarCallback); 173 // Mark the callback to require waiting for idle state 174 snackbarCallback.mNeedsIdle = true; 175 // Perform a dummy Espresso action that loops until the UI thread is idle. This 176 // effectively blocks us until the Snackbar has completed its sliding animation. 177 onView(isRoot()).perform(waitUntilIdle()); 178 snackbarCallback.mNeedsIdle = false; 179 } finally { 180 // Unregister our idling resource 181 Espresso.unregisterIdlingResources(snackbarCallback); 182 // And remove our tracker listener from Snackbar 183 snackbar.setCallback(null); 184 } 185 } 186} 187