1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.appcompat.app;
18
19import static android.support.test.espresso.Espresso.onView;
20import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
21
22import static androidx.appcompat.testutils.DrawerLayoutActions.openDrawer;
23
24import static org.hamcrest.CoreMatchers.equalTo;
25import static org.hamcrest.CoreMatchers.is;
26import static org.hamcrest.MatcherAssert.assertThat;
27
28import android.app.Activity;
29import android.content.Context;
30import android.support.test.InstrumentationRegistry;
31import android.support.test.filters.LargeTest;
32import android.support.test.rule.ActivityTestRule;
33import android.support.test.runner.AndroidJUnit4;
34import android.view.Gravity;
35import android.view.InputDevice;
36import android.view.MotionEvent;
37import android.view.View;
38import android.view.ViewGroup;
39
40import androidx.core.view.GravityCompat;
41import androidx.drawerlayout.widget.DrawerLayout;
42
43import org.junit.Before;
44import org.junit.Rule;
45import org.junit.Test;
46import org.junit.runner.RunWith;
47
48import java.util.concurrent.CountDownLatch;
49import java.util.concurrent.TimeUnit;
50
51@RunWith(AndroidJUnit4.class)
52public class DrawerInteractionTest {
53
54    @Rule
55    public final ActivityTestRule<DrawerInteractionActivity> mActivityTestRule =
56            new ActivityTestRule<>(DrawerInteractionActivity.class);
57
58    private TestDrawerLayout mDrawerLayout;
59    private MockView mStartDrawer;
60    private MockView mContentView;
61
62    @Before
63    public void setUp() throws Throwable {
64        final Activity activity = mActivityTestRule.getActivity();
65
66        Context context = InstrumentationRegistry.getContext();
67        mDrawerLayout = new TestDrawerLayout(InstrumentationRegistry.getContext());
68        mContentView = new MockView(context);
69        mStartDrawer = new MockView(context);
70
71        mDrawerLayout.setLayoutParams(new ViewGroup.LayoutParams(100, 100));
72        DrawerLayout.LayoutParams contentViewLayoutParams = new DrawerLayout.LayoutParams(100, 100);
73        DrawerLayout.LayoutParams drawerViewLayoutParams = new DrawerLayout.LayoutParams(50, 100);
74
75        contentViewLayoutParams.gravity = Gravity.NO_GRAVITY;
76        drawerViewLayoutParams.gravity = Gravity.START;
77
78        mContentView.setLayoutParams(contentViewLayoutParams);
79        mStartDrawer.setLayoutParams(drawerViewLayoutParams);
80        mStartDrawer.setBackgroundColor(0xFF00FF00);
81
82        mDrawerLayout.addView(mContentView);
83        mDrawerLayout.addView(mStartDrawer);
84        mDrawerLayout.setBackgroundColor(0xFFFF0000);
85
86        mDrawerLayout.expectLayouts(1);
87        mActivityTestRule.runOnUiThread(new Runnable() {
88            @Override
89            public void run() {
90                activity.setContentView(mDrawerLayout);
91            }
92        });
93        assertThat(mDrawerLayout.waitForLayouts(2), is(true));
94    }
95
96    // Verification that DrawerLayout blocks certain pointer motion events from getting to the
97    // content view when the drawer is open.
98
99    @Test
100    @LargeTest
101    public void ui_pointerEvent_overDrawer_receivedByDrawer() throws Throwable {
102        onView(isAssignableFrom(DrawerLayout.class)).perform(openDrawer(
103                GravityCompat.START));
104        MotionEvent motionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_SCROLL, 25, 50, 0);
105        motionEvent.setSource(InputDevice.SOURCE_CLASS_POINTER);
106
107        mDrawerLayout.dispatchGenericMotionEvent(motionEvent);
108
109        assertThat(mStartDrawer.mMotionEventPassedToDispatchGenericMotionEvent,
110                is(equalTo(motionEvent)));
111    }
112
113    @Test
114    @LargeTest
115    public void ui_pointerEvent_overDrawer_notReceivedByContent() throws Throwable {
116        onView(isAssignableFrom(DrawerLayout.class)).perform(openDrawer(
117                GravityCompat.START));
118        MotionEvent motionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_SCROLL, 25, 50, 0);
119        motionEvent.setSource(InputDevice.SOURCE_CLASS_POINTER);
120
121        mDrawerLayout.dispatchGenericMotionEvent(motionEvent);
122
123        assertThat(mContentView.mDispatchGenericMotionEventCalled, is(false));
124    }
125
126    @Test
127    @LargeTest
128    public void ui_pointerEvent_overContentDrawerOpen_notReceivedByContent() throws Throwable {
129        onView(isAssignableFrom(DrawerLayout.class)).perform(openDrawer(
130                GravityCompat.START));
131        MotionEvent motionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_SCROLL, 75, 50, 0);
132        motionEvent.setSource(InputDevice.SOURCE_CLASS_POINTER);
133
134        mDrawerLayout.dispatchGenericMotionEvent(motionEvent);
135
136        assertThat(mContentView.mDispatchGenericMotionEventCalled, is(false));
137    }
138
139    @Test
140    @LargeTest
141    public void ui_pointerEvent_overContentDrawerClosed_receivedByContent() {
142        MotionEvent motionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_SCROLL, 75, 50, 0);
143        motionEvent.setSource(InputDevice.SOURCE_CLASS_POINTER);
144
145        mDrawerLayout.dispatchGenericMotionEvent(motionEvent);
146
147        assertThat(mContentView.mMotionEventPassedToDispatchGenericMotionEvent,
148                is(equalTo(motionEvent)));
149    }
150
151    private class TestDrawerLayout extends DrawerLayout {
152
153        CountDownLatch mLayoutCountDownLatch;
154
155        TestDrawerLayout(Context context) {
156            super(context);
157        }
158
159        public void expectLayouts(int count) {
160            mLayoutCountDownLatch = new CountDownLatch(count);
161        }
162
163        public boolean waitForLayouts(int seconds) throws InterruptedException {
164            boolean result = mLayoutCountDownLatch.await(seconds, TimeUnit.SECONDS);
165            mLayoutCountDownLatch = null;
166            return result;
167        }
168
169        @Override
170        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
171            super.onLayout(changed, left, top, right, bottom);
172            if (mLayoutCountDownLatch != null) {
173                mLayoutCountDownLatch.countDown();
174            }
175        }
176
177    }
178
179    private class MockView extends View {
180
181        MotionEvent mMotionEventPassedToDispatchGenericMotionEvent;
182        boolean mDispatchGenericMotionEventCalled = false;
183
184        MockView(Context context) {
185            super(context);
186        }
187
188        @Override
189        public boolean dispatchGenericMotionEvent(MotionEvent event) {
190            mMotionEventPassedToDispatchGenericMotionEvent = event;
191            mDispatchGenericMotionEventCalled = true;
192            return super.dispatchGenericMotionEvent(event);
193        }
194    }
195
196
197}
198