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 208f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Arakiimport static org.hamcrest.CoreMatchers.is; 218f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Arakiimport static org.hamcrest.CoreMatchers.not; 228f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Arakiimport static org.hamcrest.MatcherAssert.assertThat; 23e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Arakiimport static org.hamcrest.Matchers.greaterThanOrEqualTo; 24e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Arakiimport static org.hamcrest.Matchers.lessThanOrEqualTo; 258f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 266bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.os.SystemClock; 276bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.annotation.LayoutRes; 28dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Arakiimport android.support.annotation.NonNull; 2948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.support.design.test.R; 30ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikovimport android.support.test.InstrumentationRegistry; 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; 486bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Arakiimport android.support.v4.view.ViewCompat; 49b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Arakiimport android.support.v4.widget.NestedScrollView; 5048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.test.suitebuilder.annotation.MediumTest; 5148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakiimport android.test.suitebuilder.annotation.SmallTest; 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 Arakiimport android.widget.TextView; 58b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki 59ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikovimport org.hamcrest.Matcher; 606cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Arakiimport org.hamcrest.Matchers; 61ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikovimport org.junit.Test; 6248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 6348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Arakipublic class BottomSheetBehaviorTest extends 6448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki BaseInstrumentationTestCase<BottomSheetBehaviorActivity> { 6548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 6648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public static class Callback extends BottomSheetBehavior.BottomSheetCallback 6748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki implements IdlingResource { 6848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 6948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private boolean mIsIdle; 7048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 7148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private IdlingResource.ResourceCallback mResourceCallback; 7248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 7348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public Callback(BottomSheetBehavior behavior) { 7448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki behavior.setBottomSheetCallback(this); 7548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki int state = behavior.getState(); 7648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mIsIdle = isIdleState(state); 7748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 7848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 7948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 80dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public void onStateChanged(@NonNull View bottomSheet, 81dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki @BottomSheetBehavior.State int newState) { 8248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki boolean wasIdle = mIsIdle; 8348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mIsIdle = isIdleState(newState); 8448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki if (!wasIdle && mIsIdle && mResourceCallback != null) { 8548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mResourceCallback.onTransitionToIdle(); 8648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 8748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 8848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 8948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 90dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public void onSlide(@NonNull View bottomSheet, float slideOffset) { 91e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki assertThat(slideOffset, is(greaterThanOrEqualTo(-1f))); 92e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki assertThat(slideOffset, is(lessThanOrEqualTo(1f))); 9348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 9448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 9548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 9648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public String getName() { 9748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki return Callback.class.getSimpleName(); 9848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 9948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 10048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 10148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public boolean isIdleNow() { 10248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki return mIsIdle; 10348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 10448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 10548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Override 10648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void registerIdleTransitionCallback(IdlingResource.ResourceCallback callback) { 10748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mResourceCallback = callback; 10848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 10948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 11048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private boolean isIdleState(int state) { 11148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki return state != BottomSheetBehavior.STATE_DRAGGING && 11248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki state != BottomSheetBehavior.STATE_SETTLING; 11348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 11448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 11548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 11648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 1176bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki /** 1188f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki * Wait for a FAB to change its visibility (either shown or hidden). 1198f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki */ 1208f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki private static class OnVisibilityChangedListener extends 1218f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki FloatingActionButton.OnVisibilityChangedListener implements IdlingResource { 1228f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1238f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki private final boolean mShown; 1248f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki private boolean mIsIdle; 1258f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki private ResourceCallback mResourceCallback; 1268f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1278f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki OnVisibilityChangedListener(boolean shown) { 1288f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki mShown = shown; 1298f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1308f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1318f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki private void transitionToIdle() { 1328f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki if (!mIsIdle) { 1338f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki mIsIdle = true; 1348f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki if (mResourceCallback != null) { 1358f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki mResourceCallback.onTransitionToIdle(); 1368f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1378f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1388f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1398f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1408f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 1418f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public void onShown(FloatingActionButton fab) { 1428f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki if (mShown) { 1438f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki transitionToIdle(); 1448f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1458f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1468f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1478f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 1488f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public void onHidden(FloatingActionButton fab) { 1498f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki if (!mShown) { 1508f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki transitionToIdle(); 1518f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1528f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1538f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1548f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 1558f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public String getName() { 1568f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki return OnVisibilityChangedListener.class.getSimpleName(); 1578f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1588f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1598f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 1608f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public boolean isIdleNow() { 1618f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki return mIsIdle; 1628f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1638f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1648f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 1658f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { 1668f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki mResourceCallback = resourceCallback; 1678f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1688f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 1698f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 1708f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki /** 1716bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki * This is like {@link GeneralSwipeAction}, but it does not send ACTION_UP at the end. 1726bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki */ 1736bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static class DragAction implements ViewAction { 1746bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1756bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static final int STEPS = 10; 1766bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static final int DURATION = 100; 1776bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1786bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private final CoordinatesProvider mStart; 1796bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private final CoordinatesProvider mEnd; 1806bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private final PrecisionDescriber mPrecisionDescriber; 1816bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1826bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public DragAction(CoordinatesProvider start, CoordinatesProvider end, 1836bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki PrecisionDescriber precisionDescriber) { 1846bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki mStart = start; 1856bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki mEnd = end; 1866bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki mPrecisionDescriber = precisionDescriber; 1876bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1886bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1896bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 1906bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public Matcher<View> getConstraints() { 1916cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki return Matchers.any(View.class); 1926bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1936bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1946bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 1956bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public String getDescription() { 1966bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return "drag"; 1976bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 1986bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 1996bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 2006bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void perform(UiController uiController, View view) { 2016bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[] precision = mPrecisionDescriber.describePrecision(); 2026bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[] start = mStart.calculateCoordinates(view); 2036bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[] end = mEnd.calculateCoordinates(view); 2046bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[][] steps = interpolate(start, end, STEPS); 2056bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki int delayBetweenMovements = DURATION / steps.length; 2066bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Down 2076bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki MotionEvent downEvent = MotionEvents.sendDown(uiController, start, precision).down; 2086bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki try { 2096bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki for (int i = 0; i < steps.length; i++) { 2106bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Wait 2116bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki long desiredTime = downEvent.getDownTime() + (long)(delayBetweenMovements * i); 2126bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki long timeUntilDesired = desiredTime - SystemClock.uptimeMillis(); 2136bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki if (timeUntilDesired > 10L) { 2146bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki uiController.loopMainThreadForAtLeast(timeUntilDesired); 2156bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2166bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Move 2176bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki if (!MotionEvents.sendMovement(uiController, downEvent, steps[i])) { 2186bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki MotionEvents.sendCancel(uiController, downEvent); 2196bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki throw new RuntimeException("Cannot drag: failed to send a move event."); 2206bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2216bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2226bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki int duration = ViewConfiguration.getPressedStateDuration(); 2236bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki if (duration > 0) { 2246bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki uiController.loopMainThreadForAtLeast((long) duration); 2256bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2266bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } finally { 2276bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki downEvent.recycle(); 2286bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2296bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2306bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2316bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static float[][] interpolate(float[] start, float[] end, int steps) { 2326bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki Preconditions.checkElementIndex(1, start.length); 2336bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki Preconditions.checkElementIndex(1, end.length); 2346bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki float[][] res = new float[steps][2]; 2356bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki for(int i = 1; i < steps + 1; ++i) { 2366bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki res[i - 1][0] = start[0] + (end[0] - start[0]) * (float)i / ((float)steps + 2.0F); 2376bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki res[i - 1][1] = start[1] + (end[1] - start[1]) * (float)i / ((float)steps + 2.0F); 2386bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2396bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return res; 2406bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2416bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2426bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2436bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private static class AddViewAction implements ViewAction { 2446bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2456bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki private final int mLayout; 2466bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2476bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public AddViewAction(@LayoutRes int layout) { 2486bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki mLayout = layout; 2496bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2506bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2516bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 2526bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public Matcher<View> getConstraints() { 2536bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return ViewMatchers.isAssignableFrom(ViewGroup.class); 2546bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2556bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2566bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 2576bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public String getDescription() { 2586bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki return "add view"; 2596bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2606bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 2616bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 2626bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void perform(UiController uiController, View view) { 2636bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki ViewGroup parent = (ViewGroup) view; 2646bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki View child = LayoutInflater.from(view.getContext()).inflate(mLayout, parent, false); 2656bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki parent.addView(child); 2666bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2676bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 2686bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 26948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private Callback mCallback; 27048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 27148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public BottomSheetBehaviorTest() { 27248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki super(BottomSheetBehaviorActivity.class); 27348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 27448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 27548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 27648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @SmallTest 27748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testInitialSetup() { 27848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki BottomSheetBehavior behavior = getBehavior(); 27948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 28048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki CoordinatorLayout coordinatorLayout = getCoordinatorLayout(); 28148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki ViewGroup bottomSheet = getBottomSheet(); 28248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(bottomSheet.getTop(), 28348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki is(coordinatorLayout.getHeight() - behavior.getPeekHeight())); 28448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 28548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 28648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 28748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 28848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSetStateExpandedToCollapsed() { 28948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 29048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 29148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 29248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 29348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 29448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 29548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSetStateHiddenToCollapsed() { 29648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_HIDDEN, not(ViewMatchers.isDisplayed())); 29748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 29848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 29948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 30048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 30148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 302e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki public void testSetStateCollapsedToCollapsed() { 303e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 304e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki } 305e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki 306e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki @Test 307e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki @MediumTest 30848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSwipeDownToCollapse() { 30948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 31048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 311e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki .perform(DesignViewActions.withCustomConstraints(new GeneralSwipeAction( 3128609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki Swipe.FAST, 3138609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki // Manually calculate the starting coordinates to make sure that the touch 3148609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki // actually falls onto the view on Gingerbread 3158609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki new CoordinatesProvider() { 3168609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki @Override 3178609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki public float[] calculateCoordinates(View view) { 3188609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki int[] location = new int[2]; 3198609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki view.getLocationInWindow(location); 3208609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki return new float[]{ 3218609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki view.getWidth() / 2, 3228609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki location[1] + 1 3238609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki }; 3248609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki } 3258609a20bfe8d61704facb9c18836db5b3a754105Yuichi Araki }, 326e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // Manually calculate the ending coordinates to make sure that the bottom 327e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // sheet is collapsed, not hidden 328e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki new CoordinatesProvider() { 329e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki @Override 330e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki public float[] calculateCoordinates(View view) { 331e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki BottomSheetBehavior behavior = getBehavior(); 332e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki return new float[]{ 333e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // x: center of the bottom sheet 334e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki view.getWidth() / 2, 335e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki // y: just above the peek height 336e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki view.getHeight() - behavior.getPeekHeight()}; 337e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki } 338e1b07825c14a3dee006a0489fd8e2c3060d9884fYuichi Araki }, Press.FINGER), ViewMatchers.isDisplayingAtLeast(5))); 33948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki registerIdlingResourceCallback(); 34048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 34148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 34248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 34348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 34448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 34548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 34648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 34748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 34848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 34948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 35048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @MediumTest 35148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki public void testSwipeDownToHide() { 35248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 35348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .perform(DesignViewActions.withCustomConstraints(ViewActions.swipeDown(), 35448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki ViewMatchers.isDisplayingAtLeast(5))); 355d7e25b5cfb70b867b7467acb613a984310a0c40dYuichi Araki registerIdlingResourceCallback(); 35648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 35748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 35848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))); 35948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_HIDDEN)); 36048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 36148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 36248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 36348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 36448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 36548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki @Test 366362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki public void testSkipCollapsed() { 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 433ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov public void testInvisible() { 43417fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki // Make the bottomsheet invisible 435ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov InstrumentationRegistry.getInstrumentation().runOnMainSync(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 454ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov InstrumentationRegistry.getInstrumentation().runOnMainSync(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 4646cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki public void testInvisibleThenVisible() { 4656cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki InstrumentationRegistry.getInstrumentation().runOnMainSync(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 504b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void testNestedScroll() { 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 509b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki InstrumentationRegistry.getInstrumentation().runOnMainSync(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)); 514b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki TextView view = new TextView(mActivityTestRule.getActivity()); 515b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki StringBuilder sb = new StringBuilder(); 516b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki for (int i = 0; i < 500; ++i) { 517b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki sb.append("It is fine today. "); 518b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 519b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki view.setText(sb); 520b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki view.setOnClickListener(new View.OnClickListener() { 521b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 522b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void onClick(View v) { 523b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // Do nothing 524b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 525b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }); 526b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki scroll.addView(view); 527b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 528b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // The scroll offset is 0 at first 529b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(scroll.getScrollY(), is(0)); 530b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 531b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }); 532b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // Swipe from the very bottom of the bottom sheet to the top edge of the screen so that the 533b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // scrolling content is also scrolled 534b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.coordinator)) 535a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki .perform(new GeneralSwipeAction(Swipe.SLOW, 536b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki new CoordinatesProvider() { 537b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 538b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public float[] calculateCoordinates(View view) { 539b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki return new float[]{view.getWidth() / 2, view.getHeight() - 1}; 540b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 541b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }, 542b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki new CoordinatesProvider() { 543b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 544b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public float[] calculateCoordinates(View view) { 545b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki return new float[]{view.getWidth() / 2, 1}; 546b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 547b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }, Press.FINGER)); 548b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki registerIdlingResourceCallback(); 549b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki try { 550b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 551b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 552b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 553b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki @Override 554b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki public void run() { 555b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_EXPANDED)); 556b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // This confirms that the nested scrolling area was scrolled continuously after 557b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki // the bottom sheet is expanded. 558b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki assertThat(scroll.getScrollY(), is(not(0))); 559b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 560b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki }); 561b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } finally { 562b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki unregisterIdlingResourceCallback(); 563b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 564b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 565b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki 5666bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Test 5676bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @MediumTest 568912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki public void testDragOutside() { 569912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Swipe up outside of the bottom sheet 570912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki Espresso.onView(ViewMatchers.withId(R.id.coordinator)) 571912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki .perform(DesignViewActions.withCustomConstraints( 572912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki new GeneralSwipeAction(Swipe.FAST, 573912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Just above the bottom sheet 574912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki new CoordinatesProvider() { 575912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki @Override 576912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki public float[] calculateCoordinates(View view) { 577912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki return new float[]{ 578912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki view.getWidth() / 2, 579912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki view.getHeight() - getBehavior().getPeekHeight() - 9 580912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki }; 581912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 582912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki }, 583912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // Top of the CoordinatorLayout 584912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki new CoordinatesProvider() { 585912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki @Override 586912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki public float[] calculateCoordinates(View view) { 587912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki return new float[]{view.getWidth() / 2, 1}; 588912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 589912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki }, Press.FINGER), 590912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki ViewMatchers.isDisplayed())); 591912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki registerIdlingResourceCallback(); 592912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki try { 593912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 594912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 595912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki // The bottom sheet should remain collapsed 596912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED)); 597912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } finally { 598912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki unregisterIdlingResourceCallback(); 599912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 600912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki } 601912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki 6026bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Test 6036bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @MediumTest 6046bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void testLayoutWhileDragging() { 6056bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 6066bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Drag (and not release) 6076bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki .perform(new DragAction( 6086bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki GeneralLocation.VISIBLE_CENTER, 6096bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki GeneralLocation.TOP_CENTER, 6106bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki Press.FINGER)) 6116bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Check that the bottom sheet is in STATE_DRAGGING 6126bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki .check(new ViewAssertion() { 6136bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 6146bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void check(View view, NoMatchingViewException e) { 6156bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki assertThat(view, is(ViewMatchers.isDisplayed())); 6166bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki BottomSheetBehavior behavior = BottomSheetBehavior.from(view); 6176bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_DRAGGING)); 6186bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 6196bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki }) 6206bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Add a new view 6216bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki .perform(new AddViewAction(R.layout.frame_layout)) 6226bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // Check that the newly added view is properly laid out 6236bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki .check(new ViewAssertion() { 6246bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki @Override 6256bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki public void check(View view, NoMatchingViewException e) { 6266bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki ViewGroup parent = (ViewGroup) view; 6276bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki assertThat(parent.getChildCount(), is(1)); 6286bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki View child = parent.getChildAt(0); 6296bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki assertThat(ViewCompat.isLaidOut(child), is(true)); 6306bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 6316bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki }); 6326bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } 6336bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki 6348f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Test 6358f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public void testFabVisibility() { 6368f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki withFabVisibilityChange(false, new Runnable() { 6378f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 6388f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public void run() { 6398f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed()); 6408f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 6418f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki }); 6428f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki withFabVisibilityChange(true, new Runnable() { 6438f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki @Override 6448f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki public void run() { 6458f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed()); 6468f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 6478f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki }); 6488f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 6498f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 65001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki @Test 65101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki public void testAutoPeekHeight() { 65201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 65301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki @Override 65401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki public void run() { 65501ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki getBehavior().setPeekHeight(BottomSheetBehavior.PEEK_HEIGHT_AUTO); 65601ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 65701ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki }); 65801ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 65901ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki @Override 66001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki public void run() { 661a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki CoordinatorLayout col = getCoordinatorLayout(); 66201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki assertThat(getBottomSheet().getTop(), 663a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki is(Math.min(col.getWidth() * 9 / 16, 664a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki col.getHeight() - getBehavior().getPeekHeightMin()))); 66501ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 66601ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki }); 66701ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 66801ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki 669bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki @Test 670e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki @MediumTest 671e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki public void testAutoPeekHeightHide() { 672e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 673e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki @Override 674e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki public void run() { 675e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki getBehavior().setHideable(true); 676e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki getBehavior().setPeekHeight(0); 677e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki getBehavior().setPeekHeight(BottomSheetBehavior.PEEK_HEIGHT_AUTO); 678e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki } 679e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki }); 680e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki checkSetState(BottomSheetBehavior.STATE_HIDDEN, not(ViewMatchers.isDisplayed())); 681e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki } 682e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki 683e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki @Test 684bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki public void testDynamicContent() { 685bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki registerIdlingResourceCallback(); 686bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki try { 687bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 688bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki @Override 689bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki public void run() { 690bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki ViewGroup.LayoutParams params = getBottomSheet().getLayoutParams(); 691bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki params.height = ViewGroup.LayoutParams.WRAP_CONTENT; 692bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki getBottomSheet().setLayoutParams(params); 693bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki View view = new View(getBottomSheet().getContext()); 694bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki int size = getBehavior().getPeekHeight() * 2; 695bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki getBottomSheet().addView(view, new ViewGroup.LayoutParams(size, size)); 696bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki assertThat(getBottomSheet().getChildCount(), is(1)); 697bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki // Shrink the content height. 698bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki ViewGroup.LayoutParams lp = view.getLayoutParams(); 699bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki lp.height = (int) (size * 0.8); 700bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki view.setLayoutParams(lp); 701bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki // Immediately expand the bottom sheet. 702bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki getBehavior().setState(BottomSheetBehavior.STATE_EXPANDED); 703bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } 704bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki }); 705bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 706bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 707bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_EXPANDED)); 708bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki // Make sure that the bottom sheet is not floating above the bottom. 709bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki assertThat(getBottomSheet().getBottom(), is(getCoordinatorLayout().getBottom())); 710bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } finally { 711bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki unregisterIdlingResourceCallback(); 712bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } 713bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } 714bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki 715ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov private void checkSetState(final int state, Matcher<View> matcher) { 71648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki registerIdlingResourceCallback(); 71748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki try { 718ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 719ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov @Override 720ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov public void run() { 721ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov getBehavior().setState(state); 722ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov } 723ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov }); 72448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet)) 72548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki .check(ViewAssertions.matches(matcher)); 72648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki assertThat(getBehavior().getState(), is(state)); 72748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } finally { 72848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki unregisterIdlingResourceCallback(); 72948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 73048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 73148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 73248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private void registerIdlingResourceCallback() { 733e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki // This cannot be done in setUp(), or swiping action cannot be executed. 73448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mCallback = new Callback(getBehavior()); 73548cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.registerIdlingResources(mCallback); 73648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 73748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 73848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private void unregisterIdlingResourceCallback() { 73948cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki if (mCallback != null) { 74048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki Espresso.unregisterIdlingResources(mCallback); 74148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki mCallback = null; 74248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 74348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 74448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 7458f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki private void withFabVisibilityChange(boolean shown, Runnable action) { 7468f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki OnVisibilityChangedListener listener = new OnVisibilityChangedListener(shown); 7478f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki CoordinatorLayout.LayoutParams lp = 7488f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki (CoordinatorLayout.LayoutParams) mActivityTestRule.getActivity().mFab 7498f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki .getLayoutParams(); 7508f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki FloatingActionButton.Behavior behavior = (FloatingActionButton.Behavior) lp.getBehavior(); 7518f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki behavior.setInternalAutoHideListener(listener); 7528f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki Espresso.registerIdlingResources(listener); 7538f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki try { 7548f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki action.run(); 7558f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } finally { 7568f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki Espresso.unregisterIdlingResources(listener); 7578f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 7588f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki } 7598f8034a4204fc10c4805758051f0cec2159c0e40Yuichi Araki 76048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private ViewGroup getBottomSheet() { 761ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov return mActivityTestRule.getActivity().mBottomSheet; 76248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 76348cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 76448cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private BottomSheetBehavior getBehavior() { 765ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov return mActivityTestRule.getActivity().mBehavior; 76648cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 76748cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 76848cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki private CoordinatorLayout getCoordinatorLayout() { 769ee3e8053ef6b55e31fa0be004061318ec394eb64Kirill Grouchnikov return mActivityTestRule.getActivity().mCoordinatorLayout; 77048cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki } 77148cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki 77248cd60a8daad661611618f52a0dce00d5b2c64b9Yuichi Araki} 773