BottomSheetBehaviorTest.java revision e0573dfb5896eb66e266ea366d8c58437c739c95
148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki/* 248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * Copyright (C) 2016 The Android Open Source Project 348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * 448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * Licensed under the Apache License, Version 2.0 (the "License"); 548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * you may not use this file except in compliance with the License. 648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * You may obtain a copy of the License at 748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * 848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * http://www.apache.org/licenses/LICENSE-2.0 948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * 1048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * Unless required by applicable law or agreed to in writing, software 1148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * distributed under the License is distributed on an "AS IS" BASIS, 1248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * See the License for the specific language governing permissions and 1448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki * limitations under the License. 1548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki */ 1648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 1748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakipackage android.support.design.widget; 1848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 19dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Arakiimport android.support.annotation.NonNull; 2048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.design.test.R; 21ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikovimport android.support.test.InstrumentationRegistry; 2248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.Espresso; 2348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.IdlingResource; 24e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Arakiimport android.support.test.espresso.action.CoordinatesProvider; 2548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.GeneralLocation; 2648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.GeneralSwipeAction; 2748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.Press; 2848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.Swipe; 2948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.ViewActions; 3048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.assertion.ViewAssertions; 3148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.matcher.ViewMatchers; 32b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Arakiimport android.support.v4.widget.NestedScrollView; 3348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.test.suitebuilder.annotation.MediumTest; 3448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.test.suitebuilder.annotation.SmallTest; 3548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.view.View; 3648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.view.ViewGroup; 37b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Arakiimport android.widget.TextView; 38b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki 39ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikovimport org.hamcrest.Matcher; 40ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikovimport org.junit.Test; 4148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 4248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport static org.hamcrest.CoreMatchers.is; 4348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport static org.hamcrest.CoreMatchers.not; 4448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport static org.hamcrest.MatcherAssert.assertThat; 4548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 4648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakipublic class BottomSheetBehaviorTest extends 4748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki BaseInstrumentationTestCase<BottomSheetBehaviorActivity> { 4848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 4948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public static class Callback extends BottomSheetBehavior.BottomSheetCallback 5048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki implements IdlingResource { 5148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 5248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private boolean mIsIdle; 5348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 5448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private IdlingResource.ResourceCallback mResourceCallback; 5548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 5648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public Callback(BottomSheetBehavior behavior) { 5748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki behavior.setBottomSheetCallback(this); 5848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki int state = behavior.getState(); 5948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mIsIdle = isIdleState(state); 6048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 6148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 6248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 63dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public void onStateChanged(@NonNull View bottomSheet, 64dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki @BottomSheetBehavior.State int newState) { 6548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki boolean wasIdle = mIsIdle; 6648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mIsIdle = isIdleState(newState); 6748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki if (!wasIdle && mIsIdle && mResourceCallback != null) { 6848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mResourceCallback.onTransitionToIdle(); 6948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 7048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 7148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 7248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 73dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public void onSlide(@NonNull View bottomSheet, float slideOffset) { 7448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 7548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 7648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 7748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public String getName() { 7848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki return Callback.class.getSimpleName(); 7948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 8048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 8148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 8248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public boolean isIdleNow() { 8348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki return mIsIdle; 8448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 8548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 8648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 8748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void registerIdleTransitionCallback(IdlingResource.ResourceCallback callback) { 8848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mResourceCallback = callback; 8948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 9048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 9148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private boolean isIdleState(int state) { 9248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki return state != BottomSheetBehavior.STATE_DRAGGING && 9348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki state != BottomSheetBehavior.STATE_SETTLING; 9448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 9548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 9648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 9748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 9848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private Callback mCallback; 9948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 10048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public BottomSheetBehaviorTest() { 10148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki super(BottomSheetBehaviorActivity.class); 10248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 10348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 10448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 10548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @SmallTest 10648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testInitialSetup() { 10748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki BottomSheetBehavior behavior = getBehavior(); 10848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 10948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki CoordinatorLayout coordinatorLayout = getCoordinatorLayout(); 11048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki ViewGroup bottomSheet = getBottomSheet(); 11148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(bottomSheet.getTop(), 11248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki is(coordinatorLayout.getHeight() - behavior.getPeekHeight())); 11348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 11448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 11548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 11648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 11748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSetStateExpandedToCollapsed() { 11848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 11948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 12048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 12148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 12248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 12348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 12448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSetStateHiddenToCollapsed() { 12548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_HIDDEN, not(ViewMatchers.isDisplayed())); 12648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 12748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 12848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 12948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 13048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 131e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki public void testSetStateCollapsedToCollapsed() { 132e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 133e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki } 134e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki 135e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki @Test 136e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki @MediumTest 13748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSwipeDownToCollapse() { 13848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 13948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 140e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki .perform(DesignViewActions.withCustomConstraints(new GeneralSwipeAction( 1418609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki Swipe.FAST, 1428609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki // Manually calculate the starting coordinates to make sure that the touch 1438609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki // actually falls onto the view on Gingerbread 1448609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki new CoordinatesProvider() { 1458609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki @Override 1468609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki public float[] calculateCoordinates(View view) { 1478609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki int[] location = new int[2]; 1488609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki view.getLocationInWindow(location); 1498609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki return new float[]{ 1508609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki view.getWidth() / 2, 1518609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki location[1] + 1 1528609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki }; 1538609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki } 1548609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki }, 155e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // Manually calculate the ending coordinates to make sure that the bottom 156e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // sheet is collapsed, not hidden 157e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki new CoordinatesProvider() { 158e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki @Override 159e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki public float[] calculateCoordinates(View view) { 160e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki BottomSheetBehavior behavior = getBehavior(); 161e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki return new float[]{ 162e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // x: center of the bottom sheet 163e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki view.getWidth() / 2, 164e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // y: just above the peek height 165e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki view.getHeight() - behavior.getPeekHeight()}; 166e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki } 167e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki }, Press.FINGER), ViewMatchers.isDisplayingAtLeast(5))); 16848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki // Avoid a deadlock (b/26160710) 16948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki registerIdlingResourceCallback(); 17048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 17148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 17248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 17348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 17448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 17548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 17648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 17748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 17848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 17948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 18048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 18148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSwipeDownToHide() { 18248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 18348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .perform(DesignViewActions.withCustomConstraints(ViewActions.swipeDown(), 18448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki ViewMatchers.isDisplayingAtLeast(5))); 18548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki // Avoid a deadlock (b/26160710) 186d7e25b5cfb70b867b7467acb613a984310a0c40dYuichi Araki registerIdlingResourceCallback(); 18748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 18848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 18948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))); 19048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_HIDDEN)); 19148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 19248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 19348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 19448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 19548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 19648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 19748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 19848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSwipeUpToExpand() { 19948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 20048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .perform(DesignViewActions.withCustomConstraints( 20148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki new GeneralSwipeAction(Swipe.FAST, 202e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki GeneralLocation.VISIBLE_CENTER, new CoordinatesProvider() { 203e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki @Override 204e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki public float[] calculateCoordinates(View view) { 205e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki return new float[]{view.getWidth() / 2, 0}; 206e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki } 207e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki }, Press.FINGER), 20848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki ViewMatchers.isDisplayingAtLeast(5))); 20948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki // Avoid a deadlock (b/26160710) 21048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki registerIdlingResourceCallback(); 21148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 21248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 21348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 21448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_EXPANDED)); 21548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 21648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 21748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 21848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 21948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 22017fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @Test 22117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @MediumTest 222ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov public void testInvisible() { 22317fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki // Make the bottomsheet invisible 224ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 22517fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @Override 22617fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki public void run() { 22717fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki getBottomSheet().setVisibility(View.INVISIBLE); 22817fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 22917fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 23017fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki }); 23117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki // Swipe up as if to expand it 23217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 23317fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki .perform(DesignViewActions.withCustomConstraints( 23417fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki new GeneralSwipeAction(Swipe.FAST, 23517fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki GeneralLocation.VISIBLE_CENTER, new CoordinatesProvider() { 23617fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @Override 23717fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki public float[] calculateCoordinates(View view) { 23817fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki return new float[]{view.getWidth() / 2, 0}; 23917fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 24017fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki }, Press.FINGER), 24117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki not(ViewMatchers.isDisplayed()))); 24217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki // Check that the bottom sheet stays the same collapsed state 243ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 24417fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @Override 24517fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki public void run() { 24617fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 24717fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 24817fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki }); 24917fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 25017fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki 251b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Test 252b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @MediumTest 253b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void testNestedScroll() { 254b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki final ViewGroup bottomSheet = getBottomSheet(); 255b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki final BottomSheetBehavior behavior = getBehavior(); 256b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki final NestedScrollView scroll = new NestedScrollView(mActivityTestRule.getActivity()); 257b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // Set up nested scrolling area 258b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 259b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 260b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void run() { 261b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki bottomSheet.addView(scroll, new ViewGroup.LayoutParams( 262b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 263b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki TextView view = new TextView(mActivityTestRule.getActivity()); 264b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki StringBuilder sb = new StringBuilder(); 265b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki for (int i = 0; i < 500; ++i) { 266b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki sb.append("It is fine today. "); 267b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 268b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki view.setText(sb); 269b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki view.setOnClickListener(new View.OnClickListener() { 270b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 271b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void onClick(View v) { 272b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // Do nothing 273b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 274b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }); 275b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki scroll.addView(view); 276b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 277b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // The scroll offset is 0 at first 278b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(scroll.getScrollY(), is(0)); 279b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 280b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }); 281b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // Swipe from the very bottom of the bottom sheet to the top edge of the screen so that the 282b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // scrolling content is also scrolled 283b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.coordinator)) 284b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki .perform(new GeneralSwipeAction(Swipe.FAST, 285b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki new CoordinatesProvider() { 286b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 287b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public float[] calculateCoordinates(View view) { 288b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki return new float[]{view.getWidth() / 2, view.getHeight() - 1}; 289b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 290b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }, 291b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki new CoordinatesProvider() { 292b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 293b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public float[] calculateCoordinates(View view) { 294b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki return new float[]{view.getWidth() / 2, 1}; 295b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 296b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }, Press.FINGER)); 297b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki registerIdlingResourceCallback(); 298b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki try { 299b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 300b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 301b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 302b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 303b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void run() { 304b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_EXPANDED)); 305b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // This confirms that the nested scrolling area was scrolled continuously after 306b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // the bottom sheet is expanded. 307b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(scroll.getScrollY(), is(not(0))); 308b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 309b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }); 310b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } finally { 311b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki unregisterIdlingResourceCallback(); 312b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 313b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 314b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki 315912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki public void testDragOutside() { 316912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Swipe up outside of the bottom sheet 317912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki Espresso.onView(ViewMatchers.withId(R.id.coordinator)) 318912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki .perform(DesignViewActions.withCustomConstraints( 319912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki new GeneralSwipeAction(Swipe.FAST, 320912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Just above the bottom sheet 321912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki new CoordinatesProvider() { 322912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki @Override 323912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki public float[] calculateCoordinates(View view) { 324912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki return new float[]{ 325912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki view.getWidth() / 2, 326912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki view.getHeight() - getBehavior().getPeekHeight() - 9 327912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki }; 328912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 329912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki }, 330912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Top of the CoordinatorLayout 331912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki new CoordinatesProvider() { 332912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki @Override 333912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki public float[] calculateCoordinates(View view) { 334912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki return new float[]{view.getWidth() / 2, 1}; 335912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 336912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki }, Press.FINGER), 337912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki ViewMatchers.isDisplayed())); 338912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Avoid a deadlock (b/26160710) 339912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki registerIdlingResourceCallback(); 340912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki try { 341912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 342912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 343912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // The bottom sheet should remain collapsed 344912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 345912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } finally { 346912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki unregisterIdlingResourceCallback(); 347912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 348912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 349912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki 350ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov private void checkSetState(final int state, Matcher<View> matcher) { 35148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki registerIdlingResourceCallback(); 35248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 353ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 354ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov @Override 355ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov public void run() { 356ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov getBehavior().setState(state); 357ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov } 358ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov }); 35948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 36048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(matcher)); 36148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(state)); 36248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 36348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 36448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 36548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 36648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 36748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private void registerIdlingResourceCallback() { 36848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki // TODO(yaraki): Move this to setUp() when b/26160710 is fixed 36948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mCallback = new Callback(getBehavior()); 37048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.registerIdlingResources(mCallback); 37148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 37248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 37348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private void unregisterIdlingResourceCallback() { 37448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki if (mCallback != null) { 37548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.unregisterIdlingResources(mCallback); 37648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mCallback = null; 37748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 37848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 37948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 38048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private ViewGroup getBottomSheet() { 381ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov return mActivityTestRule.getActivity().mBottomSheet; 38248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 38348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 38448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private BottomSheetBehavior getBehavior() { 385ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov return mActivityTestRule.getActivity().mBehavior; 38648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 38748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 38848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private CoordinatorLayout getCoordinatorLayout() { 389ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov return mActivityTestRule.getActivity().mCoordinatorLayout; 39048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 39148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 39248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki} 393