/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.design.testutils; import static android.support.design.testutils.TestUtilsActions.waitUntilIdle; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.matcher.ViewMatchers.isRoot; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.BaseTransientBottomBar; import android.support.design.widget.Snackbar; import android.support.test.espresso.Espresso; import android.support.test.espresso.IdlingResource; public class SnackbarUtils { public interface TransientBottomBarAction { void perform() throws Throwable; } private static class TransientBottomBarShownCallback extends BaseTransientBottomBar.BaseCallback implements IdlingResource { private boolean mIsShown = false; @Nullable private IdlingResource.ResourceCallback mCallback; private boolean mNeedsIdle = false; @Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { mCallback = resourceCallback; } @Override public String getName() { return "Transient bottom bar shown callback"; } @Override public boolean isIdleNow() { if (!mNeedsIdle) { return true; } else { return mIsShown; } } @Override public void onShown(BaseTransientBottomBar transientBottomBar) { mIsShown = true; if (mCallback != null) { mCallback.onTransitionToIdle(); } } } private static class TransientBottomBarDismissedCallback extends BaseTransientBottomBar.BaseCallback implements IdlingResource { private boolean mIsDismissed = false; @Nullable private IdlingResource.ResourceCallback mCallback; private boolean mNeedsIdle = false; @Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { mCallback = resourceCallback; } @Override public String getName() { return "Transient bottom bar dismissed callback"; } @Override public boolean isIdleNow() { if (!mNeedsIdle) { return true; } else { return mIsDismissed; } } @Override public void onDismissed(BaseTransientBottomBar transientBottomBar, @DismissEvent int event) { mIsDismissed = true; if (mCallback != null) { mCallback.onTransitionToIdle(); } } } /** * Helper method that shows that specified {@link Snackbar} and waits until * it has been fully shown. */ public static void showTransientBottomBarAndWaitUntilFullyShown( @NonNull BaseTransientBottomBar transientBottomBar) { TransientBottomBarShownCallback callback = new TransientBottomBarShownCallback(); transientBottomBar.addCallback(callback); try { // Register our listener as idling resource so that Espresso waits until the // the bar has been fully shown Espresso.registerIdlingResources(callback); // Show the bar transientBottomBar.show(); // Mark the callback to require waiting for idle state callback.mNeedsIdle = true; // Perform a dummy Espresso action that loops until the UI thread is idle. This // effectively blocks us until the Snackbar has completed its sliding animation. onView(isRoot()).perform(waitUntilIdle()); callback.mNeedsIdle = false; } finally { // Unregister our idling resource Espresso.unregisterIdlingResources(callback); // And remove our tracker listener from Snackbar transientBottomBar.removeCallback(callback); } } /** * Helper method that dismissed that specified {@link Snackbar} and waits until * it has been fully dismissed. */ public static void dismissTransientBottomBarAndWaitUntilFullyDismissed( @NonNull final BaseTransientBottomBar transientBottomBar) throws Throwable { performActionAndWaitUntilFullyDismissed(transientBottomBar, new TransientBottomBarAction() { @Override public void perform() throws Throwable { transientBottomBar.dismiss(); } }); } /** * Helper method that dismissed that specified {@link Snackbar} and waits until * it has been fully dismissed. */ public static void performActionAndWaitUntilFullyDismissed( @NonNull BaseTransientBottomBar transientBottomBar, @NonNull TransientBottomBarAction action) throws Throwable { TransientBottomBarDismissedCallback callback = new TransientBottomBarDismissedCallback(); transientBottomBar.addCallback(callback); try { // Register our listener as idling resource so that Espresso waits until the // the bar has been fully dismissed Espresso.registerIdlingResources(callback); // Run the action action.perform(); // Mark the callback to require waiting for idle state callback.mNeedsIdle = true; // Perform a dummy Espresso action that loops until the UI thread is idle. This // effectively blocks us until the Snackbar has completed its sliding animation. onView(isRoot()).perform(waitUntilIdle()); callback.mNeedsIdle = false; } finally { // Unregister our idling resource Espresso.unregisterIdlingResources(callback); // And remove our tracker listener from Snackbar transientBottomBar.removeCallback(null); } } /** * Helper method that waits until the given bar has been fully dismissed. */ public static void waitUntilFullyDismissed(@NonNull BaseTransientBottomBar transientBottomBar) { TransientBottomBarDismissedCallback callback = new TransientBottomBarDismissedCallback(); transientBottomBar.addCallback(callback); try { // Register our listener as idling resource so that Espresso waits until the // the bar has been fully dismissed Espresso.registerIdlingResources(callback); // Mark the callback to require waiting for idle state callback.mNeedsIdle = true; // Perform a dummy Espresso action that loops until the UI thread is idle. This // effectively blocks us until the Snackbar has completed its sliding animation. onView(isRoot()).perform(waitUntilIdle()); callback.mNeedsIdle = false; } finally { // Unregister our idling resource Espresso.unregisterIdlingResources(callback); // And remove our tracker listener from Snackbar transientBottomBar.removeCallback(null); } } }