InteractionController.java revision ca4964ccbef5f2c85855fc14577c7c25d0e0588d
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