/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.utils.traversal;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import com.android.utils.AccessibilityNodeInfoUtils;
import java.util.HashMap;
import java.util.Map;
/**
* Window could have its content views hierarchy. Views in that hierarchy could be traversed one
* after another. Every view inside that hierarchy could change its natural traverse order by
* setting traversal before/after view. See
* {@link android.view.View.getTraversalBefore()}, {@link android.view.View.getTraversalAfter()}.
*
* This strategy considers changes in the traverse order according to after/before view movements
*/
@SuppressWarnings("JavadocReference")
public class OrderedTraversalStrategy implements TraversalStrategy {
private AccessibilityNodeInfoCompat mRootNode;
private final OrderedTraversalController mController;
private final Map<AccessibilityNodeInfoCompat, Boolean> mSpeakingNodesCache;
public OrderedTraversalStrategy(AccessibilityNodeInfoCompat rootNode) {
if (rootNode != null) {
mRootNode = AccessibilityNodeInfoCompat.obtain(rootNode);
}
mSpeakingNodesCache = new HashMap<>();
mController = new OrderedTraversalController();
mController.setSpeakNodesCache(mSpeakingNodesCache);
mController.initOrder(mRootNode, false);
}
@Override
public void recycle() {
if (mRootNode != null) {
mRootNode.recycle();
}
mController.recycle();
}
@Override
public Map<AccessibilityNodeInfoCompat, Boolean> getSpeakingNodesCache() {
return mSpeakingNodesCache;
}
@Override
public AccessibilityNodeInfoCompat findFocus(AccessibilityNodeInfoCompat startNode,
@SearchDirection int direction) {
switch (direction) {
case TraversalStrategy.SEARCH_FOCUS_FORWARD:
return focusNext(startNode);
case TraversalStrategy.SEARCH_FOCUS_BACKWARD:
return focusPrevious(startNode);
}
return null;
}
private AccessibilityNodeInfoCompat focusNext(AccessibilityNodeInfoCompat node) {
AccessibilityNodeInfoCompat rootNode = AccessibilityNodeInfoCompat.obtain(node);
AccessibilityNodeInfoCompat targetNode;
try {
targetNode = mController.findNext(rootNode);
} finally {
AccessibilityNodeInfoUtils.recycleNodes(rootNode);
}
return targetNode;
}
private AccessibilityNodeInfoCompat focusPrevious(AccessibilityNodeInfoCompat node) {
AccessibilityNodeInfoCompat rootNode = AccessibilityNodeInfoCompat.obtain(node);
AccessibilityNodeInfoCompat targetNode;
try {
targetNode = mController.findPrevious(rootNode);
} finally {
AccessibilityNodeInfoUtils.recycleNodes(rootNode);
}
return targetNode;
}
@Override
public AccessibilityNodeInfoCompat focusInitial(AccessibilityNodeInfoCompat root,
@SearchDirection int direction) {
if (direction == SEARCH_FOCUS_FORWARD) {
return mController.findFirst(root);
} else if (direction == SEARCH_FOCUS_BACKWARD) {
return mController.findLast(root);
} else {
return null;
}
}
}