ExploreByTouchHelper.java revision 6eb3cdf42d5382aef6b6a6afd7c305dbc27885b9
16eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette/*
26eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Copyright (C) 2013 The Android Open Source Project
36eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette *
46eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License");
56eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * you may not use this file except in compliance with the License.
66eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * You may obtain a copy of the License at
76eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette *
86eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette *      http://www.apache.org/licenses/LICENSE-2.0
96eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette *
106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Unless required by applicable law or agreed to in writing, software
116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS,
126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * See the License for the specific language governing permissions and
146eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * limitations under the License.
156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */
166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverettepackage android.support.v4.widget;
186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.content.Context;
206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.graphics.Rect;
216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.os.Bundle;
226eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.AccessibilityDelegateCompat;
236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.MotionEventCompat;
246eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.ViewCompat;
256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.ViewParentCompat;
266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.accessibility.AccessibilityEventCompat;
276eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.accessibility.AccessibilityManagerCompat;
286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
296eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.accessibility.AccessibilityRecordCompat;
316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.view.MotionEvent;
326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.view.View;
336eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.view.ViewParent;
346eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.view.accessibility.AccessibilityEvent;
356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.view.accessibility.AccessibilityManager;
366eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
376eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport java.util.LinkedList;
386eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport java.util.List;
396eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
406eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette/**
416eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * ExploreByTouchHelper is a utility class for implementing accessibility
426eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * support in custom {@link View}s that represent a collection of View-like
436eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * logical items. It extends {@link AccessibilityNodeProviderCompat} and
446eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * simplifies many aspects of providing information to accessibility services
456eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * and managing accessibility focus. This class does not currently support
466eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * hierarchies of logical items.
476eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p>
486eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * This should be applied to the parent view using
496eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * {@link ViewCompat#setAccessibilityDelegate}:
506eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette *
516eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <pre>
526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * mAccessHelper = ExploreByTouchHelper.create(someView, mAccessHelperCallback);
536eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * ViewCompat.setAccessibilityDelegate(someView, mAccessHelper);
546eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * </pre>
556eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */
566eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverettepublic abstract class ExploreByTouchHelper extends AccessibilityDelegateCompat {
576eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /** Virtual node identifier value for invalid nodes. */
586eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    public static final int INVALID_ID = Integer.MIN_VALUE;
596eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
606eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /** Default class name used for virtual views. */
616eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private static final String DEFAULT_CLASS_NAME = View.class.getName();
626eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
636eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    // Temporary, reusable data structures.
646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private final Rect mTempScreenRect = new Rect();
656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private final Rect mTempParentRect = new Rect();
666eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private final Rect mTempVisibleRect = new Rect();
676eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private final int[] mTempGlobalRect = new int[2];
686eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
696eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /** System accessibility manager, used to check state and send events. */
706eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private final AccessibilityManager mManager;
716eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
726eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /** View whose internal structure is exposed through this helper. */
736eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private final View mView;
746eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
756eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /** Node provider that handles creating nodes and performing actions. */
766eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private ExploreByTouchNodeProvider mNodeProvider;
776eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
786eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /** Virtual view id for the currently focused logical item. */
796eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private int mFocusedVirtualViewId = INVALID_ID;
806eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
816eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /** Virtual view id for the currently hovered logical item. */
826eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private int mHoveredVirtualViewId = INVALID_ID;
836eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
846eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
856eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Factory method to create a new {@link ExploreByTouchHelper}.
866eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param forView View whose logical children are exposed by this helper.
886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    public ExploreByTouchHelper(View forView) {
906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (forView == null) {
916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            throw new IllegalArgumentException("View may not be null");
926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
936eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
946eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        mView = forView;
956eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final Context context = forView.getContext();
966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        mManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
976eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
986eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
996eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
1006eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Returns the {@link AccessibilityNodeProviderCompat} for this helper.
1016eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
1026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param host View whose logical children are exposed by this helper.
1036eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return The accessibility node provider for this helper.
1046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
1056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    @Override
1066eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
1076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (mNodeProvider == null) {
1086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            mNodeProvider = new ExploreByTouchNodeProvider();
1096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
1106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        return mNodeProvider;
1116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
1126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
1136eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
1146eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Dispatches hover {@link MotionEvent}s to the virtual view hierarchy when
1156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * the Explore by Touch feature is enabled.
1166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
1176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * This method should be called by overriding
1186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link View#dispatchHoverEvent}:
1196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
1206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <pre>&#64;Override
1216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * public boolean dispatchHoverEvent(MotionEvent event) {
1226eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *   if (mHelper.dispatchHoverEvent(this, event) {
1236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *     return true;
1246eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *   }
1256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *   return super.dispatchHoverEvent(event);
1266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * }
1276eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * </pre>
1286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
1296eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param event The hover event to dispatch to the virtual view hierarchy.
1306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return Whether the hover event was handled.
1316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
1326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    public boolean dispatchHoverEvent(MotionEvent event) {
1336eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (!mManager.isEnabled()
1346eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                || !AccessibilityManagerCompat.isTouchExplorationEnabled(mManager)) {
1356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            return false;
1366eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
1376eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
1386eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        switch (event.getAction()) {
1396eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            case MotionEventCompat.ACTION_HOVER_MOVE:
1406eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            case MotionEventCompat.ACTION_HOVER_ENTER:
1416eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                final int virtualViewId = getVirtualViewAt(event.getX(), event.getY());
1426eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                updateHoveredVirtualView(virtualViewId);
1436eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return (virtualViewId != INVALID_ID);
1446eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            case MotionEventCompat.ACTION_HOVER_EXIT:
1456eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                if (mFocusedVirtualViewId != INVALID_ID) {
1466eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                    updateHoveredVirtualView(INVALID_ID);
1476eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                    return true;
1486eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                }
1496eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return false;
1506eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            default:
1516eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return false;
1526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
1536eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
1546eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
1556eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
1566eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Populates an event of the specified type with information about an item
1576eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * and attempts to send it up through the view hierarchy.
1586eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
1596eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * You should call this method after performing a user action that normally
1606eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * fires an accessibility event, such as clicking on an item.
1616eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
1626eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <pre>public void performItemClick(T item) {
1636eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *   ...
1646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *   sendEventForVirtualViewId(item.id, AccessibilityEvent.TYPE_VIEW_CLICKED);
1656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * }
1666eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * </pre>
1676eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
1686eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param virtualViewId The virtual view id for which to send an event.
1696eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param eventType The type of event to send.
1706eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return true if the event was sent successfully.
1716eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
1726eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    public boolean sendEventForVirtualView(int virtualViewId, int eventType) {
1736eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if ((virtualViewId == INVALID_ID) || !mManager.isEnabled()) {
1746eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            return false;
1756eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
1766eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
1776eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final ViewParent parent = mView.getParent();
1786eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (parent == null) {
1796eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            return false;
1806eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
1816eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
1826eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final AccessibilityEvent event = createEvent(virtualViewId, eventType);
1836eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        return ViewParentCompat.requestSendAccessibilityEvent(parent, mView, event);
1846eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
1856eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
1866eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
1876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Notifies the accessibility framework that the properties of the parent
1886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * view have changed.
1896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
1906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * You <b>must</b> call this method after adding or removing items from the
1916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * parent view.
1926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
1936eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    public void invalidateRoot() {
1946eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        invalidateVirtualView(View.NO_ID);
1956eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
1966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
1976eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
1986eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Notifies the accessibility framework that the properties of a particular
1996eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * item have changed.
2006eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
2016eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * You <b>must</b> call this method after changing any of the properties set
2026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * in {@link #onPopulateNodeForVirtualView}.
2036eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
2046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param virtualViewId The virtual view id to invalidate.
2056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
2066eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    public void invalidateVirtualView(int virtualViewId) {
2076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        sendEventForVirtualView(
2086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                virtualViewId, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
2096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
2106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
2116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
2126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Sets the currently hovered item, sending hover accessibility events as
2136eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * necessary to maintain the correct state.
2146eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
2156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param virtualViewId The virtual view id for the item currently being
2166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *            hovered, or {@link #INVALID_ID} if no item is hovered within
2176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *            the parent view.
2186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
2196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private void updateHoveredVirtualView(int virtualViewId) {
2206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (mHoveredVirtualViewId == virtualViewId) {
2216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            return;
2226eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
2236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
2246eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final int previousVirtualViewId = mHoveredVirtualViewId;
2256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        mHoveredVirtualViewId = virtualViewId;
2266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
2276eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Stay consistent with framework behavior by sending ENTER/EXIT pairs
2286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // in reverse order. This is accurate as of API 18.
2296eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        sendEventForVirtualView(virtualViewId, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER);
2306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        sendEventForVirtualView(
2316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                previousVirtualViewId, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT);
2326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
2336eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
2346eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
2356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Constructs and returns an {@link AccessibilityEvent} for the specified
2366eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * virtual view id, which includes the host view ({@link View#NO_ID}).
2376eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
2386eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param virtualViewId The virtual view id for the item for which to
2396eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *            construct an event.
2406eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param eventType The type of event to construct.
2416eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return An {@link AccessibilityEvent} populated with information about
2426eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *         the specified item.
2436eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
2446eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private AccessibilityEvent createEvent(int virtualViewId, int eventType) {
2456eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        switch (virtualViewId) {
2466eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            case View.NO_ID:
2476eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return createEventForHost(eventType);
2486eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            default:
2496eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return createEventForChild(virtualViewId, eventType);
2506eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
2516eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
2526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
2536eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
2546eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Constructs and returns an {@link AccessibilityEvent} for the host node.
2556eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
2566eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param eventType The type of event to construct.
2576eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return An {@link AccessibilityEvent} populated with information about
2586eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *         the specified item.
2596eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
2606eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private AccessibilityEvent createEventForHost(int eventType) {
2616eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
2626eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        ViewCompat.onInitializeAccessibilityEvent(mView, event);
2636eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        return event;
2646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
2656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
2666eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
2676eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Constructs and returns an {@link AccessibilityEvent} populated with
2686eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * information about the specified item.
2696eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
2706eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param virtualViewId The virtual view id for the item for which to
2716eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *            construct an event.
2726eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param eventType The type of event to construct.
2736eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return An {@link AccessibilityEvent} populated with information about
2746eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *         the specified item.
2756eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
2766eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
2776eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
2786eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        event.setEnabled(true);
2796eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        event.setClassName(DEFAULT_CLASS_NAME);
2806eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
2816eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Allow the client to populate the event.
2826eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        onPopulateEventForVirtualView(virtualViewId, event);
2836eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
2846eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Make sure the developer is following the rules.
2856eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (event.getText().isEmpty() && (event.getContentDescription() == null)) {
2866eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            throw new RuntimeException("Callbacks must add text or a content description in "
2876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                    + "populateEventForVirtualViewId()");
2886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
2896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
2906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Don't allow the client to override these properties.
2916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        event.setPackageName(mView.getContext().getPackageName());
2926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
2936eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
2946eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        record.setSource(mView, virtualViewId);
2956eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
2966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        return event;
2976eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
2986eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
2996eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
3006eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
3016eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * specified virtual view id, which includes the host view
3026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * ({@link View#NO_ID}).
3036eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
3046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param virtualViewId The virtual view id for the item for which to
3056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *            construct a node.
3066eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return An {@link AccessibilityNodeInfoCompat} populated with information
3076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *         about the specified item.
3086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
3096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private AccessibilityNodeInfoCompat createNode(int virtualViewId) {
3106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        switch (virtualViewId) {
3116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            case View.NO_ID:
3126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return createNodeForHost();
3136eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            default:
3146eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return createNodeForChild(virtualViewId);
3156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
3166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
3176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
3186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
3196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
3206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * host view populated with its virtual descendants.
3216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
3226eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return An {@link AccessibilityNodeInfoCompat} for the parent node.
3236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
3246eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private AccessibilityNodeInfoCompat createNodeForHost() {
3256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain(mView);
3266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        ViewCompat.onInitializeAccessibilityNodeInfo(mView, node);
3276eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
3286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Add the virtual descendants.
3296eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final LinkedList<Integer> virtualViewIds = new LinkedList<Integer>();
3306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        getVisibleVirtualViews(virtualViewIds);
3316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
3326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        for (Integer childVirtualViewId : virtualViewIds) {
3336eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            node.addChild(mView, childVirtualViewId);
3346eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
3356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
3366eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        return node;
3376eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
3386eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
3396eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
3406eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
3416eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * specified item. Automatically manages accessibility focus actions.
3426eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
3436eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Allows the implementing class to specify most node properties, but
3446eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * overrides the following:
3456eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <ul>
3466eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>{@link AccessibilityNodeInfoCompat#setPackageName}
3476eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>{@link AccessibilityNodeInfoCompat#setClassName}
3486eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>{@link AccessibilityNodeInfoCompat#setParent(View)}
3496eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>{@link AccessibilityNodeInfoCompat#setSource(View, int)}
3506eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
3516eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)}
3526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * </ul>
3536eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
3546eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Uses the bounds of the parent view and the parent-relative bounding
3556eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * rectangle specified by
3566eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#getBoundsInParent} to automatically
3576eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * update the following properties:
3586eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <ul>
3596eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
3606eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent}
3616eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * </ul>
3626eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
3636eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param virtualViewId The virtual view id for item for which to construct
3646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *            a node.
3656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return An {@link AccessibilityNodeInfoCompat} for the specified item.
3666eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
3676eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private AccessibilityNodeInfoCompat createNodeForChild(int virtualViewId) {
3686eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain();
3696eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
3706eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Ensure the client has good defaults.
3716eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        node.setEnabled(true);
3726eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        node.setClassName(DEFAULT_CLASS_NAME);
3736eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
3746eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Allow the client to populate the node.
3756eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        onPopulateNodeForVirtualView(virtualViewId, node);
3766eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
3776eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Make sure the developer is following the rules.
3786eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if ((node.getText() == null) && (node.getContentDescription() == null)) {
3796eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            throw new RuntimeException("Callbacks must add text or a content description in "
3806eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                    + "populateNodeForVirtualViewId()");
3816eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
3826eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
3836eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        node.getBoundsInParent(mTempParentRect);
3846eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (mTempParentRect.isEmpty()) {
3856eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            throw new RuntimeException("Callbacks must set parent bounds in "
3866eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                    + "populateNodeForVirtualViewId()");
3876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
3886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
3896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final int actions = node.getActions();
3906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if ((actions & AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS) != 0) {
3916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in "
3926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                    + "populateNodeForVirtualViewId()");
3936eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
3946eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if ((actions & AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS) != 0) {
3956eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in "
3966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                    + "populateNodeForVirtualViewId()");
3976eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
3986eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
3996eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Don't allow the client to override these properties.
4006eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        node.setPackageName(mView.getContext().getPackageName());
4016eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        node.setSource(mView, virtualViewId);
4026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        node.setParent(mView);
4036eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
4046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Manage internal accessibility focus state.
4056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (mFocusedVirtualViewId == virtualViewId) {
4066eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            node.setAccessibilityFocused(true);
4076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
4086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        } else {
4096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            node.setAccessibilityFocused(false);
4106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            node.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
4116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
4126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
4136eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Set the visibility based on the parent bound.
4146eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (intersectVisibleToUser(mTempParentRect)) {
4156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            node.setVisibleToUser(true);
4166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            node.setBoundsInParent(mTempParentRect);
4176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
4186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
4196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Calculate screen-relative bound.
4206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        mView.getLocationOnScreen(mTempGlobalRect);
4216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final int offsetX = mTempGlobalRect[0];
4226eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        final int offsetY = mTempGlobalRect[1];
4236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        mTempScreenRect.set(mTempParentRect);
4246eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        mTempScreenRect.offset(offsetX, offsetY);
4256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        node.setBoundsInScreen(mTempScreenRect);
4266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
4276eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        return node;
4286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
4296eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
4306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private boolean performAction(int virtualViewId, int action, Bundle arguments) {
4316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        switch (virtualViewId) {
4326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            case View.NO_ID:
4336eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return performActionForHost(action, arguments);
4346eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            default:
4356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return performActionForChild(virtualViewId, action, arguments);
4366eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
4376eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
4386eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
4396eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private boolean performActionForHost(int action, Bundle arguments) {
4406eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        return ViewCompat.performAccessibilityAction(mView, action, arguments);
4416eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
4426eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
4436eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private boolean performActionForChild(int virtualViewId, int action, Bundle arguments) {
4446eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        switch (action) {
4456eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS:
4466eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
4476eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return manageFocusForChild(virtualViewId, action, arguments);
4486eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            default:
4496eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return onPerformActionForVirtualView(virtualViewId, action, arguments);
4506eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
4516eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
4526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
4536eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private boolean manageFocusForChild(int virtualViewId, int action, Bundle arguments) {
4546eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        switch (action) {
4556eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS:
4566eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                if (!isAccessibilityFocused(virtualViewId)) {
4576eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                    return requestAccessibilityFocus(virtualViewId);
4586eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                }
4596eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return false;
4606eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
4616eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                if (isAccessibilityFocused(virtualViewId)) {
4626eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                    clearAccessibilityFocus(virtualViewId);
4636eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                    return true;
4646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                }
4656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return false;
4666eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            default:
4676eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return false;
4686eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
4696eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
4706eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
4716eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
4726eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Computes whether the specified {@link Rect} intersects with the visible
4736eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * portion of its parent {@link View}. Modifies {@code localRect} to contain
4746eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * only the visible portion.
4756eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
4766eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param localRect A rectangle in local (parent) coordinates.
4776eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return Whether the specified {@link Rect} is visible on the screen.
4786eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
4796eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private boolean intersectVisibleToUser(Rect localRect) {
4806eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Missing or empty bounds mean this view is not visible.
4816eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if ((localRect == null) || localRect.isEmpty()) {
4826eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            return false;
4836eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
4846eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
4856eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Attached to invisible window means this view is not visible.
4866eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (mView.getWindowVisibility() != View.VISIBLE) {
4876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            return false;
4886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
4896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
4906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // An invisible predecessor means that this view is not visible.
4916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        Object viewParent = this;
4926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        while (viewParent instanceof View) {
4936eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            final View view = (View) viewParent;
4946eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            if ((ViewCompat.getAlpha(view) <= 0) || (view.getVisibility() != View.VISIBLE)) {
4956eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                return false;
4966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            }
4976eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            viewParent = view.getParent();
4986eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
4996eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
5006eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // A null parent implies the view is not visible.
5016eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (viewParent == null) {
5026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            return false;
5036eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
5046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
5056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // If no portion of the parent is visible, this view is not visible.
5066eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (!mView.getLocalVisibleRect(mTempVisibleRect)) {
5076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            return false;
5086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
5096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
5106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // Check if the view intersects the visible portion of the parent.
5116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        return localRect.intersect(mTempVisibleRect);
5126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
5136eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
5146eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
5156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Returns whether this virtual view is accessibility focused.
5166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
5176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return True if the view is accessibility focused.
5186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
5196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private boolean isAccessibilityFocused(int virtualViewId) {
5206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        return (mFocusedVirtualViewId == virtualViewId);
5216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
5226eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
5236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
5246eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Attempts to give accessibility focus to a virtual view.
5256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
5266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * A virtual view will not actually take focus if
5276eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityManager#isEnabled()} returns false,
5286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityManager#isTouchExplorationEnabled()} returns false,
5296eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * or the view already has accessibility focus.
5306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
5316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param virtualViewId The id of the virtual view on which to place
5326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *            accessibility focus.
5336eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return Whether this virtual view actually took accessibility focus.
5346eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
5356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private boolean requestAccessibilityFocus(int virtualViewId) {
5366eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (!mManager.isEnabled()
5376eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                || !AccessibilityManagerCompat.isTouchExplorationEnabled(mManager)) {
5386eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            return false;
5396eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
5406eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // TODO: Check virtual view visibility.
5416eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (!isAccessibilityFocused(virtualViewId)) {
5426eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            mFocusedVirtualViewId = virtualViewId;
5436eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            // TODO: Only invalidate virtual view bounds.
5446eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            mView.invalidate();
5456eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            sendEventForVirtualView(virtualViewId,
5466eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                    AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
5476eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            notifyAccessibilityStateChanged(virtualViewId);
5486eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            return true;
5496eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
5506eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        return false;
5516eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
5526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
5536eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
5546eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Attempts to clear accessibility focus from a virtual view.
5556eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
5566eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private void clearAccessibilityFocus(int virtualViewId) {
5576eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        if (isAccessibilityFocused(virtualViewId)) {
5586eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            mFocusedVirtualViewId = INVALID_ID;
5596eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            mView.invalidate();
5606eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            sendEventForVirtualView(virtualViewId,
5616eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette                    AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
5626eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            notifyAccessibilityStateChanged(virtualViewId);
5636eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
5646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
5656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
5666eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private void notifyAccessibilityStateChanged(int virtualViewId) {
5676eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        // TODO: This method is not visible.
5686eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
5696eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
5706eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
5716eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Provides a mapping between view-relative coordinates and logical
5726eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * items.
5736eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
5746eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param x The view-relative x coordinate
5756eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param y The view-relative y coordinate
5766eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return virtual view identifier for the logical item under
5776eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *         coordinates (x,y)
5786eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
5796eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    protected abstract int getVirtualViewAt(float x, float y);
5806eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
5816eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
5826eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Populates a list with the view's visible items. The ordering of items
5836eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * within {@code virtualViewIds} specifies order of accessibility focus
5846eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * traversal.
5856eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
5866eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param virtualViewIds The list to populate with visible items
5876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
5886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    protected abstract void getVisibleVirtualViews(List<Integer> virtualViewIds);
5896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
5906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
5916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Populates an {@link AccessibilityEvent} with information about the
5926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * specified item.
5936eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
5946eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Implementations <b>must</b> populate the following required fields:
5956eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <ul>
5966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>event text, see {@link AccessibilityEvent#getText} or
5976eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityEvent#setContentDescription}
5986eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * </ul>
5996eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
6006eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * The helper class automatically populates the following fields with
6016eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * default values, but implementations may optionally override them:
6026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <ul>
6036eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>item class name, set to android.view.View, see
6046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityEvent#setClassName}
6056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * </ul>
6066eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
6076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * The following required fields are automatically populated by the
6086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * helper class and may not be overridden:
6096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <ul>
6106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>package name, set to the package of the host view's
6116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link Context}, see {@link AccessibilityEvent#setPackageName}
6126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>event source, set to the host view and virtual view identifier,
6136eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * see {@link AccessibilityRecordCompat#setSource(View, int)}
6146eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * </ul>
6156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
6166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param virtualViewId The virtual view id for the item for which to
6176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *            populate the event
6186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param event The event to populate
6196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
6206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    protected abstract void onPopulateEventForVirtualView(
6216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            int virtualViewId, AccessibilityEvent event);
6226eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
6236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
6246eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Populates an {@link AccessibilityNodeInfoCompat} with information
6256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * about the specified item.
6266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
6276eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Implementations <b>must</b> populate the following required fields:
6286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <ul>
6296eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>event text, see {@link AccessibilityNodeInfoCompat#setText} or
6306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#setContentDescription}
6316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>bounds in parent coordinates, see
6326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#setBoundsInParent}
6336eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * </ul>
6346eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
6356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * The helper class automatically populates the following fields with
6366eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * default values, but implementations may optionally override them:
6376eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <ul>
6386eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>enabled state, set to true, see
6396eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#setEnabled}
6406eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>item class name, identical to the class name set by
6416eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link #onPopulateEventForVirtualView}, see
6426eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#setClassName}
6436eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * </ul>
6446eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
6456eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * The following required fields are automatically populated by the
6466eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * helper class and may not be overridden:
6476eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <ul>
6486eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>package name, identical to the package name set by
6496eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link #onPopulateEventForVirtualView}, see
6506eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#setPackageName}
6516eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>node source, identical to the event source set in
6526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link #onPopulateEventForVirtualView}, see
6536eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#setSource(View, int)}
6546eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>parent view, set to the host view, see
6556eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#setParent(View)}
6566eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>visibility, computed based on parent-relative bounds, see
6576eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#setVisibleToUser}
6586eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>accessibility focus, computed based on internal helper state, see
6596eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#setAccessibilityFocused}
6606eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <li>bounds in screen coordinates, computed based on host view bounds,
6616eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * see {@link AccessibilityNodeInfoCompat#setBoundsInScreen}
6626eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * </ul>
6636eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
6646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Additionally, the helper class automatically handles accessibility
6656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * focus management by adding the appropriate
6666eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS} or
6676eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
6686eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * action. Implementations must <b>never</b> manually add these actions.
6696eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
6706eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * The helper class also automatically modifies parent- and
6716eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * screen-relative bounds to reflect the portion of the item visible
6726eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * within its parent.
6736eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
6746eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param virtualViewId The virtual view identifier of the item for
6756eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *            which to populate the node
6766eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param node The node to populate
6776eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
6786eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    protected abstract void onPopulateNodeForVirtualView(
6796eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            int virtualViewId, AccessibilityNodeInfoCompat node);
6806eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
6816eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
6826eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Performs the specified accessibility action on the item associated
6836eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * with the virtual view identifier. See
6846eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#performAction(int, Bundle)} for
6856eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * more information.
6866eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
6876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Implementations <b>must</b> handle any actions added manually in
6886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link #onPopulateNodeForVirtualView}.
6896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * <p>
6906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * The helper class automatically handles focus management resulting
6916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * from {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS}
6926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * and
6936eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
6946eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * actions.
6956eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *
6966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param virtualViewId The virtual view identifier of the item on which
6976eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *            to perform the action
6986eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param action The accessibility action to perform
6996eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @param arguments (Optional) A bundle with additional arguments, or
7006eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     *            null
7016eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * @return true if the action was performed
7026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
7036eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    protected abstract boolean onPerformActionForVirtualView(
7046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            int virtualViewId, int action, Bundle arguments);
7056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
7066eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    /**
7076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * Exposes a virtual view hierarchy to the accessibility framework. Only
7086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     * used in API 16+.
7096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette     */
7106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    private class ExploreByTouchNodeProvider extends AccessibilityNodeProviderCompat {
7116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        @Override
7126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
7136eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            return ExploreByTouchHelper.this.createNode(virtualViewId);
7146eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
7156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette
7166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        @Override
7176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        public boolean performAction(int virtualViewId, int action, Bundle arguments) {
7186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette            return ExploreByTouchHelper.this.performAction(virtualViewId, action, arguments);
7196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette        }
7206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette    }
7216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette}
722