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