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