/* * Copyright (C) 2015 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.v7.testutils; import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; import android.support.annotation.Nullable; import android.support.test.espresso.Espresso; import android.support.test.espresso.IdlingResource; import android.support.test.espresso.UiController; import android.support.test.espresso.ViewAction; import android.support.v4.widget.DrawerLayout; import android.view.View; import org.hamcrest.Matcher; public class DrawerLayoutActions { /** * Drawer listener that serves as Espresso's {@link IdlingResource} and notifies the registered * callback when the drawer gets to STATE_IDLE state. */ private static class CustomDrawerListener implements DrawerLayout.DrawerListener, IdlingResource { private int mCurrState = DrawerLayout.STATE_IDLE; @Nullable private IdlingResource.ResourceCallback mCallback; private boolean mNeedsIdle = false; @Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { mCallback = resourceCallback; } @Override public String getName() { return "Drawer listener"; } @Override public boolean isIdleNow() { if (!mNeedsIdle) { return true; } else { return mCurrState == DrawerLayout.STATE_IDLE; } } @Override public void onDrawerClosed(View drawer) { if (mCurrState == DrawerLayout.STATE_IDLE) { if (mCallback != null) { mCallback.onTransitionToIdle(); } } } @Override public void onDrawerOpened(View drawer) { if (mCurrState == DrawerLayout.STATE_IDLE) { if (mCallback != null) { mCallback.onTransitionToIdle(); } } } @Override public void onDrawerSlide(View drawer, float slideOffset) { } @Override public void onDrawerStateChanged(int state) { mCurrState = state; if (state == DrawerLayout.STATE_IDLE) { if (mCallback != null) { mCallback.onTransitionToIdle(); } } } } private abstract static class WrappedViewAction implements ViewAction { } public static ViewAction wrap(final ViewAction baseAction) { if (baseAction instanceof WrappedViewAction) { throw new IllegalArgumentException("Don't wrap and already wrapped action"); } return new WrappedViewAction() { @Override public Matcher getConstraints() { return baseAction.getConstraints(); } @Override public String getDescription() { return baseAction.getDescription(); } @Override public final void perform(UiController uiController, View view) { final DrawerLayout drawer = (DrawerLayout) view; // Add a custom tracker listener final CustomDrawerListener customListener = new CustomDrawerListener(); drawer.addDrawerListener(customListener); // Note that we're running the following block in a try-finally construct. This // is needed since some of the wrapped actions are going to throw (expected) // exceptions. If that happens, we still need to clean up after ourselves to // leave the system (Espesso) in a good state. try { // Register our listener as idling resource so that Espresso waits until the // wrapped action results in the drawer getting to the STATE_IDLE state Espresso.registerIdlingResources(customListener); baseAction.perform(uiController, view); customListener.mNeedsIdle = true; uiController.loopMainThreadUntilIdle(); customListener.mNeedsIdle = false; } finally { // Unregister our idling resource Espresso.unregisterIdlingResources(customListener); // And remove our tracker listener from DrawerLayout drawer.removeDrawerListener(customListener); } } }; } /** * Opens the drawer at the specified edge gravity. */ public static ViewAction openDrawer(final int drawerEdgeGravity, final boolean animate) { return wrap(new ViewAction() { @Override public Matcher getConstraints() { return isAssignableFrom(DrawerLayout.class); } @Override public String getDescription() { return "Opens the drawer"; } @Override public void perform(UiController uiController, View view) { uiController.loopMainThreadUntilIdle(); DrawerLayout drawerLayout = (DrawerLayout) view; drawerLayout.openDrawer(drawerEdgeGravity, animate); } }); } /** * Opens the drawer at the specified edge gravity. */ public static ViewAction openDrawer(final int drawerEdgeGravity) { return wrap(new ViewAction() { @Override public Matcher getConstraints() { return isAssignableFrom(DrawerLayout.class); } @Override public String getDescription() { return "Opens the drawer"; } @Override public void perform(UiController uiController, View view) { uiController.loopMainThreadUntilIdle(); DrawerLayout drawerLayout = (DrawerLayout) view; drawerLayout.openDrawer(drawerEdgeGravity); } }); } /** * Opens the drawer. */ public static ViewAction openDrawer(final View drawerView) { return wrap(new ViewAction() { @Override public Matcher getConstraints() { return isAssignableFrom(DrawerLayout.class); } @Override public String getDescription() { return "Opens the drawer"; } @Override public void perform(UiController uiController, View view) { uiController.loopMainThreadUntilIdle(); DrawerLayout drawerLayout = (DrawerLayout) view; drawerLayout.openDrawer(drawerView); } }); } /** * Closes the drawer at the specified edge gravity. */ public static ViewAction closeDrawer(final int drawerEdgeGravity) { return wrap(new ViewAction() { @Override public Matcher getConstraints() { return isAssignableFrom(DrawerLayout.class); } @Override public String getDescription() { return "Closes the drawer"; } @Override public void perform(UiController uiController, View view) { uiController.loopMainThreadUntilIdle(); DrawerLayout drawerLayout = (DrawerLayout) view; drawerLayout.closeDrawer(drawerEdgeGravity); } }); } /** * Closes the drawer at the specified edge gravity. */ public static ViewAction closeDrawer(final int drawerEdgeGravity, final boolean animate) { return wrap(new ViewAction() { @Override public Matcher getConstraints() { return isAssignableFrom(DrawerLayout.class); } @Override public String getDescription() { return "Closes the drawer"; } @Override public void perform(UiController uiController, View view) { uiController.loopMainThreadUntilIdle(); DrawerLayout drawerLayout = (DrawerLayout) view; drawerLayout.closeDrawer(drawerEdgeGravity, animate); } }); } /** * Closes the drawer. */ public static ViewAction closeDrawer(final View drawerView) { return wrap(new ViewAction() { @Override public Matcher getConstraints() { return isAssignableFrom(DrawerLayout.class); } @Override public String getDescription() { return "Closes the drawer"; } @Override public void perform(UiController uiController, View view) { uiController.loopMainThreadUntilIdle(); DrawerLayout drawerLayout = (DrawerLayout) view; drawerLayout.closeDrawer(drawerView); } }); } /** * Sets the lock mode for the drawer at the specified edge gravity. */ public static ViewAction setDrawerLockMode(final int lockMode, final int drawerEdgeGravity) { return wrap(new ViewAction() { @Override public Matcher getConstraints() { return isAssignableFrom(DrawerLayout.class); } @Override public String getDescription() { return "Sets drawer lock mode"; } @Override public void perform(UiController uiController, View view) { uiController.loopMainThreadUntilIdle(); DrawerLayout drawerLayout = (DrawerLayout) view; drawerLayout.setDrawerLockMode(lockMode, drawerEdgeGravity); uiController.loopMainThreadUntilIdle(); } }); } /** * Sets the lock mode for the drawer. */ public static ViewAction setDrawerLockMode(final int lockMode, final View drawerView) { return wrap(new ViewAction() { @Override public Matcher getConstraints() { return isAssignableFrom(DrawerLayout.class); } @Override public String getDescription() { return "Sets drawer lock mode"; } @Override public void perform(UiController uiController, View view) { uiController.loopMainThreadUntilIdle(); DrawerLayout drawerLayout = (DrawerLayout) view; drawerLayout.setDrawerLockMode(lockMode, drawerView); uiController.loopMainThreadUntilIdle(); } }); } }