118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu/*
218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Copyright (C) 2012 The Android Open Source Project
318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu *
418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Licensed under the Apache License, Version 2.0 (the "License");
518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * you may not use this file except in compliance with the License.
618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * You may obtain a copy of the License at
718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu *
818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu *      http://www.apache.org/licenses/LICENSE-2.0
918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu *
1018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Unless required by applicable law or agreed to in writing, software
1118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * distributed under the License is distributed on an "AS IS" BASIS,
1218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * See the License for the specific language governing permissions and
1418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * limitations under the License.
1518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */
1618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
1718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhupackage com.android.uiautomator.core;
1818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
1918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.accessibilityservice.AccessibilityService;
2018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.app.UiAutomation;
2118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.app.UiAutomation.AccessibilityEventFilter;
2218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.graphics.Point;
2318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.os.RemoteException;
2418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.os.SystemClock;
2518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.util.Log;
2618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.view.InputDevice;
2718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.view.InputEvent;
2818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.view.KeyCharacterMap;
2918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.view.KeyEvent;
3018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.view.MotionEvent;
3118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.view.MotionEvent.PointerCoords;
3218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.view.MotionEvent.PointerProperties;
3318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.view.accessibility.AccessibilityEvent;
3418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
3518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.util.ArrayList;
3618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.util.List;
3718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.util.concurrent.TimeoutException;
3818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
3918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu/**
4018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * The InteractionProvider is responsible for injecting user events such as touch events
4118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * (includes swipes) and text key events into the system. To do so, all it needs to know about
4218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * are coordinates of the touch events and text for the text input events.
4318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * The InteractionController performs no synchronization. It will fire touch and text input events
4418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * as fast as it receives them. All idle synchronization is performed prior to querying the
4518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * hierarchy. See {@link QueryController}
4618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */
4718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuclass InteractionController {
4818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
4918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final String LOG_TAG = InteractionController.class.getSimpleName();
5018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
5118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
5218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
5318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private final KeyCharacterMap mKeyCharacterMap =
5418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
5518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
5618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private final UiAutomatorBridge mUiAutomatorBridge;
5718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
5818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final long REGULAR_CLICK_LENGTH = 100;
5918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
6018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private long mDownTime;
6118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
6218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    // Inserted after each motion event injection.
6318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final int MOTION_EVENT_INJECTION_DELAY_MILLIS = 5;
6418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
6518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public InteractionController(UiAutomatorBridge bridge) {
6618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mUiAutomatorBridge = bridge;
6718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
6818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
6918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
7018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Predicate for waiting for any of the events specified in the mask
7118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
7218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    class WaitForAnyEventPredicate implements AccessibilityEventFilter {
7318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int mMask;
7418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        WaitForAnyEventPredicate(int mask) {
7518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            mMask = mask;
7618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
7718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        @Override
7818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        public boolean accept(AccessibilityEvent t) {
7918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // check current event in the list
8018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if ((t.getEventType() & mMask) != 0) {
8118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return true;
8218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
8318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
8418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // no match yet
8518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return false;
8618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
8718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
8818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
8918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
9018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Predicate for waiting for all the events specified in the mask and populating
9118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * a ctor passed list with matching events. User of this Predicate must recycle
9218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * all populated events in the events list.
9318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
9418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    class EventCollectingPredicate implements AccessibilityEventFilter {
9518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int mMask;
9618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        List<AccessibilityEvent> mEventsList;
9718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
9818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        EventCollectingPredicate(int mask, List<AccessibilityEvent> events) {
9918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            mMask = mask;
10018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            mEventsList = events;
10118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
10218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
10318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        @Override
10418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        public boolean accept(AccessibilityEvent t) {
10518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // check current event in the list
10618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if ((t.getEventType() & mMask) != 0) {
10718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // For the events you need, always store a copy when returning false from
10818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // predicates since the original will automatically be recycled after the call.
10918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                mEventsList.add(AccessibilityEvent.obtain(t));
11018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
11118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
11218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // get more
11318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return false;
11418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
11518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
11618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
11718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
11818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Predicate for waiting for every event specified in the mask to be matched at least once
11918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
12018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    class WaitForAllEventPredicate implements AccessibilityEventFilter {
12118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int mMask;
12218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        WaitForAllEventPredicate(int mask) {
12318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            mMask = mask;
12418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
12518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
12618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        @Override
12718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        public boolean accept(AccessibilityEvent t) {
12818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // check current event in the list
12918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if ((t.getEventType() & mMask) != 0) {
13018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // remove from mask since this condition is satisfied
13118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                mMask &= ~t.getEventType();
13218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
13318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // Since we're waiting for all events to be matched at least once
13418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                if (mMask != 0)
13518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    return false;
13618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
13718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // all matched
13818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return true;
13918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
14018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
14118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // no match yet
14218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return false;
14318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
14418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
14518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
14618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
14718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Helper used by methods to perform actions and wait for any accessibility events and return
14818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * predicated on predefined filter.
14918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
15018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param command
15118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param filter
15218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param timeout
15318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return
15418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
15518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private AccessibilityEvent runAndWaitForEvents(Runnable command,
15618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            AccessibilityEventFilter filter, long timeout) {
15718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
15818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        try {
15918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return mUiAutomatorBridge.executeCommandAndWaitForAccessibilityEvent(command, filter,
16018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    timeout);
16118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        } catch (TimeoutException e) {
16218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.w(LOG_TAG, "runAndwaitForEvent timedout waiting for events");
16318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return null;
16418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        } catch (Exception e) {
16518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.e(LOG_TAG, "exception from executeCommandAndWaitForAccessibilityEvent", e);
16618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return null;
16718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
16818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
16918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
17018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
17118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Send keys and blocks until the first specified accessibility event.
17218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
17318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Most key presses will cause some UI change to occur. If the device is busy, this will
17418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * block until the device begins to process the key press at which point the call returns
17518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * and normal wait for idle processing may begin. If no events are detected for the
17618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * timeout period specified, the call will return anyway with false.
17718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
17818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param keyCode
17918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param metaState
18018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param eventType
18118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param timeout
18218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if events is received, otherwise false.
18318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
18418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean sendKeyAndWaitForEvent(final int keyCode, final int metaState,
18518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            final int eventType, long timeout) {
18618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Runnable command = new Runnable() {
18718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            @Override
18818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            public void run() {
18918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                final long eventTime = SystemClock.uptimeMillis();
19018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                KeyEvent downEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN,
19118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                        keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
19218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                        InputDevice.SOURCE_KEYBOARD);
19318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                if (injectEventSync(downEvent)) {
19418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    KeyEvent upEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP,
19518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                            keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
19618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                            InputDevice.SOURCE_KEYBOARD);
19718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    injectEventSync(upEvent);
19818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                }
19918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
20018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        };
20118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
20218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return runAndWaitForEvents(command, new WaitForAnyEventPredicate(eventType), timeout)
20318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                != null;
20418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
20518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
20618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
20718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Clicks at coordinates without waiting for device idle. This may be used for operations
20818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * that require stressing the target.
20918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param x
21018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param y
21118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if the click executed successfully
21218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
21318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean clickNoSync(int x, int y) {
21418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Log.d(LOG_TAG, "clickNoSync (" + x + ", " + y + ")");
21518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
21618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (touchDown(x, y)) {
21718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            SystemClock.sleep(REGULAR_CLICK_LENGTH);
21818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if (touchUp(x, y))
21918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return true;
22018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
22118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return false;
22218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
22318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
22418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
22518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Click at coordinates and blocks until either accessibility event TYPE_WINDOW_CONTENT_CHANGED
22618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * or TYPE_VIEW_SELECTED are received.
22718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
22818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param x
22918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param y
23018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param timeout waiting for event
23118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if events are received, else false if timeout.
23218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
23318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean clickAndSync(final int x, final int y, long timeout) {
23418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
23518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        String logString = String.format("clickAndSync(%d, %d)", x, y);
23618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Log.d(LOG_TAG, logString);
23718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
23818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return runAndWaitForEvents(clickRunnable(x, y), new WaitForAnyEventPredicate(
23918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED |
24018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                AccessibilityEvent.TYPE_VIEW_SELECTED), timeout) != null;
24118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
24218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
24318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
24418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Clicks at coordinates and waits for for a TYPE_WINDOW_STATE_CHANGED event followed
24518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * by TYPE_WINDOW_CONTENT_CHANGED. If timeout occurs waiting for TYPE_WINDOW_STATE_CHANGED,
24618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * no further waits will be performed and the function returns.
24718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param x
24818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param y
24918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param timeout waiting for event
25018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if both events occurred in the expected order
25118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
25218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean clickAndWaitForNewWindow(final int x, final int y, long timeout) {
25318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        String logString = String.format("clickAndWaitForNewWindow(%d, %d)", x, y);
25418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Log.d(LOG_TAG, logString);
25518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
25618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return runAndWaitForEvents(clickRunnable(x, y), new WaitForAllEventPredicate(
25718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED |
25818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED), timeout) != null;
25918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
26018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
26118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
262ca4964ccbef5f2c85855fc14577c7c25d0e0588dPaul Duffin     * Returns a Runnable for use in {@link #runAndWaitForEvents(Runnable, AccessibilityEventFilter, long) to
26318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * perform a click.
26418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
26518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param x coordinate
26618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param y coordinate
26718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return Runnable
26818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
26918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private Runnable clickRunnable(final int x, final int y) {
27018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return new Runnable() {
27118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            @Override
27218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            public void run() {
27318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                if(touchDown(x, y)) {
27418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    SystemClock.sleep(REGULAR_CLICK_LENGTH);
27518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    touchUp(x, y);
27618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                }
27718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
27818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        };
27918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
28018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
28118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
28218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Touches down for a long press at the specified coordinates.
28318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
28418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param x
28518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param y
28618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if successful.
28718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
28818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean longTapNoSync(int x, int y) {
28918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (DEBUG) {
29018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.d(LOG_TAG, "longTapNoSync (" + x + ", " + y + ")");
29118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
29218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
29318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (touchDown(x, y)) {
29418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            SystemClock.sleep(mUiAutomatorBridge.getSystemLongPressTime());
29518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if(touchUp(x, y)) {
29618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return true;
29718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
29818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
29918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return false;
30018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
30118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
30218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private boolean touchDown(int x, int y) {
30318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (DEBUG) {
30418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.d(LOG_TAG, "touchDown (" + x + ", " + y + ")");
30518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
30618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mDownTime = SystemClock.uptimeMillis();
30718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        MotionEvent event = MotionEvent.obtain(
30818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                mDownTime, mDownTime, MotionEvent.ACTION_DOWN, x, y, 1);
30918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
31018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return injectEventSync(event);
31118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
31218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
31318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private boolean touchUp(int x, int y) {
31418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (DEBUG) {
31518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.d(LOG_TAG, "touchUp (" + x + ", " + y + ")");
31618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
31718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        final long eventTime = SystemClock.uptimeMillis();
31818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        MotionEvent event = MotionEvent.obtain(
31918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                mDownTime, eventTime, MotionEvent.ACTION_UP, x, y, 1);
32018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
32118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mDownTime = 0;
32218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return injectEventSync(event);
32318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
32418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
32518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private boolean touchMove(int x, int y) {
32618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (DEBUG) {
32718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.d(LOG_TAG, "touchMove (" + x + ", " + y + ")");
32818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
32918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        final long eventTime = SystemClock.uptimeMillis();
33018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        MotionEvent event = MotionEvent.obtain(
33118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                mDownTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 1);
33218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
33318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return injectEventSync(event);
33418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
33518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
33618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
33718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Handle swipes in any direction where the result is a scroll event. This call blocks
33818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * until the UI has fired a scroll event or timeout.
33918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param downX
34018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param downY
34118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param upX
34218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param upY
34318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param steps
34418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if we are not at the beginning or end of the scrollable view.
34518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
34618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollSwipe(final int downX, final int downY, final int upX, final int upY,
34718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            final int steps) {
34818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Log.d(LOG_TAG, "scrollSwipe (" +  downX + ", " + downY + ", " + upX + ", "
34918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                + upY + ", " + steps +")");
35018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
35118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Runnable command = new Runnable() {
35218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            @Override
35318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            public void run() {
35418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                swipe(downX, downY, upX, upY, steps);
35518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
35618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        };
35718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
35818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // Collect all accessibility events generated during the swipe command and get the
35918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // last event
36018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        ArrayList<AccessibilityEvent> events = new ArrayList<AccessibilityEvent>();
36118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        runAndWaitForEvents(command,
36218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                new EventCollectingPredicate(AccessibilityEvent.TYPE_VIEW_SCROLLED, events),
36318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                Configurator.getInstance().getScrollAcknowledgmentTimeout());
36418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
36518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        AccessibilityEvent event = getLastMatchingEvent(events,
36618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                AccessibilityEvent.TYPE_VIEW_SCROLLED);
36718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
36818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (event == null) {
36918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // end of scroll since no new scroll events received
37018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            recycleAccessibilityEvents(events);
37118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return false;
37218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
37318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
37418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // AdapterViews have indices we can use to check for the beginning.
37518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        boolean foundEnd = false;
37618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (event.getFromIndex() != -1 && event.getToIndex() != -1 && event.getItemCount() != -1) {
37718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            foundEnd = event.getFromIndex() == 0 ||
37818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    (event.getItemCount() - 1) == event.getToIndex();
37918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.d(LOG_TAG, "scrollSwipe reached scroll end: " + foundEnd);
38018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        } else if (event.getScrollX() != -1 && event.getScrollY() != -1) {
38118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // Determine if we are scrolling vertically or horizontally.
38218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if (downX == upX) {
38318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // Vertical
38418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                foundEnd = event.getScrollY() == 0 ||
38518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                        event.getScrollY() == event.getMaxScrollY();
38618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                Log.d(LOG_TAG, "Vertical scrollSwipe reached scroll end: " + foundEnd);
38718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            } else if (downY == upY) {
38818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // Horizontal
38918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                foundEnd = event.getScrollX() == 0 ||
39018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                        event.getScrollX() == event.getMaxScrollX();
39118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                Log.d(LOG_TAG, "Horizontal scrollSwipe reached scroll end: " + foundEnd);
39218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
39318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
39418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        recycleAccessibilityEvents(events);
39518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return !foundEnd;
39618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
39718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
39818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private AccessibilityEvent getLastMatchingEvent(List<AccessibilityEvent> events, int type) {
39918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for (int x = events.size(); x > 0; x--) {
40018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            AccessibilityEvent event = events.get(x - 1);
40118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if (event.getEventType() == type)
40218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return event;
40318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
40418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return null;
40518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
40618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
40718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private void recycleAccessibilityEvents(List<AccessibilityEvent> events) {
40818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for (AccessibilityEvent event : events)
40918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            event.recycle();
41018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        events.clear();
41118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
41218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
41318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
41418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Handle swipes in any direction.
41518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param downX
41618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param downY
41718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param upX
41818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param upY
41918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param steps
42018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if the swipe executed successfully
42118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
42218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean swipe(int downX, int downY, int upX, int upY, int steps) {
42318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return swipe(downX, downY, upX, upY, steps, false /*drag*/);
42418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
42518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
42618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
42718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Handle swipes/drags in any direction.
42818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param downX
42918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param downY
43018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param upX
43118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param upY
43218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param steps
43318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param drag when true, the swipe becomes a drag swipe
43418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if the swipe executed successfully
43518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
43618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean swipe(int downX, int downY, int upX, int upY, int steps, boolean drag) {
43718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        boolean ret = false;
43818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int swipeSteps = steps;
43918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        double xStep = 0;
44018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        double yStep = 0;
44118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
44218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // avoid a divide by zero
44318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if(swipeSteps == 0)
44418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            swipeSteps = 1;
44518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
44618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        xStep = ((double)(upX - downX)) / swipeSteps;
44718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        yStep = ((double)(upY - downY)) / swipeSteps;
44818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
44918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // first touch starts exactly at the point requested
45018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        ret = touchDown(downX, downY);
45118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (drag)
45218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            SystemClock.sleep(mUiAutomatorBridge.getSystemLongPressTime());
45318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for(int i = 1; i < swipeSteps; i++) {
45418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            ret &= touchMove(downX + (int)(xStep * i), downY + (int)(yStep * i));
45518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if(ret == false)
45618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                break;
45718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // set some known constant delay between steps as without it this
45818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // become completely dependent on the speed of the system and results
45918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // may vary on different devices. This guarantees at minimum we have
46018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // a preset delay.
46118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            SystemClock.sleep(MOTION_EVENT_INJECTION_DELAY_MILLIS);
46218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
46318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (drag)
46418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            SystemClock.sleep(REGULAR_CLICK_LENGTH);
46518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        ret &= touchUp(upX, upY);
46618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return(ret);
46718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
46818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
46918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
47018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Performs a swipe between points in the Point array.
47118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param segments is Point array containing at least one Point object
47218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param segmentSteps steps to inject between two Points
47318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true on success
47418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
47518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean swipe(Point[] segments, int segmentSteps) {
47618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        boolean ret = false;
47718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int swipeSteps = segmentSteps;
47818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        double xStep = 0;
47918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        double yStep = 0;
48018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
48118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // avoid a divide by zero
48218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if(segmentSteps == 0)
48318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            segmentSteps = 1;
48418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
48518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // must have some points
48618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if(segments.length == 0)
48718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return false;
48818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
48918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // first touch starts exactly at the point requested
49018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        ret = touchDown(segments[0].x, segments[0].y);
49118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for(int seg = 0; seg < segments.length; seg++) {
49218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if(seg + 1 < segments.length) {
49318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
49418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                xStep = ((double)(segments[seg+1].x - segments[seg].x)) / segmentSteps;
49518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                yStep = ((double)(segments[seg+1].y - segments[seg].y)) / segmentSteps;
49618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
49718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                for(int i = 1; i < swipeSteps; i++) {
49818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    ret &= touchMove(segments[seg].x + (int)(xStep * i),
49918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                            segments[seg].y + (int)(yStep * i));
50018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    if(ret == false)
50118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                        break;
50218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    // set some known constant delay between steps as without it this
50318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    // become completely dependent on the speed of the system and results
50418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    // may vary on different devices. This guarantees at minimum we have
50518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    // a preset delay.
50618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    SystemClock.sleep(MOTION_EVENT_INJECTION_DELAY_MILLIS);
50718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                }
50818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
50918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
51018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        ret &= touchUp(segments[segments.length - 1].x, segments[segments.length -1].y);
51118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return(ret);
51218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
51318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
51418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
51518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean sendText(String text) {
51618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (DEBUG) {
51718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.d(LOG_TAG, "sendText (" + text + ")");
51818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
51918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
52018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        KeyEvent[] events = mKeyCharacterMap.getEvents(text.toCharArray());
52118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
52218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (events != null) {
52318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            long keyDelay = Configurator.getInstance().getKeyInjectionDelay();
52418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            for (KeyEvent event2 : events) {
52518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // We have to change the time of an event before injecting it because
52618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // all KeyEvents returned by KeyCharacterMap.getEvents() have the same
52718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // time stamp and the system rejects too old events. Hence, it is
52818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // possible for an event to become stale before it is injected if it
52918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // takes too long to inject the preceding ones.
53018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                KeyEvent event = KeyEvent.changeTimeRepeat(event2,
53118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                        SystemClock.uptimeMillis(), 0);
53218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                if (!injectEventSync(event)) {
53318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    return false;
53418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                }
53518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                SystemClock.sleep(keyDelay);
53618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
53718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
53818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return true;
53918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
54018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
54118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean sendKey(int keyCode, int metaState) {
54218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (DEBUG) {
54318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.d(LOG_TAG, "sendKey (" + keyCode + ", " + metaState + ")");
54418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
54518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
54618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        final long eventTime = SystemClock.uptimeMillis();
54718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        KeyEvent downEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN,
54818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
54918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                InputDevice.SOURCE_KEYBOARD);
55018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (injectEventSync(downEvent)) {
55118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            KeyEvent upEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP,
55218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
55318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    InputDevice.SOURCE_KEYBOARD);
55418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if(injectEventSync(upEvent)) {
55518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return true;
55618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
55718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
55818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return false;
55918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
56018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
56118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
56218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Rotates right and also freezes rotation in that position by
56318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * disabling the sensors. If you want to un-freeze the rotation
56418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * and re-enable the sensors see {@link #unfreezeRotation()}. Note
56518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * that doing so may cause the screen contents to rotate
56618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * depending on the current physical position of the test device.
56718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws RemoteException
56818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
56918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public void setRotationRight() {
57018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mUiAutomatorBridge.setRotation(UiAutomation.ROTATION_FREEZE_270);
57118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
57218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
57318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
57418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Rotates left and also freezes rotation in that position by
57518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * disabling the sensors. If you want to un-freeze the rotation
57618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * and re-enable the sensors see {@link #unfreezeRotation()}. Note
57718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * that doing so may cause the screen contents to rotate
57818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * depending on the current physical position of the test device.
57918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws RemoteException
58018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
58118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public void setRotationLeft() {
58218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mUiAutomatorBridge.setRotation(UiAutomation.ROTATION_FREEZE_90);
58318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
58418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
58518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
58618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Rotates up and also freezes rotation in that position by
58718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * disabling the sensors. If you want to un-freeze the rotation
58818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * and re-enable the sensors see {@link #unfreezeRotation()}. Note
58918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * that doing so may cause the screen contents to rotate
59018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * depending on the current physical position of the test device.
59118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws RemoteException
59218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
59318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public void setRotationNatural() {
59418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mUiAutomatorBridge.setRotation(UiAutomation.ROTATION_FREEZE_0);
59518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
59618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
59718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
59818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Disables the sensors and freezes the device rotation at its
59918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * current rotation state.
60018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws RemoteException
60118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
60218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public void freezeRotation() {
60318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mUiAutomatorBridge.setRotation(UiAutomation.ROTATION_FREEZE_CURRENT);
60418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
60518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
60618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
60718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Re-enables the sensors and un-freezes the device rotation
60818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * allowing its contents to rotate with the device physical rotation.
60918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws RemoteException
61018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
61118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public void unfreezeRotation() {
61218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mUiAutomatorBridge.setRotation(UiAutomation.ROTATION_UNFREEZE);
61318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
61418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
61518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
61618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * This method simply presses the power button if the screen is OFF else
61718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * it does nothing if the screen is already ON.
61818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if the device was asleep else false
61918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws RemoteException
62018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
62118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean wakeDevice() throws RemoteException {
62218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if(!isScreenOn()) {
62318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            sendKey(KeyEvent.KEYCODE_POWER, 0);
62418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return true;
62518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
62618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return false;
62718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
62818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
62918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
63018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * This method simply presses the power button if the screen is ON else
63118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * it does nothing if the screen is already OFF.
63218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if the device was awake else false
63318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws RemoteException
63418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
63518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean sleepDevice() throws RemoteException {
63618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if(isScreenOn()) {
63718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            this.sendKey(KeyEvent.KEYCODE_POWER, 0);
63818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return true;
63918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
64018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return false;
64118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
64218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
64318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
64418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Checks the power manager if the screen is ON
64518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if the screen is ON else false
64618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws RemoteException
64718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
64818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean isScreenOn() throws RemoteException {
64918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return mUiAutomatorBridge.isScreenOn();
65018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
65118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
65218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private boolean injectEventSync(InputEvent event) {
65318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return mUiAutomatorBridge.injectInputEvent(event, true);
65418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
65518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
65618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private int getPointerAction(int motionEnvent, int index) {
65718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return motionEnvent + (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
65818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
65918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
66018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
66118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Performs a multi-touch gesture
66218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
66318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Takes a series of touch coordinates for at least 2 pointers. Each pointer must have
66418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * all of its touch steps defined in an array of {@link PointerCoords}. By having the ability
66518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * to specify the touch points along the path of a pointer, the caller is able to specify
66618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * complex gestures like circles, irregular shapes etc, where each pointer may take a
66718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * different path.
66818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
66918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * To create a single point on a pointer's touch path
67018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * <code>
67118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *       PointerCoords p = new PointerCoords();
67218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *       p.x = stepX;
67318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *       p.y = stepY;
67418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *       p.pressure = 1;
67518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *       p.size = 1;
67618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * </code>
67718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param touches each array of {@link PointerCoords} constitute a single pointer's touch path.
67818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *        Multiple {@link PointerCoords} arrays constitute multiple pointers, each with its own
67918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *        path. Each {@link PointerCoords} in an array constitute a point on a pointer's path.
68018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return <code>true</code> if all points on all paths are injected successfully, <code>false
68118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *        </code>otherwise
68218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 18
68318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
68418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean performMultiPointerGesture(PointerCoords[] ... touches) {
68518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        boolean ret = true;
68618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (touches.length < 2) {
68718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            throw new IllegalArgumentException("Must provide coordinates for at least 2 pointers");
68818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
68918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
69018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // Get the pointer with the max steps to inject.
69118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int maxSteps = 0;
69218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for (int x = 0; x < touches.length; x++)
69318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            maxSteps = (maxSteps < touches[x].length) ? touches[x].length : maxSteps;
69418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
69518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // specify the properties for each pointer as finger touch
69618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        PointerProperties[] properties = new PointerProperties[touches.length];
69718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        PointerCoords[] pointerCoords = new PointerCoords[touches.length];
69818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for (int x = 0; x < touches.length; x++) {
69918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            PointerProperties prop = new PointerProperties();
70018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            prop.id = x;
70118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            prop.toolType = MotionEvent.TOOL_TYPE_FINGER;
70218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            properties[x] = prop;
70318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
70418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // for each pointer set the first coordinates for touch down
70518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            pointerCoords[x] = touches[x][0];
70618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
70718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
70818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // Touch down all pointers
70918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        long downTime = SystemClock.uptimeMillis();
71018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        MotionEvent event;
71118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        event = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 1,
71218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                properties, pointerCoords, 0, 0, 1, 1, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
71318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        ret &= injectEventSync(event);
71418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
71518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for (int x = 1; x < touches.length; x++) {
71618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            event = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(),
71718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    getPointerAction(MotionEvent.ACTION_POINTER_DOWN, x), x + 1, properties,
71818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    pointerCoords, 0, 0, 1, 1, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
71918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            ret &= injectEventSync(event);
72018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
72118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
72218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // Move all pointers
72318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for (int i = 1; i < maxSteps - 1; i++) {
72418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // for each pointer
72518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            for (int x = 0; x < touches.length; x++) {
72618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                // check if it has coordinates to move
72718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                if (touches[x].length > i)
72818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    pointerCoords[x] = touches[x][i];
72918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                else
73018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    pointerCoords[x] = touches[x][touches[x].length - 1];
73118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
73218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
73318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            event = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(),
73418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    MotionEvent.ACTION_MOVE, touches.length, properties, pointerCoords, 0, 0, 1, 1,
73518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
73618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
73718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            ret &= injectEventSync(event);
73818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            SystemClock.sleep(MOTION_EVENT_INJECTION_DELAY_MILLIS);
73918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
74018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
74118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // For each pointer get the last coordinates
74218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for (int x = 0; x < touches.length; x++)
74318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            pointerCoords[x] = touches[x][touches[x].length - 1];
74418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
74518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // touch up
74618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for (int x = 1; x < touches.length; x++) {
74718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            event = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(),
74818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    getPointerAction(MotionEvent.ACTION_POINTER_UP, x), x + 1, properties,
74918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    pointerCoords, 0, 0, 1, 1, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
75018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            ret &= injectEventSync(event);
75118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
75218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
75318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Log.i(LOG_TAG, "x " + pointerCoords[0].x);
75418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // first to touch down is last up
75518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        event = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 1,
75618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                properties, pointerCoords, 0, 0, 1, 1, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
75718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        ret &= injectEventSync(event);
75818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return ret;
75918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
76018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
76118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
76218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Simulates a short press on the Recent Apps button.
76318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
76418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if successful, else return false
76518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 18
76618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
76718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean toggleRecentApps() {
76818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return mUiAutomatorBridge.performGlobalAction(
76918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                AccessibilityService.GLOBAL_ACTION_RECENTS);
77018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
77118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
77218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
77318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Opens the notification shade
77418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
77518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if successful, else return false
77618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 18
77718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
77818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean openNotification() {
77918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return mUiAutomatorBridge.performGlobalAction(
78018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
78118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
78218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
78318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
78418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Opens the quick settings shade
78518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
78618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if successful, else return false
78718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 18
78818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
78918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean openQuickSettings() {
79018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return mUiAutomatorBridge.performGlobalAction(
79118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
79218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
79318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu}
794