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 196bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 206bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.os.SystemClock; 216bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.annotation.LayoutRes; 22dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Arakiimport android.support.annotation.NonNull; 2348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.design.test.R; 24ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikovimport android.support.test.InstrumentationRegistry; 2548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.Espresso; 2648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.IdlingResource; 276bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.NoMatchingViewException; 286bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.UiController; 296bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.ViewAction; 306bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.ViewAssertion; 31e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Arakiimport android.support.test.espresso.action.CoordinatesProvider; 3248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.GeneralLocation; 3348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.GeneralSwipeAction; 346bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.action.MotionEvents; 356bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.action.PrecisionDescriber; 3648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.Press; 3748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.Swipe; 3848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.ViewActions; 3948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.assertion.ViewAssertions; 406bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.core.deps.guava.base.Preconditions; 4148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.matcher.ViewMatchers; 426bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.v4.view.ViewCompat; 43b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Arakiimport android.support.v4.widget.NestedScrollView; 4448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.test.suitebuilder.annotation.MediumTest; 4548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.test.suitebuilder.annotation.SmallTest; 466bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.view.LayoutInflater; 476bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.view.MotionEvent; 4848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.view.View; 496bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.view.ViewConfiguration; 5048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.view.ViewGroup; 51b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Arakiimport android.widget.TextView; 52b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki 53ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikovimport org.hamcrest.Matcher; 54ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikovimport org.junit.Test; 5548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 5648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport static org.hamcrest.CoreMatchers.is; 5748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport static org.hamcrest.CoreMatchers.not; 5848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport static org.hamcrest.MatcherAssert.assertThat; 5948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 6048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakipublic class BottomSheetBehaviorTest extends 6148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki BaseInstrumentationTestCase<BottomSheetBehaviorActivity> { 6248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 6348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public static class Callback extends BottomSheetBehavior.BottomSheetCallback 6448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki implements IdlingResource { 6548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 6648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private boolean mIsIdle; 6748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 6848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private IdlingResource.ResourceCallback mResourceCallback; 6948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 7048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public Callback(BottomSheetBehavior behavior) { 7148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki behavior.setBottomSheetCallback(this); 7248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki int state = behavior.getState(); 7348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mIsIdle = isIdleState(state); 7448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 7548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 7648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 77dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public void onStateChanged(@NonNull View bottomSheet, 78dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki @BottomSheetBehavior.State int newState) { 7948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki boolean wasIdle = mIsIdle; 8048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mIsIdle = isIdleState(newState); 8148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki if (!wasIdle && mIsIdle && mResourceCallback != null) { 8248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mResourceCallback.onTransitionToIdle(); 8348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 8448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 8548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 8648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 87dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public void onSlide(@NonNull View bottomSheet, float slideOffset) { 8848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 8948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 9048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 9148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public String getName() { 9248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki return Callback.class.getSimpleName(); 9348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 9448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 9548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 9648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public boolean isIdleNow() { 9748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki return mIsIdle; 9848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 9948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 10048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 10148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void registerIdleTransitionCallback(IdlingResource.ResourceCallback callback) { 10248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mResourceCallback = callback; 10348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 10448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 10548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private boolean isIdleState(int state) { 10648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki return state != BottomSheetBehavior.STATE_DRAGGING && 10748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki state != BottomSheetBehavior.STATE_SETTLING; 10848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 10948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 11048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 11148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 1126bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki /** 1136bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki * This is like {@link GeneralSwipeAction}, but it does not send ACTION_UP at the end. 1146bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki */ 1156bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static class DragAction implements ViewAction { 1166bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1176bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static final int STEPS = 10; 1186bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static final int DURATION = 100; 1196bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1206bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private final CoordinatesProvider mStart; 1216bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private final CoordinatesProvider mEnd; 1226bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private final PrecisionDescriber mPrecisionDescriber; 1236bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1246bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public DragAction(CoordinatesProvider start, CoordinatesProvider end, 1256bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki PrecisionDescriber precisionDescriber) { 1266bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki mStart = start; 1276bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki mEnd = end; 1286bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki mPrecisionDescriber = precisionDescriber; 1296bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1306bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1316bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 1326bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public Matcher<View> getConstraints() { 1336bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return ViewMatchers.isDisplayed(); 1346bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1356bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1366bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 1376bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public String getDescription() { 1386bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return "drag"; 1396bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1406bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1416bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 1426bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void perform(UiController uiController, View view) { 1436bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[] precision = mPrecisionDescriber.describePrecision(); 1446bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[] start = mStart.calculateCoordinates(view); 1456bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[] end = mEnd.calculateCoordinates(view); 1466bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[][] steps = interpolate(start, end, STEPS); 1476bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki int delayBetweenMovements = DURATION / steps.length; 1486bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Down 1496bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki MotionEvent downEvent = MotionEvents.sendDown(uiController, start, precision).down; 1506bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki try { 1516bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki for (int i = 0; i < steps.length; i++) { 1526bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Wait 1536bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki long desiredTime = downEvent.getDownTime() + (long)(delayBetweenMovements * i); 1546bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki long timeUntilDesired = desiredTime - SystemClock.uptimeMillis(); 1556bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki if (timeUntilDesired > 10L) { 1566bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki uiController.loopMainThreadForAtLeast(timeUntilDesired); 1576bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1586bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Move 1596bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki if (!MotionEvents.sendMovement(uiController, downEvent, steps[i])) { 1606bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki MotionEvents.sendCancel(uiController, downEvent); 1616bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki throw new RuntimeException("Cannot drag: failed to send a move event."); 1626bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1636bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki BottomSheetBehavior behavior = BottomSheetBehavior.from(view); 1646bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1656bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki int duration = ViewConfiguration.getPressedStateDuration(); 1666bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki if (duration > 0) { 1676bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki uiController.loopMainThreadForAtLeast((long) duration); 1686bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1696bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } finally { 1706bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki downEvent.recycle(); 1716bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1726bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1736bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1746bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static float[][] interpolate(float[] start, float[] end, int steps) { 1756bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki Preconditions.checkElementIndex(1, start.length); 1766bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki Preconditions.checkElementIndex(1, end.length); 1776bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[][] res = new float[steps][2]; 1786bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki for(int i = 1; i < steps + 1; ++i) { 1796bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki res[i - 1][0] = start[0] + (end[0] - start[0]) * (float)i / ((float)steps + 2.0F); 1806bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki res[i - 1][1] = start[1] + (end[1] - start[1]) * (float)i / ((float)steps + 2.0F); 1816bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1826bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return res; 1836bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1846bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1856bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1866bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static class AddViewAction implements ViewAction { 1876bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1886bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private final int mLayout; 1896bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1906bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public AddViewAction(@LayoutRes int layout) { 1916bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki mLayout = layout; 1926bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1936bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1946bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 1956bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public Matcher<View> getConstraints() { 1966bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return ViewMatchers.isAssignableFrom(ViewGroup.class); 1976bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1986bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1996bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 2006bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public String getDescription() { 2016bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return "add view"; 2026bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2036bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2046bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 2056bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void perform(UiController uiController, View view) { 2066bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki ViewGroup parent = (ViewGroup) view; 2076bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki View child = LayoutInflater.from(view.getContext()).inflate(mLayout, parent, false); 2086bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki parent.addView(child); 2096bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2106bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2116bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 21248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private Callback mCallback; 21348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 21448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public BottomSheetBehaviorTest() { 21548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki super(BottomSheetBehaviorActivity.class); 21648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 21748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 21848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 21948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @SmallTest 22048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testInitialSetup() { 22148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki BottomSheetBehavior behavior = getBehavior(); 22248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 22348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki CoordinatorLayout coordinatorLayout = getCoordinatorLayout(); 22448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki ViewGroup bottomSheet = getBottomSheet(); 22548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(bottomSheet.getTop(), 22648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki is(coordinatorLayout.getHeight() - behavior.getPeekHeight())); 22748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 22848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 22948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 23048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 23148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSetStateExpandedToCollapsed() { 23248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 23348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 23448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 23548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 23648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 23748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 23848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSetStateHiddenToCollapsed() { 23948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_HIDDEN, not(ViewMatchers.isDisplayed())); 24048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 24148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 24248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 24348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 24448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 245e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki public void testSetStateCollapsedToCollapsed() { 246e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 247e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki } 248e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki 249e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki @Test 250e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki @MediumTest 25148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSwipeDownToCollapse() { 25248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 25348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 254e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki .perform(DesignViewActions.withCustomConstraints(new GeneralSwipeAction( 2558609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki Swipe.FAST, 2568609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki // Manually calculate the starting coordinates to make sure that the touch 2578609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki // actually falls onto the view on Gingerbread 2588609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki new CoordinatesProvider() { 2598609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki @Override 2608609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki public float[] calculateCoordinates(View view) { 2618609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki int[] location = new int[2]; 2628609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki view.getLocationInWindow(location); 2638609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki return new float[]{ 2648609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki view.getWidth() / 2, 2658609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki location[1] + 1 2668609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki }; 2678609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki } 2688609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki }, 269e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // Manually calculate the ending coordinates to make sure that the bottom 270e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // sheet is collapsed, not hidden 271e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki new CoordinatesProvider() { 272e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki @Override 273e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki public float[] calculateCoordinates(View view) { 274e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki BottomSheetBehavior behavior = getBehavior(); 275e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki return new float[]{ 276e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // x: center of the bottom sheet 277e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki view.getWidth() / 2, 278e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // y: just above the peek height 279e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki view.getHeight() - behavior.getPeekHeight()}; 280e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki } 281e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki }, Press.FINGER), ViewMatchers.isDisplayingAtLeast(5))); 28248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki // Avoid a deadlock (b/26160710) 28348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki registerIdlingResourceCallback(); 28448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 28548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 28648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 28748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 28848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 28948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 29048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 29148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 29248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 29348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 29448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 29548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSwipeDownToHide() { 29648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 29748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .perform(DesignViewActions.withCustomConstraints(ViewActions.swipeDown(), 29848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki ViewMatchers.isDisplayingAtLeast(5))); 29948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki // Avoid a deadlock (b/26160710) 300d7e25b5cfb70b867b7467acb613a984310a0c40dYuichi Araki registerIdlingResourceCallback(); 30148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 30248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 30348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))); 30448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_HIDDEN)); 30548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 30648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 30748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 30848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 30948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 31048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 311362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki public void testSkipCollapsed() { 312362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki getBehavior().setSkipCollapsed(true); 313362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 314362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 315362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki .perform(DesignViewActions.withCustomConstraints(new GeneralSwipeAction( 316362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki Swipe.FAST, 317362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki // Manually calculate the starting coordinates to make sure that the touch 318362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki // actually falls onto the view on Gingerbread 319362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki new CoordinatesProvider() { 320362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki @Override 321362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki public float[] calculateCoordinates(View view) { 322362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki int[] location = new int[2]; 323362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki view.getLocationInWindow(location); 324362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki return new float[]{ 325362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki view.getWidth() / 2, 326362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki location[1] + 1 327362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki }; 328362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 329362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki }, 330362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki // Manually calculate the ending coordinates to make sure that the bottom 331362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki // sheet is collapsed, not hidden 332362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki new CoordinatesProvider() { 333362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki @Override 334362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki public float[] calculateCoordinates(View view) { 335362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki BottomSheetBehavior behavior = getBehavior(); 336362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki return new float[]{ 337362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki // x: center of the bottom sheet 338362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki view.getWidth() / 2, 339362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki // y: just above the peek height 340362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki view.getHeight() - behavior.getPeekHeight()}; 341362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 342362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki }, Press.FINGER), ViewMatchers.isDisplayingAtLeast(5))); 343362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki registerIdlingResourceCallback(); 344362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki try { 345362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 346362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))); 347362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_HIDDEN)); 348362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } finally { 349362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki unregisterIdlingResourceCallback(); 350362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 351362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 352362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki 353362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki @Test 35448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 35548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSwipeUpToExpand() { 35648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 35748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .perform(DesignViewActions.withCustomConstraints( 35848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki new GeneralSwipeAction(Swipe.FAST, 359e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki GeneralLocation.VISIBLE_CENTER, new CoordinatesProvider() { 360e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki @Override 361e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki public float[] calculateCoordinates(View view) { 362e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki return new float[]{view.getWidth() / 2, 0}; 363e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki } 364e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki }, Press.FINGER), 36548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki ViewMatchers.isDisplayingAtLeast(5))); 36648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki // Avoid a deadlock (b/26160710) 36748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki registerIdlingResourceCallback(); 36848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 36948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 37048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 37148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_EXPANDED)); 37248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 37348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 37448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 37548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 37648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 37717fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @Test 37817fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @MediumTest 379ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov public void testInvisible() { 38017fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki // Make the bottomsheet invisible 381ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 38217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @Override 38317fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki public void run() { 38417fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki getBottomSheet().setVisibility(View.INVISIBLE); 38517fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 38617fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 38717fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki }); 38817fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki // Swipe up as if to expand it 38917fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 39017fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki .perform(DesignViewActions.withCustomConstraints( 39117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki new GeneralSwipeAction(Swipe.FAST, 39217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki GeneralLocation.VISIBLE_CENTER, new CoordinatesProvider() { 39317fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @Override 39417fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki public float[] calculateCoordinates(View view) { 39517fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki return new float[]{view.getWidth() / 2, 0}; 39617fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 39717fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki }, Press.FINGER), 39817fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki not(ViewMatchers.isDisplayed()))); 39917fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki // Check that the bottom sheet stays the same collapsed state 400ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 40117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @Override 40217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki public void run() { 40317fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 40417fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 40517fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki }); 40617fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 40717fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki 408b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Test 409b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @MediumTest 410b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void testNestedScroll() { 411b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki final ViewGroup bottomSheet = getBottomSheet(); 412b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki final BottomSheetBehavior behavior = getBehavior(); 413b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki final NestedScrollView scroll = new NestedScrollView(mActivityTestRule.getActivity()); 414b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // Set up nested scrolling area 415b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 416b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 417b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void run() { 418b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki bottomSheet.addView(scroll, new ViewGroup.LayoutParams( 419b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 420b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki TextView view = new TextView(mActivityTestRule.getActivity()); 421b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki StringBuilder sb = new StringBuilder(); 422b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki for (int i = 0; i < 500; ++i) { 423b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki sb.append("It is fine today. "); 424b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 425b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki view.setText(sb); 426b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki view.setOnClickListener(new View.OnClickListener() { 427b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 428b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void onClick(View v) { 429b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // Do nothing 430b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 431b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }); 432b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki scroll.addView(view); 433b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 434b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // The scroll offset is 0 at first 435b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(scroll.getScrollY(), is(0)); 436b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 437b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }); 438b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // Swipe from the very bottom of the bottom sheet to the top edge of the screen so that the 439b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // scrolling content is also scrolled 440b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.coordinator)) 441b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki .perform(new GeneralSwipeAction(Swipe.FAST, 442b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki new CoordinatesProvider() { 443b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 444b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public float[] calculateCoordinates(View view) { 445b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki return new float[]{view.getWidth() / 2, view.getHeight() - 1}; 446b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 447b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }, 448b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki new CoordinatesProvider() { 449b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 450b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public float[] calculateCoordinates(View view) { 451b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki return new float[]{view.getWidth() / 2, 1}; 452b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 453b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }, Press.FINGER)); 454b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki registerIdlingResourceCallback(); 455b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki try { 456b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 457b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 458b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 459b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 460b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void run() { 461b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_EXPANDED)); 462b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // This confirms that the nested scrolling area was scrolled continuously after 463b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // the bottom sheet is expanded. 464b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(scroll.getScrollY(), is(not(0))); 465b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 466b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }); 467b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } finally { 468b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki unregisterIdlingResourceCallback(); 469b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 470b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 471b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki 4726bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Test 4736bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @MediumTest 474912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki public void testDragOutside() { 475912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Swipe up outside of the bottom sheet 476912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki Espresso.onView(ViewMatchers.withId(R.id.coordinator)) 477912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki .perform(DesignViewActions.withCustomConstraints( 478912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki new GeneralSwipeAction(Swipe.FAST, 479912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Just above the bottom sheet 480912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki new CoordinatesProvider() { 481912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki @Override 482912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki public float[] calculateCoordinates(View view) { 483912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki return new float[]{ 484912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki view.getWidth() / 2, 485912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki view.getHeight() - getBehavior().getPeekHeight() - 9 486912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki }; 487912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 488912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki }, 489912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Top of the CoordinatorLayout 490912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki new CoordinatesProvider() { 491912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki @Override 492912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki public float[] calculateCoordinates(View view) { 493912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki return new float[]{view.getWidth() / 2, 1}; 494912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 495912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki }, Press.FINGER), 496912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki ViewMatchers.isDisplayed())); 497912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Avoid a deadlock (b/26160710) 498912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki registerIdlingResourceCallback(); 499912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki try { 500912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 501912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 502912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // The bottom sheet should remain collapsed 503912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 504912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } finally { 505912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki unregisterIdlingResourceCallback(); 506912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 507912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 508912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki 5096bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Test 5106bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @MediumTest 5116bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void testLayoutWhileDragging() { 5126bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 5136bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Drag (and not release) 5146bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki .perform(new DragAction( 5156bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki GeneralLocation.VISIBLE_CENTER, 5166bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki GeneralLocation.TOP_CENTER, 5176bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki Press.FINGER)) 5186bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Check that the bottom sheet is in STATE_DRAGGING 5196bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki .check(new ViewAssertion() { 5206bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 5216bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void check(View view, NoMatchingViewException e) { 5226bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki assertThat(view, is(ViewMatchers.isDisplayed())); 5236bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki BottomSheetBehavior behavior = BottomSheetBehavior.from(view); 5246bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_DRAGGING)); 5256bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 5266bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki }) 5276bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Add a new view 5286bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki .perform(new AddViewAction(R.layout.frame_layout)) 5296bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Check that the newly added view is properly laid out 5306bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki .check(new ViewAssertion() { 5316bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 5326bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void check(View view, NoMatchingViewException e) { 5336bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki ViewGroup parent = (ViewGroup) view; 5346bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki assertThat(parent.getChildCount(), is(1)); 5356bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki View child = parent.getChildAt(0); 5366bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki assertThat(ViewCompat.isLaidOut(child), is(true)); 5376bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 5386bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki }); 5396bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 5406bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 541ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov private void checkSetState(final int state, Matcher<View> matcher) { 54248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki registerIdlingResourceCallback(); 54348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 544ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 545ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov @Override 546ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov public void run() { 547ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov getBehavior().setState(state); 548ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov } 549ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov }); 55048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 55148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(matcher)); 55248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(state)); 55348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 55448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 55548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 55648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 55748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 55848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private void registerIdlingResourceCallback() { 55948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki // TODO(yaraki): Move this to setUp() when b/26160710 is fixed 56048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mCallback = new Callback(getBehavior()); 56148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.registerIdlingResources(mCallback); 56248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 56348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 56448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private void unregisterIdlingResourceCallback() { 56548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki if (mCallback != null) { 56648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.unregisterIdlingResources(mCallback); 56748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mCallback = null; 56848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 56948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 57048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 57148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private ViewGroup getBottomSheet() { 572ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov return mActivityTestRule.getActivity().mBottomSheet; 57348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 57448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 57548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private BottomSheetBehavior getBehavior() { 576ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov return mActivityTestRule.getActivity().mBehavior; 57748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 57848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 57948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private CoordinatorLayout getCoordinatorLayout() { 580ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov return mActivityTestRule.getActivity().mCoordinatorLayout; 58148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 58248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 58348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki} 584