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 198f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Arakiimport static org.hamcrest.CoreMatchers.is; 208f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Arakiimport static org.hamcrest.CoreMatchers.not; 218f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Arakiimport static org.hamcrest.MatcherAssert.assertThat; 22e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Arakiimport static org.hamcrest.Matchers.greaterThanOrEqualTo; 23e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Arakiimport static org.hamcrest.Matchers.lessThanOrEqualTo; 24acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikasimport static org.junit.Assert.fail; 258f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 2647a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguireimport android.content.Context; 276bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.os.SystemClock; 286bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.annotation.LayoutRes; 29dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Arakiimport android.support.annotation.NonNull; 3048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.design.test.R; 3148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.Espresso; 3248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.IdlingResource; 336bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.NoMatchingViewException; 346bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.UiController; 356bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.ViewAction; 366bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.ViewAssertion; 37e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Arakiimport android.support.test.espresso.action.CoordinatesProvider; 3848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.GeneralLocation; 3948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.GeneralSwipeAction; 406bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.action.MotionEvents; 416bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.action.PrecisionDescriber; 4248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.Press; 4348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.Swipe; 4448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.action.ViewActions; 4548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.assertion.ViewAssertions; 466bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.test.espresso.core.deps.guava.base.Preconditions; 4748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.test.espresso.matcher.ViewMatchers; 48754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport android.support.test.filters.MediumTest; 49754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport android.support.test.filters.SmallTest; 506bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.v4.view.ViewCompat; 51b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Arakiimport android.support.v4.widget.NestedScrollView; 526bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.view.LayoutInflater; 536bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.view.MotionEvent; 5448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.view.View; 556bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.view.ViewConfiguration; 5648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.view.ViewGroup; 57b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki 58ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikovimport org.hamcrest.Matcher; 596cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Arakiimport org.hamcrest.Matchers; 60ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikovimport org.junit.Test; 6148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 6248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakipublic class BottomSheetBehaviorTest extends 6348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki BaseInstrumentationTestCase<BottomSheetBehaviorActivity> { 6448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 6548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public static class Callback extends BottomSheetBehavior.BottomSheetCallback 6648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki implements IdlingResource { 6748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 6848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private boolean mIsIdle; 6948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 7048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private IdlingResource.ResourceCallback mResourceCallback; 7148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 7248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public Callback(BottomSheetBehavior behavior) { 7348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki behavior.setBottomSheetCallback(this); 7448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki int state = behavior.getState(); 7548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mIsIdle = isIdleState(state); 7648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 7748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 7848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 79dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public void onStateChanged(@NonNull View bottomSheet, 80dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki @BottomSheetBehavior.State int newState) { 8148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki boolean wasIdle = mIsIdle; 8248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mIsIdle = isIdleState(newState); 8348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki if (!wasIdle && mIsIdle && mResourceCallback != null) { 8448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mResourceCallback.onTransitionToIdle(); 8548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 8648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 8748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 8848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 89dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public void onSlide(@NonNull View bottomSheet, float slideOffset) { 90e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki assertThat(slideOffset, is(greaterThanOrEqualTo(-1f))); 91e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki assertThat(slideOffset, is(lessThanOrEqualTo(1f))); 9248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 9348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 9448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 9548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public String getName() { 9648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki return Callback.class.getSimpleName(); 9748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 9848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 9948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 10048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public boolean isIdleNow() { 10148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki return mIsIdle; 10248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 10348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 10448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 10548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void registerIdleTransitionCallback(IdlingResource.ResourceCallback callback) { 10648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mResourceCallback = callback; 10748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 10848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 10948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private boolean isIdleState(int state) { 11048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki return state != BottomSheetBehavior.STATE_DRAGGING && 11148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki state != BottomSheetBehavior.STATE_SETTLING; 11248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 11348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 11448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 11548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 1166bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki /** 1178f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki * Wait for a FAB to change its visibility (either shown or hidden). 1188f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki */ 1198f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki private static class OnVisibilityChangedListener extends 1208f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki FloatingActionButton.OnVisibilityChangedListener implements IdlingResource { 1218f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1228f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki private final boolean mShown; 1238f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki private boolean mIsIdle; 1248f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki private ResourceCallback mResourceCallback; 1258f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1268f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki OnVisibilityChangedListener(boolean shown) { 1278f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki mShown = shown; 1288f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1298f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1308f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki private void transitionToIdle() { 1318f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki if (!mIsIdle) { 1328f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki mIsIdle = true; 1338f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki if (mResourceCallback != null) { 1348f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki mResourceCallback.onTransitionToIdle(); 1358f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1368f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1378f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1388f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1398f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 1408f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public void onShown(FloatingActionButton fab) { 1418f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki if (mShown) { 1428f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki transitionToIdle(); 1438f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1448f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1458f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1468f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 1478f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public void onHidden(FloatingActionButton fab) { 1488f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki if (!mShown) { 1498f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki transitionToIdle(); 1508f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1518f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1528f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1538f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 1548f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public String getName() { 1558f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki return OnVisibilityChangedListener.class.getSimpleName(); 1568f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1578f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1588f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 1598f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public boolean isIdleNow() { 1608f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki return mIsIdle; 1618f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1628f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1638f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 1648f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { 1658f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki mResourceCallback = resourceCallback; 1668f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1678f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1688f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1698f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki /** 1706bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki * This is like {@link GeneralSwipeAction}, but it does not send ACTION_UP at the end. 1716bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki */ 1726bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static class DragAction implements ViewAction { 1736bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1746bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static final int STEPS = 10; 1756bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static final int DURATION = 100; 1766bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1776bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private final CoordinatesProvider mStart; 1786bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private final CoordinatesProvider mEnd; 1796bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private final PrecisionDescriber mPrecisionDescriber; 1806bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1816bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public DragAction(CoordinatesProvider start, CoordinatesProvider end, 1826bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki PrecisionDescriber precisionDescriber) { 1836bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki mStart = start; 1846bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki mEnd = end; 1856bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki mPrecisionDescriber = precisionDescriber; 1866bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1876bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1886bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 1896bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public Matcher<View> getConstraints() { 1906cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki return Matchers.any(View.class); 1916bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1926bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1936bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 1946bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public String getDescription() { 1956bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return "drag"; 1966bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1976bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1986bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 1996bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void perform(UiController uiController, View view) { 2006bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[] precision = mPrecisionDescriber.describePrecision(); 2016bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[] start = mStart.calculateCoordinates(view); 2026bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[] end = mEnd.calculateCoordinates(view); 2036bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[][] steps = interpolate(start, end, STEPS); 2046bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki int delayBetweenMovements = DURATION / steps.length; 2056bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Down 2066bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki MotionEvent downEvent = MotionEvents.sendDown(uiController, start, precision).down; 2076bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki try { 2086bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki for (int i = 0; i < steps.length; i++) { 2096bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Wait 2106bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki long desiredTime = downEvent.getDownTime() + (long)(delayBetweenMovements * i); 2116bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki long timeUntilDesired = desiredTime - SystemClock.uptimeMillis(); 2126bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki if (timeUntilDesired > 10L) { 2136bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki uiController.loopMainThreadForAtLeast(timeUntilDesired); 2146bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2156bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Move 2166bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki if (!MotionEvents.sendMovement(uiController, downEvent, steps[i])) { 2176bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki MotionEvents.sendCancel(uiController, downEvent); 2186bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki throw new RuntimeException("Cannot drag: failed to send a move event."); 2196bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2206bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2216bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki int duration = ViewConfiguration.getPressedStateDuration(); 2226bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki if (duration > 0) { 2236bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki uiController.loopMainThreadForAtLeast((long) duration); 2246bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2256bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } finally { 2266bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki downEvent.recycle(); 2276bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2286bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2296bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2306bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static float[][] interpolate(float[] start, float[] end, int steps) { 2316bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki Preconditions.checkElementIndex(1, start.length); 2326bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki Preconditions.checkElementIndex(1, end.length); 2336bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[][] res = new float[steps][2]; 2346bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki for(int i = 1; i < steps + 1; ++i) { 2356bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki res[i - 1][0] = start[0] + (end[0] - start[0]) * (float)i / ((float)steps + 2.0F); 2366bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki res[i - 1][1] = start[1] + (end[1] - start[1]) * (float)i / ((float)steps + 2.0F); 2376bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2386bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return res; 2396bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2406bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2416bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2426bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static class AddViewAction implements ViewAction { 2436bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2446bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private final int mLayout; 2456bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2466bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public AddViewAction(@LayoutRes int layout) { 2476bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki mLayout = layout; 2486bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2496bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2506bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 2516bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public Matcher<View> getConstraints() { 2526bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return ViewMatchers.isAssignableFrom(ViewGroup.class); 2536bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2546bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2556bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 2566bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public String getDescription() { 2576bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return "add view"; 2586bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2596bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2606bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 2616bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void perform(UiController uiController, View view) { 2626bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki ViewGroup parent = (ViewGroup) view; 2636bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki View child = LayoutInflater.from(view.getContext()).inflate(mLayout, parent, false); 2646bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki parent.addView(child); 2656bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2666bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2676bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 26848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private Callback mCallback; 26948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 27048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public BottomSheetBehaviorTest() { 27148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki super(BottomSheetBehaviorActivity.class); 27248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 27348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 27448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 27548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @SmallTest 27648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testInitialSetup() { 27748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki BottomSheetBehavior behavior = getBehavior(); 27848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 27948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki CoordinatorLayout coordinatorLayout = getCoordinatorLayout(); 28048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki ViewGroup bottomSheet = getBottomSheet(); 28148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(bottomSheet.getTop(), 28248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki is(coordinatorLayout.getHeight() - behavior.getPeekHeight())); 28348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 28448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 28548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 28648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 287acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas public void testSetStateExpandedToCollapsed() throws Throwable { 28848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 28948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 29048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 29148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 29248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 29348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 294acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas public void testSetStateHiddenToCollapsed() throws Throwable { 29548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_HIDDEN, not(ViewMatchers.isDisplayed())); 29648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 29748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 29848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 29948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 30048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 301acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas public void testSetStateCollapsedToCollapsed() throws Throwable { 302e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 303e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki } 304e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki 305e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki @Test 306e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki @MediumTest 307acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas public void testSwipeDownToCollapse() throws Throwable { 30848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 30948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 310e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki .perform(DesignViewActions.withCustomConstraints(new GeneralSwipeAction( 3118609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki Swipe.FAST, 3128609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki // Manually calculate the starting coordinates to make sure that the touch 3138609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki // actually falls onto the view on Gingerbread 3148609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki new CoordinatesProvider() { 3158609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki @Override 3168609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki public float[] calculateCoordinates(View view) { 3178609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki int[] location = new int[2]; 3188609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki view.getLocationInWindow(location); 3198609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki return new float[]{ 3208609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki view.getWidth() / 2, 3218609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki location[1] + 1 3228609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki }; 3238609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki } 3248609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki }, 325e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // Manually calculate the ending coordinates to make sure that the bottom 326e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // sheet is collapsed, not hidden 327e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki new CoordinatesProvider() { 328e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki @Override 329e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki public float[] calculateCoordinates(View view) { 330e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki BottomSheetBehavior behavior = getBehavior(); 331e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki return new float[]{ 332e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // x: center of the bottom sheet 333e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki view.getWidth() / 2, 334e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // y: just above the peek height 335e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki view.getHeight() - behavior.getPeekHeight()}; 336e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki } 337e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki }, Press.FINGER), ViewMatchers.isDisplayingAtLeast(5))); 33848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki registerIdlingResourceCallback(); 33948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 34048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 34148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 34248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 34348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 34448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 34548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 34648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 34748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 34848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 34948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 35048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSwipeDownToHide() { 35148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 35248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .perform(DesignViewActions.withCustomConstraints(ViewActions.swipeDown(), 35348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki ViewMatchers.isDisplayingAtLeast(5))); 354d7e25b5cfb70b867b7467acb613a984310a0c40dYuichi Araki registerIdlingResourceCallback(); 35548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 35648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 35748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))); 35848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_HIDDEN)); 35948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 36048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 36148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 36248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 36348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 36448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 365cea79d768278d2c55f89263f99b8860cc5447aaaAurimas Liutikas @MediumTest 366acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas public void testSkipCollapsed() throws Throwable { 367362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki getBehavior().setSkipCollapsed(true); 368362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 369362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 370362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki .perform(DesignViewActions.withCustomConstraints(new GeneralSwipeAction( 371362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki Swipe.FAST, 372362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki // Manually calculate the starting coordinates to make sure that the touch 373362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki // actually falls onto the view on Gingerbread 374362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki new CoordinatesProvider() { 375362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki @Override 376362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki public float[] calculateCoordinates(View view) { 377362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki int[] location = new int[2]; 378362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki view.getLocationInWindow(location); 379362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki return new float[]{ 380362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki view.getWidth() / 2, 381362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki location[1] + 1 382362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki }; 383362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 384362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki }, 385362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki // Manually calculate the ending coordinates to make sure that the bottom 386362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki // sheet is collapsed, not hidden 387362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki new CoordinatesProvider() { 388362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki @Override 389362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki public float[] calculateCoordinates(View view) { 390362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki BottomSheetBehavior behavior = getBehavior(); 391362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki return new float[]{ 392362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki // x: center of the bottom sheet 393362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki view.getWidth() / 2, 394362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki // y: just above the peek height 395362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki view.getHeight() - behavior.getPeekHeight()}; 396362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 397362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki }, Press.FINGER), ViewMatchers.isDisplayingAtLeast(5))); 398362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki registerIdlingResourceCallback(); 399362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki try { 400362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 401362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))); 402362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_HIDDEN)); 403362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } finally { 404362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki unregisterIdlingResourceCallback(); 405362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 406362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 407362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki 408362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki @Test 40948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 41048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSwipeUpToExpand() { 41148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 41248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .perform(DesignViewActions.withCustomConstraints( 41348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki new GeneralSwipeAction(Swipe.FAST, 414e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki GeneralLocation.VISIBLE_CENTER, new CoordinatesProvider() { 415e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki @Override 416e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki public float[] calculateCoordinates(View view) { 417e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki return new float[]{view.getWidth() / 2, 0}; 418e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki } 419e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki }, Press.FINGER), 42048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki ViewMatchers.isDisplayingAtLeast(5))); 42148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki registerIdlingResourceCallback(); 42248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 42348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 42448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 42548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_EXPANDED)); 42648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 42748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 42848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 42948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 43048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 43117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @Test 43217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @MediumTest 43342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas public void testInvisible() throws Throwable { 43417fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki // Make the bottomsheet invisible 43542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 43617fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @Override 43717fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki public void run() { 43817fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki getBottomSheet().setVisibility(View.INVISIBLE); 43917fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 44017fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 44117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki }); 44217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki // Swipe up as if to expand it 44317fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 44417fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki .perform(DesignViewActions.withCustomConstraints( 44517fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki new GeneralSwipeAction(Swipe.FAST, 44617fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki GeneralLocation.VISIBLE_CENTER, new CoordinatesProvider() { 44717fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @Override 44817fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki public float[] calculateCoordinates(View view) { 44917fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki return new float[]{view.getWidth() / 2, 0}; 45017fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 45117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki }, Press.FINGER), 45217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki not(ViewMatchers.isDisplayed()))); 45317fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki // Check that the bottom sheet stays the same collapsed state 45442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 45517fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki @Override 45617fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki public void run() { 45717fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 45817fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 45917fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki }); 46017fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 46117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki 462b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Test 463b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @MediumTest 46442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas public void testInvisibleThenVisible() throws Throwable { 46542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 4666cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki @Override 4676cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki public void run() { 4686cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki // The bottom sheet is initially invisible 4696cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki getBottomSheet().setVisibility(View.INVISIBLE); 4706cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki // Then it becomes visible when the CoL is touched 4716cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki getCoordinatorLayout().setOnTouchListener(new View.OnTouchListener() { 4726cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki @Override 4736cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki public boolean onTouch(View view, MotionEvent e) { 4746cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki if (e.getAction() == MotionEvent.ACTION_DOWN) { 4756cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki getBottomSheet().setVisibility(View.VISIBLE); 4766cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki return true; 4776cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki } 4786cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki return false; 4796cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki } 4806cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki }); 4816cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 4826cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki } 4836cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki }); 4846cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki // Drag over the CoL 4856cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.coordinator)) 4866cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki // Drag (and not release) 4876cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki .perform(new DragAction( 4886cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki GeneralLocation.BOTTOM_CENTER, 4896cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki GeneralLocation.TOP_CENTER, 4906cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki Press.FINGER)) 4916cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki .check(new ViewAssertion() { 4926cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki @Override 4936cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki public void check(View view, NoMatchingViewException e) { 4946cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki // The bottom sheet should not react to the touch events 4956cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki assertThat(getBottomSheet(), is(ViewMatchers.isDisplayed())); 4966cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki assertThat(getBehavior().getState(), 4976cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki is(BottomSheetBehavior.STATE_COLLAPSED)); 4986cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki } 4996cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki }); 5006cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki } 5016cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki 5026cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki @Test 5036cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki @MediumTest 50442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas public void testNestedScroll() throws Throwable { 505b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki final ViewGroup bottomSheet = getBottomSheet(); 506b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki final BottomSheetBehavior behavior = getBehavior(); 507b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki final NestedScrollView scroll = new NestedScrollView(mActivityTestRule.getActivity()); 508b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // Set up nested scrolling area 50942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 510b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 511b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void run() { 512b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki bottomSheet.addView(scroll, new ViewGroup.LayoutParams( 513b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 5148be0c0d616f707a5c1e794a830a44bce7c1d75dfYuichi Araki final View view = new View(mActivityTestRule.getActivity()); 5158be0c0d616f707a5c1e794a830a44bce7c1d75dfYuichi Araki // Make sure that the NestedScrollView is always scrollable 5168be0c0d616f707a5c1e794a830a44bce7c1d75dfYuichi Araki view.setMinimumHeight(bottomSheet.getHeight() + 1000); 517b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki scroll.addView(view); 518b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 519b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // The scroll offset is 0 at first 520b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(scroll.getScrollY(), is(0)); 521b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 522b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }); 523b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // Swipe from the very bottom of the bottom sheet to the top edge of the screen so that the 524b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // scrolling content is also scrolled 525b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.coordinator)) 526a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki .perform(new GeneralSwipeAction(Swipe.SLOW, 527b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki new CoordinatesProvider() { 528b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 529b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public float[] calculateCoordinates(View view) { 530b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki return new float[]{view.getWidth() / 2, view.getHeight() - 1}; 531b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 532b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }, 533b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki new CoordinatesProvider() { 534b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 535b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public float[] calculateCoordinates(View view) { 536b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki return new float[]{view.getWidth() / 2, 1}; 537b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 538b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }, Press.FINGER)); 539b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki registerIdlingResourceCallback(); 540b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki try { 541b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 542b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 543acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 544b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 545b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void run() { 546b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_EXPANDED)); 547b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // This confirms that the nested scrolling area was scrolled continuously after 548b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // the bottom sheet is expanded. 549b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(scroll.getScrollY(), is(not(0))); 550b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 551b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }); 552b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } finally { 553b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki unregisterIdlingResourceCallback(); 554b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 555b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 556b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki 5576bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Test 5586bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @MediumTest 559912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki public void testDragOutside() { 560912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Swipe up outside of the bottom sheet 561912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki Espresso.onView(ViewMatchers.withId(R.id.coordinator)) 562912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki .perform(DesignViewActions.withCustomConstraints( 563912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki new GeneralSwipeAction(Swipe.FAST, 564912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Just above the bottom sheet 565912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki new CoordinatesProvider() { 566912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki @Override 567912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki public float[] calculateCoordinates(View view) { 568912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki return new float[]{ 569912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki view.getWidth() / 2, 570912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki view.getHeight() - getBehavior().getPeekHeight() - 9 571912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki }; 572912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 573912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki }, 574912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Top of the CoordinatorLayout 575912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki new CoordinatesProvider() { 576912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki @Override 577912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki public float[] calculateCoordinates(View view) { 578912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki return new float[]{view.getWidth() / 2, 1}; 579912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 580912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki }, Press.FINGER), 581912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki ViewMatchers.isDisplayed())); 582912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki registerIdlingResourceCallback(); 583912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki try { 584912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 585912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 586912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // The bottom sheet should remain collapsed 587912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 588912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } finally { 589912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki unregisterIdlingResourceCallback(); 590912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 591912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 592912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki 5936bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Test 5946bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @MediumTest 5956bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void testLayoutWhileDragging() { 5966bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 5976bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Drag (and not release) 5986bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki .perform(new DragAction( 5996bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki GeneralLocation.VISIBLE_CENTER, 6006bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki GeneralLocation.TOP_CENTER, 6016bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki Press.FINGER)) 6026bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Check that the bottom sheet is in STATE_DRAGGING 6036bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki .check(new ViewAssertion() { 6046bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 6056bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void check(View view, NoMatchingViewException e) { 6066bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki assertThat(view, is(ViewMatchers.isDisplayed())); 6076bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki BottomSheetBehavior behavior = BottomSheetBehavior.from(view); 6086bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_DRAGGING)); 6096bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 6106bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki }) 6116bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Add a new view 6126bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki .perform(new AddViewAction(R.layout.frame_layout)) 6136bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Check that the newly added view is properly laid out 6146bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki .check(new ViewAssertion() { 6156bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 6166bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void check(View view, NoMatchingViewException e) { 6176bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki ViewGroup parent = (ViewGroup) view; 6186bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki assertThat(parent.getChildCount(), is(1)); 6196bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki View child = parent.getChildAt(0); 6206bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki assertThat(ViewCompat.isLaidOut(child), is(true)); 6216bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 6226bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki }); 6236bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 6246bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 6258f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Test 626cea79d768278d2c55f89263f99b8860cc5447aaaAurimas Liutikas @MediumTest 6278f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public void testFabVisibility() { 6288f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki withFabVisibilityChange(false, new Runnable() { 6298f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 6308f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public void run() { 631acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas try { 632acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 633acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas } catch (Throwable throwable) { 634acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas fail(throwable.getMessage()); 635acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas } 6368f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 6378f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki }); 6388f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki withFabVisibilityChange(true, new Runnable() { 6398f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 6408f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public void run() { 641acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas try { 642acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 643acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas } catch (Throwable throwable) { 644acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas fail(throwable.getMessage()); 645acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas } 6468f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 6478f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki }); 6488f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 6498f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 65001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki @Test 651cea79d768278d2c55f89263f99b8860cc5447aaaAurimas Liutikas @MediumTest 65242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas public void testAutoPeekHeight() throws Throwable { 65342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 65401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki @Override 65501ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki public void run() { 65601ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki getBehavior().setPeekHeight(BottomSheetBehavior.PEEK_HEIGHT_AUTO); 65701ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 65801ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki }); 65942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 66001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki @Override 66101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki public void run() { 662a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki CoordinatorLayout col = getCoordinatorLayout(); 66301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki assertThat(getBottomSheet().getTop(), 664a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki is(Math.min(col.getWidth() * 9 / 16, 665a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki col.getHeight() - getBehavior().getPeekHeightMin()))); 66601ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 66701ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki }); 66801ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 66901ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki 670bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki @Test 671e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki @MediumTest 67242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas public void testAutoPeekHeightHide() throws Throwable { 67342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 674e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki @Override 675e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki public void run() { 676e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki getBehavior().setHideable(true); 677e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki getBehavior().setPeekHeight(0); 678e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki getBehavior().setPeekHeight(BottomSheetBehavior.PEEK_HEIGHT_AUTO); 679e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki } 680e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki }); 681e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki checkSetState(BottomSheetBehavior.STATE_HIDDEN, not(ViewMatchers.isDisplayed())); 682e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki } 683e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki 684e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki @Test 685cea79d768278d2c55f89263f99b8860cc5447aaaAurimas Liutikas @MediumTest 686acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas public void testDynamicContent() throws Throwable { 687bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki registerIdlingResourceCallback(); 688bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki try { 689acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 690bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki @Override 691bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki public void run() { 692bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki ViewGroup.LayoutParams params = getBottomSheet().getLayoutParams(); 693bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki params.height = ViewGroup.LayoutParams.WRAP_CONTENT; 694bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki getBottomSheet().setLayoutParams(params); 695bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki View view = new View(getBottomSheet().getContext()); 696bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki int size = getBehavior().getPeekHeight() * 2; 697bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki getBottomSheet().addView(view, new ViewGroup.LayoutParams(size, size)); 698bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki assertThat(getBottomSheet().getChildCount(), is(1)); 699bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki // Shrink the content height. 700bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki ViewGroup.LayoutParams lp = view.getLayoutParams(); 701bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki lp.height = (int) (size * 0.8); 702bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki view.setLayoutParams(lp); 703bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki // Immediately expand the bottom sheet. 704bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki getBehavior().setState(BottomSheetBehavior.STATE_EXPANDED); 705bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } 706bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki }); 707bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 708bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 709bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_EXPANDED)); 710bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki // Make sure that the bottom sheet is not floating above the bottom. 711bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki assertThat(getBottomSheet().getBottom(), is(getCoordinatorLayout().getBottom())); 712bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } finally { 713bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki unregisterIdlingResourceCallback(); 714bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } 715bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } 716bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki 7175347bf6b48710e49eb2058403329bf821883959fYuichi Araki @Test 7185347bf6b48710e49eb2058403329bf821883959fYuichi Araki @MediumTest 7195347bf6b48710e49eb2058403329bf821883959fYuichi Araki public void testExpandedPeekHeight() throws Throwable { 7205347bf6b48710e49eb2058403329bf821883959fYuichi Araki mActivityTestRule.runOnUiThread(new Runnable() { 7215347bf6b48710e49eb2058403329bf821883959fYuichi Araki @Override 7225347bf6b48710e49eb2058403329bf821883959fYuichi Araki public void run() { 7235347bf6b48710e49eb2058403329bf821883959fYuichi Araki // Make the peek height as tall as the bottom sheet. 7245347bf6b48710e49eb2058403329bf821883959fYuichi Araki BottomSheetBehavior behavior = getBehavior(); 7255347bf6b48710e49eb2058403329bf821883959fYuichi Araki behavior.setPeekHeight(getBottomSheet().getHeight()); 7265347bf6b48710e49eb2058403329bf821883959fYuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 7275347bf6b48710e49eb2058403329bf821883959fYuichi Araki } 7285347bf6b48710e49eb2058403329bf821883959fYuichi Araki }); 7295347bf6b48710e49eb2058403329bf821883959fYuichi Araki // Both of these will not animate the sheet , but the state should be changed. 7305347bf6b48710e49eb2058403329bf821883959fYuichi Araki checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 7315347bf6b48710e49eb2058403329bf821883959fYuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 7325347bf6b48710e49eb2058403329bf821883959fYuichi Araki } 7335347bf6b48710e49eb2058403329bf821883959fYuichi Araki 73452151cc4906f7a1f5925fcee872fa915606590c7Chris Banes @Test 73552151cc4906f7a1f5925fcee872fa915606590c7Chris Banes @SmallTest 73647a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire public void testFindScrollingChildEnabled() { 73747a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire Context context = mActivityTestRule.getActivity(); 73847a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire NestedScrollView disabledParent = new NestedScrollView(context); 73947a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire disabledParent.setNestedScrollingEnabled(false); 74047a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire NestedScrollView enabledChild = new NestedScrollView(context); 74147a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire enabledChild.setNestedScrollingEnabled(true); 74247a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire disabledParent.addView(enabledChild); 74347a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire 74447a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire View scrollingChild = getBehavior().findScrollingChild(disabledParent); 74547a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire assertThat(scrollingChild, is((View) enabledChild)); 74647a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire } 74747a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire 748acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas private void checkSetState(final int state, Matcher<View> matcher) throws Throwable { 74948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki registerIdlingResourceCallback(); 75048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 751acf7e812c17c73a27c5e5973dde80e1c59712469Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 752ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov @Override 753ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov public void run() { 754ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov getBehavior().setState(state); 755ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov } 756ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov }); 75748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 75848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(matcher)); 75948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(state)); 76048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 76148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 76248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 76348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 76448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 76548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private void registerIdlingResourceCallback() { 766e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki // This cannot be done in setUp(), or swiping action cannot be executed. 76748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mCallback = new Callback(getBehavior()); 76848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.registerIdlingResources(mCallback); 76948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 77048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 77148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private void unregisterIdlingResourceCallback() { 77248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki if (mCallback != null) { 77348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.unregisterIdlingResources(mCallback); 77448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mCallback = null; 77548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 77648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 77748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 7788f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki private void withFabVisibilityChange(boolean shown, Runnable action) { 7798f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki OnVisibilityChangedListener listener = new OnVisibilityChangedListener(shown); 7808f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki CoordinatorLayout.LayoutParams lp = 7818f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki (CoordinatorLayout.LayoutParams) mActivityTestRule.getActivity().mFab 7828f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki .getLayoutParams(); 7838f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki FloatingActionButton.Behavior behavior = (FloatingActionButton.Behavior) lp.getBehavior(); 7848f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki behavior.setInternalAutoHideListener(listener); 7858f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki Espresso.registerIdlingResources(listener); 7868f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki try { 7878f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki action.run(); 7888f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } finally { 7898f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki Espresso.unregisterIdlingResources(listener); 7908f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 7918f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 7928f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 79348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private ViewGroup getBottomSheet() { 794ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov return mActivityTestRule.getActivity().mBottomSheet; 79548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 79648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 79748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private BottomSheetBehavior getBehavior() { 798ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov return mActivityTestRule.getActivity().mBehavior; 79948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 80048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 80148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private CoordinatorLayout getCoordinatorLayout() { 802ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov return mActivityTestRule.getActivity().mCoordinatorLayout; 80348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 80448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 80548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki} 806