Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2009 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 
     17 package android.view.cts;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertNotNull;
     21 import static org.junit.Assert.assertNull;
     22 
     23 import android.app.Instrumentation;
     24 import android.os.SystemClock;
     25 import android.support.test.InstrumentationRegistry;
     26 import android.support.test.filters.MediumTest;
     27 import android.support.test.rule.ActivityTestRule;
     28 import android.support.test.runner.AndroidJUnit4;
     29 import android.view.InputDevice;
     30 import android.view.MotionEvent;
     31 
     32 
     33 import org.junit.Before;
     34 import org.junit.Rule;
     35 import org.junit.Test;
     36 import org.junit.runner.RunWith;
     37 
     38 @MediumTest
     39 @RunWith(AndroidJUnit4.class)
     40 public class TouchDelegateTest {
     41     private Instrumentation mInstrumentation;
     42     private TouchDelegateTestActivity mActivity;
     43 
     44     @Rule
     45     public ActivityTestRule<TouchDelegateTestActivity> mActivityRule =
     46             new ActivityTestRule<>(TouchDelegateTestActivity.class);
     47 
     48     @Before
     49     public void setup() throws Throwable {
     50         mActivity = mActivityRule.getActivity();
     51         mActivity.resetCounters();
     52         mInstrumentation = InstrumentationRegistry.getInstrumentation();
     53         mInstrumentation.waitForIdleSync();
     54     }
     55 
     56     @Test
     57     public void testParentClick() {
     58         // If only clicking parent, button should not receive click
     59         clickParent();
     60         assertEquals(0, mActivity.getButtonClickCount());
     61         assertEquals(1, mActivity.getParentClickCount());
     62 
     63         // When clicking TouchDelegate area, both parent and button
     64         // should receive DOWN and UP events. However, click will only be generated for the button
     65         mActivity.resetCounters();
     66         clickTouchDelegateArea();
     67         assertEquals(1, mActivity.getButtonClickCount());
     68         assertEquals(0, mActivity.getParentClickCount());
     69 
     70         // Ensure parent can still receive clicks after TouchDelegate has been activated once
     71         mActivity.resetCounters();
     72         clickParent();
     73         assertEquals(0, mActivity.getButtonClickCount());
     74         assertEquals(1, mActivity.getParentClickCount());
     75     }
     76 
     77     @Test
     78     public void testCancelEvent() {
     79         // Ensure events with ACTION_CANCEL are received by the TouchDelegate
     80         final long downTime = SystemClock.uptimeMillis();
     81         dispatchMotionEventToActivity(MotionEvent.ACTION_DOWN, mActivity.touchDelegateY,
     82                 downTime);
     83         dispatchMotionEventToActivity(MotionEvent.ACTION_CANCEL, mActivity.touchDelegateY,
     84                 downTime);
     85         mInstrumentation.waitForIdleSync();
     86 
     87         ensureOldestActionEquals(MotionEvent.ACTION_DOWN);
     88         ensureOldestActionEquals(MotionEvent.ACTION_CANCEL);
     89 
     90         assertNull(mActivity.removeOldestButtonEvent());
     91         assertEquals(0, mActivity.getButtonClickCount());
     92         assertEquals(0, mActivity.getParentClickCount());
     93     }
     94 
     95     @Test
     96     public void testTwoPointers() {
     97         // Ensure ACTION_POINTER_DOWN and ACTION_POINTER_UP are forwarded to the target view
     98         // by the TouchDelegate
     99         final long downTime = SystemClock.uptimeMillis();
    100         dispatchMotionEventToActivity(MotionEvent.ACTION_DOWN, mActivity.touchDelegateY, downTime);
    101         dispatchMotionEventToActivity(MotionEvent.ACTION_MOVE, mActivity.touchDelegateY, downTime);
    102         int actionPointer1Down =
    103                 (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT) + MotionEvent.ACTION_POINTER_DOWN;
    104         dispatchMultiTouchMotionEventToActivity(actionPointer1Down, 2,
    105                 mActivity.touchDelegateY, downTime);
    106         dispatchMultiTouchMotionEventToActivity(MotionEvent.ACTION_MOVE, 2,
    107                 mActivity.touchDelegateY, downTime);
    108         int actionPointer1Up =
    109                 (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT) + MotionEvent.ACTION_POINTER_UP;
    110         dispatchMultiTouchMotionEventToActivity(actionPointer1Up, 2,
    111                 mActivity.touchDelegateY, downTime);
    112         dispatchMotionEventToActivity(MotionEvent.ACTION_UP, mActivity.touchDelegateY, downTime);
    113         mInstrumentation.waitForIdleSync();
    114 
    115         ensureOldestActionEquals(MotionEvent.ACTION_DOWN);
    116         ensureOldestActionEquals(MotionEvent.ACTION_MOVE);
    117         ensureOldestActionEquals(MotionEvent.ACTION_POINTER_DOWN);
    118         ensureOldestActionEquals(MotionEvent.ACTION_MOVE);
    119         ensureOldestActionEquals(MotionEvent.ACTION_POINTER_UP);
    120         ensureOldestActionEquals(MotionEvent.ACTION_UP);
    121     }
    122 
    123     private void ensureOldestActionEquals(int action) {
    124         MotionEvent event = mActivity.removeOldestButtonEvent();
    125         assertNotNull(event);
    126         assertEquals(action, event.getActionMasked());
    127         event.recycle();
    128     }
    129 
    130     private void clickParent() {
    131         click(mActivity.parentViewY);
    132     }
    133 
    134     private void clickTouchDelegateArea() {
    135         click(mActivity.touchDelegateY);
    136     }
    137 
    138     // Low-level input-handling functions for the activity
    139 
    140     private void click(int y) {
    141         final long downTime = SystemClock.uptimeMillis();
    142         dispatchMotionEventToActivity(MotionEvent.ACTION_DOWN, y, downTime);
    143         dispatchMotionEventToActivity(MotionEvent.ACTION_UP, y, downTime);
    144         mInstrumentation.waitForIdleSync();
    145     }
    146 
    147     private void dispatchMotionEventToActivity(int action, int y, long downTime) {
    148         mActivity.runOnUiThread(() -> {
    149             final long eventTime = SystemClock.uptimeMillis();
    150             final MotionEvent event = MotionEvent.obtain(downTime, eventTime, action,
    151                     mActivity.x, y, 0);
    152             event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
    153             mActivity.dispatchTouchEvent(event);
    154             event.recycle();
    155         });
    156     }
    157 
    158     private void dispatchMultiTouchMotionEventToActivity(int action, int pointerCount,
    159             int y, long downTime) {
    160         mActivity.runOnUiThread(() -> {
    161             final long eventTime = SystemClock.uptimeMillis();
    162             MotionEvent.PointerProperties[] properties =
    163                     new MotionEvent.PointerProperties[pointerCount];
    164             MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[pointerCount];
    165 
    166             for (int i = 0; i < pointerCount; i++) {
    167                 properties[i] = new MotionEvent.PointerProperties();
    168                 properties[i].id = i;
    169                 properties[i].toolType = MotionEvent.TOOL_TYPE_FINGER;
    170                 coords[i] = new MotionEvent.PointerCoords();
    171                 coords[i].x = mActivity.x + i * 10; // small offset so that pointers do not overlap
    172                 coords[i].y = y;
    173             }
    174 
    175             final MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, pointerCount,
    176                     properties, coords, 0, 0, 0, 0,
    177                     0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
    178 
    179             mActivity.dispatchTouchEvent(event);
    180             event.recycle();
    181         });
    182     }
    183 }
    184