12ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi/* 22ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * Copyright (C) 2015 The Android Open Source Project 32ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * 42ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * Licensed under the Apache License, Version 2.0 (the "License"); 52ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * you may not use this file except in compliance with the License. 62ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * You may obtain a copy of the License at 72ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * 82ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * http://www.apache.org/licenses/LICENSE-2.0 92ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * 102ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * Unless required by applicable law or agreed to in writing, software 112ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * distributed under the License is distributed on an "AS IS" BASIS, 122ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * See the License for the specific language governing permissions and 142ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * limitations under the License 152ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi */ 162ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 172ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagipackage android.widget.espresso; 182ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 192ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagiimport org.hamcrest.Matcher; 202ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 212ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagiimport android.support.test.espresso.UiController; 222ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagiimport android.support.test.espresso.ViewAction; 232ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagiimport android.support.test.espresso.action.CoordinatesProvider; 2446faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagiimport android.support.test.espresso.action.MotionEvents; 2546faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagiimport android.support.test.espresso.action.MotionEvents.DownResultHolder; 2646faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagiimport android.support.test.espresso.action.Press; 272ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagiimport android.support.test.espresso.action.Tapper; 28a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagiimport android.view.MotionEvent; 292ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagiimport android.view.View; 3046faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagiimport android.view.ViewConfiguration; 312ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 322ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi/** 332ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * ViewAction for performing an click on View by a mouse. 342ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi */ 352ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagipublic final class MouseClickAction implements ViewAction { 365f318b62853495f286e011de9cb1571e22431314Keisuke Kuroyanagi private final ViewClickAction mViewClickAction; 37a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi @MouseUiController.MouseButton 38a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi private final int mButton; 392ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 4046faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi public enum CLICK implements Tapper { 4146faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi TRIPLE { 4246faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi @Override 4346faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi public Tapper.Status sendTap(UiController uiController, float[] coordinates, 4446faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi float[] precision) { 4546faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi Tapper.Status stat = sendSingleTap(uiController, coordinates, precision); 4646faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi boolean warning = false; 4746faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi if (stat == Tapper.Status.FAILURE) { 4846faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi return Tapper.Status.FAILURE; 4946faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } else if (stat == Tapper.Status.WARNING) { 5046faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi warning = true; 5146faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } 5246faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi 5346faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime(); 5446faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi for (int i = 0; i < 2; i++) { 5546faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi if (0 < doubleTapMinimumTimeout) { 5646faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi uiController.loopMainThreadForAtLeast(doubleTapMinimumTimeout); 5746faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } 5846faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi stat = sendSingleTap(uiController, coordinates, precision); 5946faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi if (stat == Tapper.Status.FAILURE) { 6046faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi return Tapper.Status.FAILURE; 6146faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } else if (stat == Tapper.Status.WARNING) { 6246faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi warning = true; 6346faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } 6446faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } 6546faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi 6646faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi if (warning) { 6746faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi return Tapper.Status.WARNING; 6846faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } else { 6946faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi return Tapper.Status.SUCCESS; 7046faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } 7146faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } 7246faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi }; 7346faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi 7446faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi private static Tapper.Status sendSingleTap(UiController uiController, 7546faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi float[] coordinates, float[] precision) { 7646faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi DownResultHolder res = MotionEvents.sendDown(uiController, coordinates, precision); 7746faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi try { 7846faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi if (!MotionEvents.sendUp(uiController, res.down)) { 7946faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi MotionEvents.sendCancel(uiController, res.down); 8046faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi return Tapper.Status.FAILURE; 8146faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } 8246faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } finally { 8346faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi res.down.recycle(); 8446faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } 8546faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi return res.longPress ? Tapper.Status.WARNING : Tapper.Status.SUCCESS; 8646faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } 8746faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi }; 8846faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi 8946faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider) { 90a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi this(tapper, coordinatesProvider, MotionEvent.BUTTON_PRIMARY); 91a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi } 92a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi 93a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi /** 94a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * Constructs MouseClickAction 95a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * 96a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * @param tapper the tapper 97a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * @param coordinatesProvider the provider of the event coordinates 98a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * @param button the mouse button used to send motion events 99a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi */ 100a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider, 101a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi @MouseUiController.MouseButton int button) { 1025f318b62853495f286e011de9cb1571e22431314Keisuke Kuroyanagi mViewClickAction = new ViewClickAction(tapper, coordinatesProvider, Press.PINPOINT); 103a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi mButton = button; 1042ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi } 1052ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 1062ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi @Override 1072ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi public Matcher<View> getConstraints() { 1085f318b62853495f286e011de9cb1571e22431314Keisuke Kuroyanagi return mViewClickAction.getConstraints(); 1092ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi } 1102ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 1112ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi @Override 1122ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi public String getDescription() { 1135f318b62853495f286e011de9cb1571e22431314Keisuke Kuroyanagi return mViewClickAction.getDescription(); 1142ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi } 1152ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 1162ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi @Override 1172ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi public void perform(UiController uiController, View view) { 1185f318b62853495f286e011de9cb1571e22431314Keisuke Kuroyanagi mViewClickAction.perform(new MouseUiController(uiController, mButton), view); 1192ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi } 1202ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi} 121