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