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