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