15ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov/* 25ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * Copyright (C) 2016 The Android Open Source Project 35ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * 45ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * Licensed under the Apache License, Version 2.0 (the "License"); 55ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * you may not use this file except in compliance with the License. 65ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * You may obtain a copy of the License at 75ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * 85ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * http://www.apache.org/licenses/LICENSE-2.0 95ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * 105ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * Unless required by applicable law or agreed to in writing, software 115ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * distributed under the License is distributed on an "AS IS" BASIS, 125ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * See the License for the specific language governing permissions and 145ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov * limitations under the License. 155ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov */ 165ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikovpackage android.support.design.testutils; 175ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov 185ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikovimport android.support.annotation.Nullable; 195ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikovimport android.support.design.widget.Snackbar; 205ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikovimport android.support.test.espresso.Espresso; 215ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikovimport android.support.test.espresso.IdlingResource; 225ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikovimport android.support.test.espresso.UiController; 235ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikovimport android.support.test.espresso.ViewAction; 245ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikovimport android.view.View; 255ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikovimport org.hamcrest.Matcher; 265ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov 27c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikovimport static android.support.design.testutils.TestUtilsActions.waitUntilIdle; 285ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikovimport static android.support.test.espresso.Espresso.onView; 295ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikovimport static android.support.test.espresso.matcher.ViewMatchers.isRoot; 305ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov 315ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikovpublic class SnackbarUtils { 323a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov private static class SnackbarShownCallback extends Snackbar.Callback 335ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov implements IdlingResource { 345ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov private boolean mIsShown = false; 355ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov 365ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov @Nullable 375ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov private IdlingResource.ResourceCallback mCallback; 385ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov 395ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov private boolean mNeedsIdle = false; 405ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov 415ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov @Override 425ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { 435ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov mCallback = resourceCallback; 445ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov } 455ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov 465ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov @Override 475ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov public String getName() { 483a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov return "Snackbar shown callback"; 495ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov } 505ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov 515ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov @Override 525ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov public boolean isIdleNow() { 535ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov if (!mNeedsIdle) { 545ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov return true; 555ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov } else { 565ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov return mIsShown; 575ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov } 585ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov } 595ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov 605ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov @Override 615ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov public void onShown(Snackbar snackbar) { 625ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov mIsShown = true; 635ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov if (mCallback != null) { 645ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov mCallback.onTransitionToIdle(); 655ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov } 665ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov } 675ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov } 685ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov 693a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov private static class SnackbarDismissedCallback extends Snackbar.Callback 703a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov implements IdlingResource { 713a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov private boolean mIsDismissed = false; 723a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov 733a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov @Nullable 743a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov private IdlingResource.ResourceCallback mCallback; 753a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov 763a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov private boolean mNeedsIdle = false; 773a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov 783a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov @Override 793a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { 803a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov mCallback = resourceCallback; 813a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov } 823a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov 833a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov @Override 843a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov public String getName() { 853a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov return "Snackbar dismissed callback"; 863a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov } 873a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov 883a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov @Override 893a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov public boolean isIdleNow() { 903a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov if (!mNeedsIdle) { 913a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov return true; 923a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov } else { 933a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov return mIsDismissed; 943a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov } 953a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov } 963a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov 973a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov @Override 983a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov public void onDismissed(Snackbar snackbar, @DismissEvent int event) { 993a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov mIsDismissed = true; 1003a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov if (mCallback != null) { 1013a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov mCallback.onTransitionToIdle(); 1023a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov } 1033a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov } 1043a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov } 1053a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov 1065ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov /** 1073a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov * Helper method that shows that specified {@link Snackbar} and waits until 1083a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov * it has been fully shown. Note that calling this method will reset the currently 1093a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov * set {@link Snackbar.Callback}. 1103a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov */ 1115ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov public static void showSnackbarAndWaitUntilFullyShown(Snackbar snackbar) { 1123a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov SnackbarShownCallback snackbarCallback = new SnackbarShownCallback(); 1135ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov snackbar.setCallback(snackbarCallback); 1145ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov try { 1155ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov // Register our listener as idling resource so that Espresso waits until the 1163a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov // the snackbar has been fully shown 1175ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov Espresso.registerIdlingResources(snackbarCallback); 1185ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov // Show the snackbar 1195ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov snackbar.show(); 1205ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov // Mark the callback to require waiting for idle state 1215ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov snackbarCallback.mNeedsIdle = true; 1225ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov // Perform a dummy Espresso action that loops until the UI thread is idle. This 1235ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov // effectively blocks us until the Snackbar has completed its sliding animation. 1245ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov onView(isRoot()).perform(waitUntilIdle()); 1255ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov snackbarCallback.mNeedsIdle = false; 1265ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov } finally { 1275ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov // Unregister our idling resource 1285ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov Espresso.unregisterIdlingResources(snackbarCallback); 1295ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov // And remove our tracker listener from Snackbar 1305ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov snackbar.setCallback(null); 1315ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov } 1325ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov } 1333a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov 1343a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov /** 1353a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov * Helper method that dismissed that specified {@link Snackbar} and waits until 1363a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov * it has been fully dismissed. Note that calling this method will reset the currently 1373a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov * set {@link Snackbar.Callback}. 1383a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov */ 1393a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov public static void dismissSnackbarAndWaitUntilFullyDismissed(Snackbar snackbar) { 1403a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov SnackbarDismissedCallback snackbarCallback = new SnackbarDismissedCallback(); 1413a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov snackbar.setCallback(snackbarCallback); 1423a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov try { 1433a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov // Register our listener as idling resource so that Espresso waits until the 1443a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov // the snackbar has been fully dismissed 1453a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov Espresso.registerIdlingResources(snackbarCallback); 1463a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov // Dismiss the snackbar 1473a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov snackbar.dismiss(); 1483a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov // Mark the callback to require waiting for idle state 1493a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov snackbarCallback.mNeedsIdle = true; 1503a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov // Perform a dummy Espresso action that loops until the UI thread is idle. This 1513a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov // effectively blocks us until the Snackbar has completed its sliding animation. 1523a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov onView(isRoot()).perform(waitUntilIdle()); 1533a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov snackbarCallback.mNeedsIdle = false; 1543a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov } finally { 1553a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov // Unregister our idling resource 1563a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov Espresso.unregisterIdlingResources(snackbarCallback); 1573a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov // And remove our tracker listener from Snackbar 1583a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov snackbar.setCallback(null); 1593a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov } 1603a8340d4a090bc8ab7ad3a774fb6d999e7384301Kirill Grouchnikov } 161b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes 162b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes /** 163b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes * Helper method that waits until the given Snackbar has been fully dismissed. Note that 164b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes * calling this method will reset the currently set {@link Snackbar.Callback}. 165b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes */ 166b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes public static void waitUntilFullyDismissed(Snackbar snackbar) { 167b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes SnackbarDismissedCallback snackbarCallback = new SnackbarDismissedCallback(); 168b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes snackbar.setCallback(snackbarCallback); 169b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes try { 170b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes // Register our listener as idling resource so that Espresso waits until the 171b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes // the snackbar has been fully dismissed 172b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes Espresso.registerIdlingResources(snackbarCallback); 173b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes // Mark the callback to require waiting for idle state 174b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes snackbarCallback.mNeedsIdle = true; 175b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes // Perform a dummy Espresso action that loops until the UI thread is idle. This 176b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes // effectively blocks us until the Snackbar has completed its sliding animation. 177b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes onView(isRoot()).perform(waitUntilIdle()); 178b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes snackbarCallback.mNeedsIdle = false; 179b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes } finally { 180b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes // Unregister our idling resource 181b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes Espresso.unregisterIdlingResources(snackbarCallback); 182b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes // And remove our tracker listener from Snackbar 183b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes snackbar.setCallback(null); 184b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes } 185b6b4a30c2832e00e4d471665471748f0cb8143bcChris Banes } 1865ee7b3de4f2bbecd3eb7e346809250d9ca806a9eKirill Grouchnikov} 187