|
|
|
|
@@ -1,484 +0,0 @@
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2011 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.example.android.apis.accessibility;
|
|
|
|
|
|
|
|
|
|
import com.example.android.apis.R;
|
|
|
|
|
|
|
|
|
|
import android.app.Activity;
|
|
|
|
|
import android.app.Service;
|
|
|
|
|
import android.content.Context;
|
|
|
|
|
import android.graphics.Canvas;
|
|
|
|
|
import android.graphics.Color;
|
|
|
|
|
import android.graphics.Paint;
|
|
|
|
|
import android.graphics.Rect;
|
|
|
|
|
import android.os.Bundle;
|
|
|
|
|
import android.text.TextUtils;
|
|
|
|
|
import android.util.AttributeSet;
|
|
|
|
|
import android.view.MotionEvent;
|
|
|
|
|
import android.view.View;
|
|
|
|
|
import android.view.accessibility.AccessibilityEvent;
|
|
|
|
|
import android.view.accessibility.AccessibilityManager;
|
|
|
|
|
import android.view.accessibility.AccessibilityNodeInfo;
|
|
|
|
|
import android.view.accessibility.AccessibilityNodeProvider;
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This sample demonstrates how a View can expose a virtual view sub-tree
|
|
|
|
|
* rooted at it. A virtual sub-tree is composed of imaginary Views
|
|
|
|
|
* that are reported as a part of the view hierarchy for accessibility
|
|
|
|
|
* purposes. This enables custom views that draw complex content to report
|
|
|
|
|
* them selves as a tree of virtual views, thus conveying their logical
|
|
|
|
|
* structure.
|
|
|
|
|
* <p>
|
|
|
|
|
* For example, a View may draw a monthly calendar as a grid of days while
|
|
|
|
|
* each such day may contains some events. From a perspective of the View
|
|
|
|
|
* hierarchy the calendar is composed of a single View but an accessibility
|
|
|
|
|
* service would benefit of traversing the logical structure of the calendar
|
|
|
|
|
* by examining each day and each event on that day.
|
|
|
|
|
* </p>
|
|
|
|
|
*/
|
|
|
|
|
public class AccessibilityNodeProviderActivity extends Activity {
|
|
|
|
|
/** Called when the activity is first created. */
|
|
|
|
|
@Override
|
|
|
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
|
setContentView(R.layout.accessibility_node_provider);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This class presents a View that is composed of three virtual children
|
|
|
|
|
* each of which is drawn with a different color and represents a region
|
|
|
|
|
* of the View that has different semantics compared to other such regions.
|
|
|
|
|
* While the virtual view tree exposed by this class is one level deep
|
|
|
|
|
* for simplicity, there is no bound on the complexity of that virtual
|
|
|
|
|
* sub-tree.
|
|
|
|
|
*/
|
|
|
|
|
public static class VirtualSubtreeRootView extends View {
|
|
|
|
|
|
|
|
|
|
/** Paint object for drawing the virtual sub-tree */
|
|
|
|
|
private final Paint mPaint = new Paint();
|
|
|
|
|
|
|
|
|
|
/** Temporary rectangle to minimize object creation. */
|
|
|
|
|
private final Rect mTempRect = new Rect();
|
|
|
|
|
|
|
|
|
|
/** Handle to the system accessibility service. */
|
|
|
|
|
private final AccessibilityManager mAccessibilityManager;
|
|
|
|
|
|
|
|
|
|
/** The virtual children of this View. */
|
|
|
|
|
private final List<VirtualView> mChildren = new ArrayList<VirtualView>();
|
|
|
|
|
|
|
|
|
|
/** The instance of the node provider for the virtual tree - lazily instantiated. */
|
|
|
|
|
private AccessibilityNodeProvider mAccessibilityNodeProvider;
|
|
|
|
|
|
|
|
|
|
/** The last hovered child used for event dispatching. */
|
|
|
|
|
private VirtualView mLastHoveredChild;
|
|
|
|
|
|
|
|
|
|
public VirtualSubtreeRootView(Context context, AttributeSet attrs) {
|
|
|
|
|
super(context, attrs);
|
|
|
|
|
mAccessibilityManager = (AccessibilityManager) context.getSystemService(
|
|
|
|
|
Service.ACCESSIBILITY_SERVICE);
|
|
|
|
|
createVirtualChildren();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@inheritDoc}
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public AccessibilityNodeProvider getAccessibilityNodeProvider() {
|
|
|
|
|
// Instantiate the provide only when requested. Since the system
|
|
|
|
|
// will call this method multiple times it is a good practice to
|
|
|
|
|
// cache the provider instance.
|
|
|
|
|
if (mAccessibilityNodeProvider == null) {
|
|
|
|
|
mAccessibilityNodeProvider = new VirtualDescendantsProvider();
|
|
|
|
|
}
|
|
|
|
|
return mAccessibilityNodeProvider;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@inheritDoc}
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public boolean dispatchHoverEvent(MotionEvent event) {
|
|
|
|
|
// This implementation assumes that the virtual children
|
|
|
|
|
// cannot overlap and are always visible. Do NOT use this
|
|
|
|
|
// code as a reference of how to implement hover event
|
|
|
|
|
// dispatch. Instead, refer to ViewGroup#dispatchHoverEvent.
|
|
|
|
|
boolean handled = false;
|
|
|
|
|
List<VirtualView> children = mChildren;
|
|
|
|
|
final int childCount = children.size();
|
|
|
|
|
for (int i = 0; i < childCount; i++) {
|
|
|
|
|
VirtualView child = children.get(i);
|
|
|
|
|
Rect childBounds = child.mBounds;
|
|
|
|
|
final int childCoordsX = (int) event.getX() + getScrollX();
|
|
|
|
|
final int childCoordsY = (int) event.getY() + getScrollY();
|
|
|
|
|
if (!childBounds.contains(childCoordsX, childCoordsY)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
final int action = event.getAction();
|
|
|
|
|
switch (action) {
|
|
|
|
|
case MotionEvent.ACTION_HOVER_ENTER: {
|
|
|
|
|
mLastHoveredChild = child;
|
|
|
|
|
handled |= onHoverVirtualView(child, event);
|
|
|
|
|
event.setAction(action);
|
|
|
|
|
} break;
|
|
|
|
|
case MotionEvent.ACTION_HOVER_MOVE: {
|
|
|
|
|
if (child == mLastHoveredChild) {
|
|
|
|
|
handled |= onHoverVirtualView(child, event);
|
|
|
|
|
event.setAction(action);
|
|
|
|
|
} else {
|
|
|
|
|
MotionEvent eventNoHistory = event.getHistorySize() > 0
|
|
|
|
|
? MotionEvent.obtainNoHistory(event) : event;
|
|
|
|
|
eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
|
|
|
|
|
onHoverVirtualView(mLastHoveredChild, eventNoHistory);
|
|
|
|
|
eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
|
|
|
|
|
onHoverVirtualView(child, eventNoHistory);
|
|
|
|
|
mLastHoveredChild = child;
|
|
|
|
|
eventNoHistory.setAction(MotionEvent.ACTION_HOVER_MOVE);
|
|
|
|
|
handled |= onHoverVirtualView(child, eventNoHistory);
|
|
|
|
|
if (eventNoHistory != event) {
|
|
|
|
|
eventNoHistory.recycle();
|
|
|
|
|
} else {
|
|
|
|
|
event.setAction(action);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
case MotionEvent.ACTION_HOVER_EXIT: {
|
|
|
|
|
mLastHoveredChild = null;
|
|
|
|
|
handled |= onHoverVirtualView(child, event);
|
|
|
|
|
event.setAction(action);
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!handled) {
|
|
|
|
|
handled |= onHoverEvent(event);
|
|
|
|
|
}
|
|
|
|
|
return handled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@inheritDoc}
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
|
|
|
|
// The virtual children are ordered horizontally next to
|
|
|
|
|
// each other and take the entire space of this View.
|
|
|
|
|
int offsetX = 0;
|
|
|
|
|
List<VirtualView> children = mChildren;
|
|
|
|
|
final int childCount = children.size();
|
|
|
|
|
for (int i = 0; i < childCount; i++) {
|
|
|
|
|
VirtualView child = children.get(i);
|
|
|
|
|
Rect childBounds = child.mBounds;
|
|
|
|
|
childBounds.set(offsetX, 0, offsetX + childBounds.width(), childBounds.height());
|
|
|
|
|
offsetX += childBounds.width();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@inheritDoc}
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
|
|
|
// The virtual children are ordered horizontally next to
|
|
|
|
|
// each other and take the entire space of this View.
|
|
|
|
|
int width = 0;
|
|
|
|
|
int height = 0;
|
|
|
|
|
List<VirtualView> children = mChildren;
|
|
|
|
|
final int childCount = children.size();
|
|
|
|
|
for (int i = 0; i < childCount; i++) {
|
|
|
|
|
VirtualView child = children.get(i);
|
|
|
|
|
width += child.mBounds.width();
|
|
|
|
|
height = Math.max(height, child.mBounds.height());
|
|
|
|
|
}
|
|
|
|
|
setMeasuredDimension(width, height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@inheritDoc}
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
protected void onDraw(Canvas canvas) {
|
|
|
|
|
// Draw the virtual children with the reusable Paint object
|
|
|
|
|
// and with the bounds and color which are child specific.
|
|
|
|
|
Rect drawingRect = mTempRect;
|
|
|
|
|
List<VirtualView> children = mChildren;
|
|
|
|
|
final int childCount = children.size();
|
|
|
|
|
for (int i = 0; i < childCount; i++) {
|
|
|
|
|
VirtualView child = children.get(i);
|
|
|
|
|
drawingRect.set(child.mBounds);
|
|
|
|
|
mPaint.setColor(child.mColor);
|
|
|
|
|
mPaint.setAlpha(child.mAlpha);
|
|
|
|
|
canvas.drawRect(drawingRect, mPaint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates the virtual children of this View.
|
|
|
|
|
*/
|
|
|
|
|
private void createVirtualChildren() {
|
|
|
|
|
// The virtual portion of the tree is one level deep. Note
|
|
|
|
|
// that implementations can use any way of representing and
|
|
|
|
|
// drawing virtual view.
|
|
|
|
|
VirtualView firstChild = new VirtualView(0, new Rect(0, 0, 150, 150), Color.RED,
|
|
|
|
|
"Virtual view 1");
|
|
|
|
|
mChildren.add(firstChild);
|
|
|
|
|
VirtualView secondChild = new VirtualView(1, new Rect(0, 0, 150, 150), Color.GREEN,
|
|
|
|
|
"Virtual view 2");
|
|
|
|
|
mChildren.add(secondChild);
|
|
|
|
|
VirtualView thirdChild = new VirtualView(2, new Rect(0, 0, 150, 150), Color.BLUE,
|
|
|
|
|
"Virtual view 3");
|
|
|
|
|
mChildren.add(thirdChild);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the selected state of a virtual view.
|
|
|
|
|
*
|
|
|
|
|
* @param virtualView The virtual view whose selected state to set.
|
|
|
|
|
* @param selected Whether the virtual view is selected.
|
|
|
|
|
*/
|
|
|
|
|
private void setVirtualViewSelected(VirtualView virtualView, boolean selected) {
|
|
|
|
|
virtualView.mAlpha = selected ? VirtualView.ALPHA_SELECTED : VirtualView.ALPHA_NOT_SELECTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handle a hover over a virtual view.
|
|
|
|
|
*
|
|
|
|
|
* @param virtualView The virtual view over which is hovered.
|
|
|
|
|
* @param event The event to dispatch.
|
|
|
|
|
* @return Whether the event was handled.
|
|
|
|
|
*/
|
|
|
|
|
private boolean onHoverVirtualView(VirtualView virtualView, MotionEvent event) {
|
|
|
|
|
// The implementation of hover event dispatch can be implemented
|
|
|
|
|
// in any way that is found suitable. However, each virtual View
|
|
|
|
|
// should fire a corresponding accessibility event whose source
|
|
|
|
|
// is that virtual view. Accessibility services get the event source
|
|
|
|
|
// as the entry point of the APIs for querying the window content.
|
|
|
|
|
final int action = event.getAction();
|
|
|
|
|
switch (action) {
|
|
|
|
|
case MotionEvent.ACTION_HOVER_ENTER: {
|
|
|
|
|
sendAccessibilityEventForVirtualView(virtualView,
|
|
|
|
|
AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
|
|
|
|
|
} break;
|
|
|
|
|
case MotionEvent.ACTION_HOVER_EXIT: {
|
|
|
|
|
sendAccessibilityEventForVirtualView(virtualView,
|
|
|
|
|
AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sends a properly initialized accessibility event for a virtual view..
|
|
|
|
|
*
|
|
|
|
|
* @param virtualView The virtual view.
|
|
|
|
|
* @param eventType The type of the event to send.
|
|
|
|
|
*/
|
|
|
|
|
private void sendAccessibilityEventForVirtualView(VirtualView virtualView, int eventType) {
|
|
|
|
|
// If touch exploration, i.e. the user gets feedback while touching
|
|
|
|
|
// the screen, is enabled we fire accessibility events.
|
|
|
|
|
if (mAccessibilityManager.isTouchExplorationEnabled()) {
|
|
|
|
|
AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
|
|
|
|
|
event.setPackageName(getContext().getPackageName());
|
|
|
|
|
event.setClassName(virtualView.getClass().getName());
|
|
|
|
|
event.setSource(VirtualSubtreeRootView.this, virtualView.mId);
|
|
|
|
|
event.getText().add(virtualView.mText);
|
|
|
|
|
getParent().requestSendAccessibilityEvent(VirtualSubtreeRootView.this, event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Finds a virtual view given its id.
|
|
|
|
|
*
|
|
|
|
|
* @param id The virtual view id.
|
|
|
|
|
* @return The found virtual view.
|
|
|
|
|
*/
|
|
|
|
|
private VirtualView findVirtualViewById(int id) {
|
|
|
|
|
List<VirtualView> children = mChildren;
|
|
|
|
|
final int childCount = children.size();
|
|
|
|
|
for (int i = 0; i < childCount; i++) {
|
|
|
|
|
VirtualView child = children.get(i);
|
|
|
|
|
if (child.mId == id) {
|
|
|
|
|
return child;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Represents a virtual View.
|
|
|
|
|
*/
|
|
|
|
|
private class VirtualView {
|
|
|
|
|
public static final int ALPHA_SELECTED = 255;
|
|
|
|
|
public static final int ALPHA_NOT_SELECTED = 127;
|
|
|
|
|
|
|
|
|
|
public final int mId;
|
|
|
|
|
public final int mColor;
|
|
|
|
|
public final Rect mBounds;
|
|
|
|
|
public final String mText;
|
|
|
|
|
public int mAlpha;
|
|
|
|
|
|
|
|
|
|
public VirtualView(int id, Rect bounds, int color, String text) {
|
|
|
|
|
mId = id;
|
|
|
|
|
mColor = color;
|
|
|
|
|
mBounds = bounds;
|
|
|
|
|
mText = text;
|
|
|
|
|
mAlpha = ALPHA_NOT_SELECTED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the provider that exposes the virtual View tree to accessibility
|
|
|
|
|
* services. From the perspective of an accessibility service the
|
|
|
|
|
* {@link AccessibilityNodeInfo}s it receives while exploring the sub-tree
|
|
|
|
|
* rooted at this View will be the same as the ones it received while
|
|
|
|
|
* exploring a View containing a sub-tree composed of real Views.
|
|
|
|
|
*/
|
|
|
|
|
private class VirtualDescendantsProvider extends AccessibilityNodeProvider {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@inheritDoc}
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
|
|
|
|
|
AccessibilityNodeInfo info = null;
|
|
|
|
|
if (virtualViewId == View.NO_ID) {
|
|
|
|
|
// We are requested to create an AccessibilityNodeInfo describing
|
|
|
|
|
// this View, i.e. the root of the virtual sub-tree. Note that the
|
|
|
|
|
// host View has an AccessibilityNodeProvider which means that this
|
|
|
|
|
// provider is responsible for creating the node info for that root.
|
|
|
|
|
info = AccessibilityNodeInfo.obtain(VirtualSubtreeRootView.this);
|
|
|
|
|
onInitializeAccessibilityNodeInfo(info);
|
|
|
|
|
// Add the virtual children of the root View.
|
|
|
|
|
List<VirtualView> children = mChildren;
|
|
|
|
|
final int childCount = children.size();
|
|
|
|
|
for (int i = 0; i < childCount; i++) {
|
|
|
|
|
VirtualView child = children.get(i);
|
|
|
|
|
info.addChild(VirtualSubtreeRootView.this, child.mId);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Find the view that corresponds to the given id.
|
|
|
|
|
VirtualView virtualView = findVirtualViewById(virtualViewId);
|
|
|
|
|
if (virtualView == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
// Obtain and initialize an AccessibilityNodeInfo with
|
|
|
|
|
// information about the virtual view.
|
|
|
|
|
info = AccessibilityNodeInfo.obtain();
|
|
|
|
|
info.addAction(AccessibilityNodeInfo.ACTION_SELECT);
|
|
|
|
|
info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION);
|
|
|
|
|
info.setPackageName(getContext().getPackageName());
|
|
|
|
|
info.setClassName(virtualView.getClass().getName());
|
|
|
|
|
info.setSource(VirtualSubtreeRootView.this, virtualViewId);
|
|
|
|
|
info.setBoundsInParent(virtualView.mBounds);
|
|
|
|
|
info.setParent(VirtualSubtreeRootView.this);
|
|
|
|
|
info.setText(virtualView.mText);
|
|
|
|
|
}
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@inheritDoc}
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String searched,
|
|
|
|
|
int virtualViewId) {
|
|
|
|
|
if (TextUtils.isEmpty(searched)) {
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
String searchedLowerCase = searched.toLowerCase();
|
|
|
|
|
List<AccessibilityNodeInfo> result = null;
|
|
|
|
|
if (virtualViewId == View.NO_ID) {
|
|
|
|
|
// If the search is from the root, i.e. this View, go over the virtual
|
|
|
|
|
// children and look for ones that contain the searched string since
|
|
|
|
|
// this View does not contain text itself.
|
|
|
|
|
List<VirtualView> children = mChildren;
|
|
|
|
|
final int childCount = children.size();
|
|
|
|
|
for (int i = 0; i < childCount; i++) {
|
|
|
|
|
VirtualView child = children.get(i);
|
|
|
|
|
String textToLowerCase = child.mText.toLowerCase();
|
|
|
|
|
if (textToLowerCase.contains(searchedLowerCase)) {
|
|
|
|
|
if (result == null) {
|
|
|
|
|
result = new ArrayList<AccessibilityNodeInfo>();
|
|
|
|
|
}
|
|
|
|
|
result.add(createAccessibilityNodeInfo(child.mId));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// If the search is from a virtual view, find the view. Since the tree
|
|
|
|
|
// is one level deep we add a node info for the child to the result if
|
|
|
|
|
// the child contains the searched text.
|
|
|
|
|
VirtualView virtualView = findVirtualViewById(virtualViewId);
|
|
|
|
|
if (virtualView != null) {
|
|
|
|
|
String textToLowerCase = virtualView.mText.toLowerCase();
|
|
|
|
|
if (textToLowerCase.contains(searchedLowerCase)) {
|
|
|
|
|
result = new ArrayList<AccessibilityNodeInfo>();
|
|
|
|
|
result.add(createAccessibilityNodeInfo(virtualViewId));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (result == null) {
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@inheritDoc}
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public boolean performAction(int virtualViewId, int action, Bundle arguments) {
|
|
|
|
|
if (virtualViewId == View.NO_ID) {
|
|
|
|
|
// Perform the action on the host View.
|
|
|
|
|
switch (action) {
|
|
|
|
|
case AccessibilityNodeInfo.ACTION_SELECT:
|
|
|
|
|
if (!isSelected()) {
|
|
|
|
|
setSelected(true);
|
|
|
|
|
return isSelected();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION:
|
|
|
|
|
if (isSelected()) {
|
|
|
|
|
setSelected(false);
|
|
|
|
|
return !isSelected();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Find the view that corresponds to the given id.
|
|
|
|
|
VirtualView child = findVirtualViewById(virtualViewId);
|
|
|
|
|
if (child == null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Perform the action on a virtual view.
|
|
|
|
|
switch (action) {
|
|
|
|
|
case AccessibilityNodeInfo.ACTION_SELECT:
|
|
|
|
|
setVirtualViewSelected(child, true);
|
|
|
|
|
invalidate();
|
|
|
|
|
return true;
|
|
|
|
|
case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION:
|
|
|
|
|
setVirtualViewSelected(child, false);
|
|
|
|
|
invalidate();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|