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 Zhupackage com.android.uiautomator.core; 1718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 1818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.app.UiAutomation.OnAccessibilityEventListener; 1918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.os.SystemClock; 2018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.util.Log; 2118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.view.accessibility.AccessibilityEvent; 2218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo; 2318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 2418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 2518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu/** 2618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * The QueryController main purpose is to translate a {@link UiSelector} selectors to 2718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * {@link AccessibilityNodeInfo}. This is all this controller does. 2818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 2918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuclass QueryController { 3018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 3118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private static final String LOG_TAG = QueryController.class.getSimpleName(); 3218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 3318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG); 3418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private static final boolean VERBOSE = Log.isLoggable(LOG_TAG, Log.VERBOSE); 3518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 3618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private final UiAutomatorBridge mUiAutomatorBridge; 3718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 3818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private final Object mLock = new Object(); 3918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 4018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private String mLastActivityName = null; 4118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 4218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // During a pattern selector search, the recursive pattern search 4318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // methods will track their counts and indexes here. 4418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private int mPatternCounter = 0; 4518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private int mPatternIndexer = 0; 4618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 4718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // These help show each selector's search context as it relates to the previous sub selector 4818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // matched. When a compound selector fails, it is hard to tell which part of it is failing. 4918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // Seeing how a selector is being parsed and which sub selector failed within a long list 5018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // of compound selectors is very helpful. 5118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private int mLogIndent = 0; 5218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private int mLogParentIndent = 0; 5318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 5418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private String mLastTraversedText = ""; 5518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 5618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu public QueryController(UiAutomatorBridge bridge) { 5718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mUiAutomatorBridge = bridge; 5818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu bridge.setOnAccessibilityEventListener(new OnAccessibilityEventListener() { 5918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu @Override 6018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu public void onAccessibilityEvent(AccessibilityEvent event) { 6118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu synchronized (mLock) { 6218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu switch(event.getEventType()) { 6318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: 6418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // don't trust event.getText(), check for nulls 6518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (event.getText() != null && event.getText().size() > 0) { 6618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(event.getText().get(0) != null) 6718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLastActivityName = event.getText().get(0).toString(); 6818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 6918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu break; 7018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu case AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: 7118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // don't trust event.getText(), check for nulls 7218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (event.getText() != null && event.getText().size() > 0) 7318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(event.getText().get(0) != null) 7418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLastTraversedText = event.getText().get(0).toString(); 7518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (DEBUG) 7618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.d(LOG_TAG, "Last text selection reported: " + 7718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLastTraversedText); 7818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu break; 7918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 8018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLock.notifyAll(); 8118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 8218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 8318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu }); 8418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 8518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 8618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 8718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Returns the last text selection reported by accessibility 8818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * event TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY. One way to cause 8918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * this event is using a DPad arrows to focus on UI elements. 9018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 9118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu public String getLastTraversedText() { 9218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mUiAutomatorBridge.waitForIdle(); 9318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu synchronized (mLock) { 9418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (mLastTraversedText.length() > 0) { 9518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return mLastTraversedText; 9618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 9718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 9818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 9918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 10018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 10118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 10218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Clears the last text selection value saved from the TYPE_VIEW_TEXT_SELECTION_CHANGED 10318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * event 10418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 10518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu public void clearLastTraversedText() { 10618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mUiAutomatorBridge.waitForIdle(); 10718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu synchronized (mLock) { 10818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLastTraversedText = ""; 10918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 11018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 11118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 11218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private void initializeNewSearch() { 11318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mPatternCounter = 0; 11418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mPatternIndexer = 0; 11518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLogIndent = 0; 11618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLogParentIndent = 0; 11718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 11818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 11918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 12018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Counts the instances of the selector group. The selector must be in the following 12118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * format: [container_selector, PATTERN=[INSTANCE=x, PATTERN=[the_pattern]] 12218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * where the container_selector is used to find the containment region to search for patterns 12318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * and the INSTANCE=x is the instance of the_pattern to return. 12418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param selector 12518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @return number of pattern matches. Returns 0 for all other cases. 12618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 12718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu public int getPatternCount(UiSelector selector) { 12818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu findAccessibilityNodeInfo(selector, true /*counting*/); 12918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return mPatternCounter; 13018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 13118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 13218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 13318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Main search method for translating By selectors to AccessibilityInfoNodes 13418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param selector 13518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @return AccessibilityNodeInfo 13618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 13718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu public AccessibilityNodeInfo findAccessibilityNodeInfo(UiSelector selector) { 13818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return findAccessibilityNodeInfo(selector, false); 13918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 14018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 14118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu protected AccessibilityNodeInfo findAccessibilityNodeInfo(UiSelector selector, 14218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu boolean isCounting) { 14318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mUiAutomatorBridge.waitForIdle(); 14418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu initializeNewSearch(); 14518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 14618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (DEBUG) 14718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.d(LOG_TAG, "Searching: " + selector); 14818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 14918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu synchronized (mLock) { 15018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo rootNode = getRootNode(); 15118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (rootNode == null) { 15218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.e(LOG_TAG, "Cannot proceed when root node is null. Aborted search"); 15318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 15418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 15518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 15618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // Copy so that we don't modify the original's sub selectors 15718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu UiSelector uiSelector = new UiSelector(selector); 15818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return translateCompoundSelector(uiSelector, rootNode, isCounting); 15918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 16018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 16118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 16218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 16318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Gets the root node from accessibility and if it fails to get one it will 16418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * retry every 250ms for up to 1000ms. 16518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @return null if no root node is obtained 16618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 16718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu protected AccessibilityNodeInfo getRootNode() { 16818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu final int maxRetry = 4; 16918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu final long waitInterval = 250; 17018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo rootNode = null; 17118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu for(int x = 0; x < maxRetry; x++) { 17218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu rootNode = mUiAutomatorBridge.getRootInActiveWindow(); 17318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (rootNode != null) { 17418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return rootNode; 17518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 17618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(x < maxRetry - 1) { 17718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.e(LOG_TAG, "Got null root node from accessibility - Retrying..."); 17818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu SystemClock.sleep(waitInterval); 17918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 18018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 18118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return rootNode; 18218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 18318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 18418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 18518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * A compoundSelector encapsulate both Regular and Pattern selectors. The formats follows: 18618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * <p/> 18718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * regular_selector = By[attributes... CHILD=By[attributes... CHILD=By[....]]] 18818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * <br/> 18918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * pattern_selector = ...CONTAINER=By[..] PATTERN=By[instance=x PATTERN=[regular_selector] 19018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * <br/> 19118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * compound_selector = [regular_selector [pattern_selector]] 19218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * <p/> 19318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * regular_selectors are the most common form of selectors and the search for them 19418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * is straightforward. On the other hand pattern_selectors requires search to be 19518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * performed as in regular_selector but where regular_selector search returns immediately 19618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * upon a successful match, the search for pattern_selector continues until the 19718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * requested matched _instance_ of that pattern is matched. 19818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * <p/> 19918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Counting UI objects requires using pattern_selectors. The counting search is the same 20018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * as a pattern_search however we're not looking to match an instance of the pattern but 20118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * rather continuously walking the accessibility node hierarchy while counting matched 20218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * patterns, until the end of the tree. 20318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * <p/> 20418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * If both present, order of parsing begins with CONTAINER followed by PATTERN then the 20518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * top most selector is processed as regular_selector within the context of the previous 20618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * CONTAINER and its PATTERN information. If neither is present then the top selector is 20718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * directly treated as regular_selector. So the presence of a CONTAINER and PATTERN within 20818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * a selector simply dictates that the selector matching will be constraint to the sub tree 20918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * node where the CONTAINER and its child PATTERN have identified. 21018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param selector 21118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param fromNode 21218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param isCounting 21318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @return AccessibilityNodeInfo 21418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 21518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private AccessibilityNodeInfo translateCompoundSelector(UiSelector selector, 21618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo fromNode, boolean isCounting) { 21718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 21818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // Start translating compound selectors by translating the regular_selector first 21918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // The regular_selector is then used as a container for any optional pattern_selectors 22018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // that may or may not be specified. 22118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(selector.hasContainerSelector()) 22218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // nested pattern selectors 22318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(selector.getContainerSelector().hasContainerSelector()) { 22418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu fromNode = translateCompoundSelector( 22518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu selector.getContainerSelector(), fromNode, false); 22618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu initializeNewSearch(); 22718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } else 22818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu fromNode = translateReqularSelector(selector.getContainerSelector(), fromNode); 22918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu else 23018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu fromNode = translateReqularSelector(selector, fromNode); 23118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 23218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(fromNode == null) { 23318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (DEBUG) 23418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.d(LOG_TAG, "Container selector not found: " + selector.dumpToString(false)); 23518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 23618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 23718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 23818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(selector.hasPatternSelector()) { 23918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu fromNode = translatePatternSelector(selector.getPatternSelector(), 24018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu fromNode, isCounting); 24118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 24218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (isCounting) { 24318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.i(LOG_TAG, String.format( 24418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu "Counted %d instances of: %s", mPatternCounter, selector)); 24518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 24618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } else { 24718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(fromNode == null) { 24818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (DEBUG) 24918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.d(LOG_TAG, "Pattern selector not found: " + 25018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu selector.dumpToString(false)); 25118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 25218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 25318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 25418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 25518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 25618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // translate any additions to the selector that may have been added by tests 25718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // with getChild(By selector) after a container and pattern selectors 25818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(selector.hasContainerSelector() || selector.hasPatternSelector()) { 25918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(selector.hasChildSelector() || selector.hasParentSelector()) 26018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu fromNode = translateReqularSelector(selector, fromNode); 26118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 26218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 26318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(fromNode == null) { 26418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (DEBUG) 26518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.d(LOG_TAG, "Object Not Found for selector " + selector); 26618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 26718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 26818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.i(LOG_TAG, String.format("Matched selector: %s <<==>> [%s]", selector, fromNode)); 26918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return fromNode; 27018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 27118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 27218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 27318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Used by the {@link #translateCompoundSelector(UiSelector, AccessibilityNodeInfo, boolean)} 27418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * to translate the regular_selector portion. It has the following format: 27518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * <p/> 27618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * regular_selector = By[attributes... CHILD=By[attributes... CHILD=By[....]]]<br/> 27718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * <p/> 27818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * regular_selectors are the most common form of selectors and the search for them 27918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * is straightforward. This method will only look for CHILD or PARENT sub selectors. 28018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * <p/> 28118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param selector 28218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param fromNode 28318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @return AccessibilityNodeInfo if found else null 28418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 28518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private AccessibilityNodeInfo translateReqularSelector(UiSelector selector, 28618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo fromNode) { 28718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 28818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return findNodeRegularRecursive(selector, fromNode, 0); 28918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 29018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 29118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private AccessibilityNodeInfo findNodeRegularRecursive(UiSelector subSelector, 29218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo fromNode, int index) { 29318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 29418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (subSelector.isMatchFor(fromNode, index)) { 29518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (DEBUG) { 29618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.d(LOG_TAG, formatLog(String.format("%s", 29718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu subSelector.dumpToString(false)))); 29818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 29918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(subSelector.isLeaf()) { 30018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return fromNode; 30118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 30218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(subSelector.hasChildSelector()) { 30318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLogIndent++; // next selector 30418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu subSelector = subSelector.getChildSelector(); 30518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(subSelector == null) { 30618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.e(LOG_TAG, "Error: A child selector without content"); 30718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; // there is an implementation fault 30818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 30918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } else if(subSelector.hasParentSelector()) { 31018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLogIndent++; // next selector 31118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu subSelector = subSelector.getParentSelector(); 31218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(subSelector == null) { 31318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.e(LOG_TAG, "Error: A parent selector without content"); 31418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; // there is an implementation fault 31518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 31618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // the selector requested we start at this level from 31718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // the parent node from the one we just matched 31818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu fromNode = fromNode.getParent(); 31918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(fromNode == null) 32018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 32118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 32218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 32318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 32418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu int childCount = fromNode.getChildCount(); 32518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu boolean hasNullChild = false; 32618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu for (int i = 0; i < childCount; i++) { 32718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo childNode = fromNode.getChild(i); 32818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (childNode == null) { 32918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.w(LOG_TAG, String.format( 33018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu "AccessibilityNodeInfo returned a null child (%d of %d)", i, childCount)); 33118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (!hasNullChild) { 33218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.w(LOG_TAG, String.format("parent = %s", fromNode.toString())); 33318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 33418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu hasNullChild = true; 33518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu continue; 33618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 33718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (!childNode.isVisibleToUser()) { 33818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (VERBOSE) 33918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.v(LOG_TAG, 34018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu String.format("Skipping invisible child: %s", childNode.toString())); 34118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu continue; 34218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 34318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo retNode = findNodeRegularRecursive(subSelector, childNode, i); 34418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (retNode != null) { 34518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return retNode; 34618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 34718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 34818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 34918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 35018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 35118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 35218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Used by the {@link #translateCompoundSelector(UiSelector, AccessibilityNodeInfo, boolean)} 35318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * to translate the pattern_selector portion. It has the following format: 35418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * <p/> 35518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * pattern_selector = ... PATTERN=By[instance=x PATTERN=[regular_selector]]<br/> 35618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * <p/> 35718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * pattern_selectors requires search to be performed as regular_selector but where 35818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * regular_selector search returns immediately upon a successful match, the search for 35918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * pattern_selector continues until the requested matched instance of that pattern is 36018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * encountered. 36118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * <p/> 36218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Counting UI objects requires using pattern_selectors. The counting search is the same 36318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * as a pattern_search however we're not looking to match an instance of the pattern but 36418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * rather continuously walking the accessibility node hierarchy while counting patterns 36518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * until the end of the tree. 36618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param subSelector 36718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param fromNode 36818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param isCounting 36918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @return null of node is not found or if counting mode is true. 37018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * See {@link #translateCompoundSelector(UiSelector, AccessibilityNodeInfo, boolean)} 37118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 37218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private AccessibilityNodeInfo translatePatternSelector(UiSelector subSelector, 37318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo fromNode, boolean isCounting) { 37418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 37518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(subSelector.hasPatternSelector()) { 37618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // Since pattern_selectors are also the type of selectors used when counting, 37718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // we check if this is a counting run or an indexing run 37818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(isCounting) 37918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu //since we're counting, we reset the indexer so to terminates the search when 38018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // the end of tree is reached. The count will be in mPatternCount 38118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mPatternIndexer = -1; 38218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu else 38318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // terminates the search once we match the pattern's instance 38418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mPatternIndexer = subSelector.getInstance(); 38518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 38618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // A pattern is wrapped in a PATTERN[instance=x PATTERN[the_pattern]] 38718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu subSelector = subSelector.getPatternSelector(); 38818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(subSelector == null) { 38918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.e(LOG_TAG, "Pattern portion of the selector is null or not defined"); 39018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; // there is an implementation fault 39118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 39218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // save the current indent level as parent indent before pattern searches 39318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // begin under the current tree position. 39418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLogParentIndent = ++mLogIndent; 39518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return findNodePatternRecursive(subSelector, fromNode, 0, subSelector); 39618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 39718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 39818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.e(LOG_TAG, "Selector must have a pattern selector defined"); // implementation fault? 39918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 40018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 40118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 40218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private AccessibilityNodeInfo findNodePatternRecursive( 40318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu UiSelector subSelector, AccessibilityNodeInfo fromNode, int index, 40418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu UiSelector originalPattern) { 40518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 40618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (subSelector.isMatchFor(fromNode, index)) { 40718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(subSelector.isLeaf()) { 40818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(mPatternIndexer == 0) { 40918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (DEBUG) 41018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.d(LOG_TAG, formatLog( 41118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu String.format("%s", subSelector.dumpToString(false)))); 41218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return fromNode; 41318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } else { 41418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (DEBUG) 41518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.d(LOG_TAG, formatLog( 41618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu String.format("%s", subSelector.dumpToString(false)))); 41718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mPatternCounter++; //count the pattern matched 41818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mPatternIndexer--; //decrement until zero for the instance requested 41918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 42018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // At a leaf selector within a group and still not instance matched 42118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // then reset the selector to continue search from current position 42218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // in the accessibility tree for the next pattern match up until the 42318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // pattern index hits 0. 42418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu subSelector = originalPattern; 42518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // starting over with next pattern search so reset to parent level 42618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLogIndent = mLogParentIndent; 42718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 42818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } else { 42918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (DEBUG) 43018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.d(LOG_TAG, formatLog( 43118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu String.format("%s", subSelector.dumpToString(false)))); 43218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 43318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(subSelector.hasChildSelector()) { 43418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLogIndent++; // next selector 43518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu subSelector = subSelector.getChildSelector(); 43618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(subSelector == null) { 43718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.e(LOG_TAG, "Error: A child selector without content"); 43818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 43918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 44018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } else if(subSelector.hasParentSelector()) { 44118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mLogIndent++; // next selector 44218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu subSelector = subSelector.getParentSelector(); 44318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(subSelector == null) { 44418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.e(LOG_TAG, "Error: A parent selector without content"); 44518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 44618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 44718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu fromNode = fromNode.getParent(); 44818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(fromNode == null) 44918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 45018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 45118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 45218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 45318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 45418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu int childCount = fromNode.getChildCount(); 45518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu boolean hasNullChild = false; 45618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu for (int i = 0; i < childCount; i++) { 45718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo childNode = fromNode.getChild(i); 45818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (childNode == null) { 45918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.w(LOG_TAG, String.format( 46018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu "AccessibilityNodeInfo returned a null child (%d of %d)", i, childCount)); 46118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (!hasNullChild) { 46218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.w(LOG_TAG, String.format("parent = %s", fromNode.toString())); 46318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 46418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu hasNullChild = true; 46518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu continue; 46618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 46718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (!childNode.isVisibleToUser()) { 46818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (DEBUG) 46918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.d(LOG_TAG, 47018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu String.format("Skipping invisible child: %s", childNode.toString())); 47118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu continue; 47218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 47318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo retNode = findNodePatternRecursive( 47418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu subSelector, childNode, i, originalPattern); 47518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (retNode != null) { 47618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return retNode; 47718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 47818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 47918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 48018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 48118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 48218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu public AccessibilityNodeInfo getAccessibilityRootNode() { 48318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return mUiAutomatorBridge.getRootInActiveWindow(); 48418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 48518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 48618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 48718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Last activity to report accessibility events. 48818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @deprecated The results returned should be considered unreliable 48918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @return String name of activity 49018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 49118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu @Deprecated 49218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu public String getCurrentActivityName() { 49318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mUiAutomatorBridge.waitForIdle(); 49418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu synchronized (mLock) { 49518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return mLastActivityName; 49618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 49718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 49818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 49918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 50018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Last package to report accessibility events 50118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @return String name of package 50218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 50318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu public String getCurrentPackageName() { 50418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu mUiAutomatorBridge.waitForIdle(); 50518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo rootNode = getRootNode(); 50618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (rootNode == null) 50718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return null; 50818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return rootNode.getPackageName() != null ? rootNode.getPackageName().toString() : null; 50918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 51018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 51118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private String formatLog(String str) { 51218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu StringBuilder l = new StringBuilder(); 51318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu for(int space = 0; space < mLogIndent; space++) 51418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu l.append(". . "); 51518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(mLogIndent > 0) 51618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu l.append(String.format(". . [%d]: %s", mPatternCounter, str)); 51718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu else 51818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu l.append(String.format(". . [%d]: %s", mPatternCounter, str)); 51918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return l.toString(); 52018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 52118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu} 522