public class

AccessibilityDelegateCompat

extends java.lang.Object

 java.lang.Object

↳androidx.core.view.AccessibilityDelegateCompat

Subclasses:

PreferenceRecyclerViewAccessibilityDelegate, RecyclerViewAccessibilityDelegate, RecyclerViewAccessibilityDelegate.ItemDelegate, ExploreByTouchHelper

Gradle dependencies

compile group: 'androidx.core', name: 'core', version: '1.15.0-alpha02'

  • groupId: androidx.core
  • artifactId: core
  • version: 1.15.0-alpha02

Artifact androidx.core:core:1.15.0-alpha02 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.core:core com.android.support:support-compat

Androidx class mapping:

androidx.core.view.AccessibilityDelegateCompat android.support.v4.view.AccessibilityDelegateCompat

Overview

Helper for accessing .

Note: On platform versions prior to , delegate methods on views in the android.widget.* package are called before host methods. This prevents certain properties such as class name from being modified by overriding AccessibilityDelegateCompat.onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat), as any changes will be overwritten by the host class.

Starting in , delegate methods are called after host methods, which all properties to be modified without being overwritten by the host class.

Summary

Constructors
publicAccessibilityDelegateCompat()

Creates a new instance.

publicAccessibilityDelegateCompat(AccessibilityDelegate originalDelegate)

Methods
public booleandispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event)

Dispatches an to the host View first and then to its children for adding their text content to the event.

public AccessibilityNodeProviderCompatgetAccessibilityNodeProvider(View host)

Gets the provider for managing a virtual view hierarchy rooted at this View and reported to s that explore the window content.

public voidonInitializeAccessibilityEvent(View host, AccessibilityEvent event)

Initializes an with information about the the host View which is the event source.

public voidonInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info)

Initializes an AccessibilityNodeInfoCompat with information about the host view.

public voidonPopulateAccessibilityEvent(View host, AccessibilityEvent event)

Gives a chance to the host View to populate the accessibility event with its text content.

public booleanonRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event)

Called when a child of the host View has requested sending an and gives an opportunity to the parent (the host) to augment the event.

public booleanperformAccessibilityAction(View host, int action, Bundle args)

Performs the specified accessibility action on the view.

public voidsendAccessibilityEvent(View host, int eventType)

Sends an accessibility event of the given type.

public voidsendAccessibilityEventUnchecked(View host, AccessibilityEvent event)

Sends an accessibility event.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public AccessibilityDelegateCompat()

Creates a new instance.

public AccessibilityDelegateCompat(AccessibilityDelegate originalDelegate)

Methods

public void sendAccessibilityEvent(View host, int eventType)

Sends an accessibility event of the given type. If accessibility is not enabled this method has no effect.

The default implementation behaves as View#sendAccessibilityEvent(int) for the case of no accessibility delegate been set.

Parameters:

host: The View hosting the delegate.
eventType: The type of the event to send.

See also: View#sendAccessibilityEvent(int)

public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event)

Sends an accessibility event. This method behaves exactly as AccessibilityDelegateCompat.sendAccessibilityEvent(View, int) but takes as an argument an empty and does not perform a check whether accessibility is enabled.

The default implementation behaves as View#sendAccessibilityEventUnchecked(AccessibilityEvent) for the case of no accessibility delegate been set.

Parameters:

host: The View hosting the delegate.
event: The event to send.

See also: View#sendAccessibilityEventUnchecked(AccessibilityEvent)

public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event)

Dispatches an to the host View first and then to its children for adding their text content to the event.

The default implementation behaves as View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) for the case of no accessibility delegate been set.

Parameters:

host: The View hosting the delegate.
event: The event.

Returns:

True if the event population was completed.

See also: View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)

public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event)

Gives a chance to the host View to populate the accessibility event with its text content.

The default implementation behaves as ViewCompat#onPopulateAccessibilityEvent(AccessibilityEvent) for the case of no accessibility delegate been set.

Parameters:

host: The View hosting the delegate.
event: The accessibility event which to populate.

See also: ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)

public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event)

Initializes an with information about the the host View which is the event source.

The default implementation behaves as ViewCompat#onInitalizeAccessibilityEvent(View v, AccessibilityEvent event) for the case of no accessibility delegate been set.

Parameters:

host: The View hosting the delegate.
event: The event to initialize.

See also: ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)

public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info)

Initializes an AccessibilityNodeInfoCompat with information about the host view.

The default implementation behaves as ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat) for the case of no accessibility delegate been set.

Parameters:

host: The View hosting the delegate.
info: The instance to initialize.

See also: ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)

public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event)

Called when a child of the host View has requested sending an and gives an opportunity to the parent (the host) to augment the event.

The default implementation behaves as ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent) for the case of no accessibility delegate been set.

Parameters:

host: The View hosting the delegate.
child: The child which requests sending the event.
event: The event to be sent.

Returns:

True if the event should be sent

See also: ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)

public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host)

Gets the provider for managing a virtual view hierarchy rooted at this View and reported to s that explore the window content.

The default implementation behaves as ViewCompat#getAccessibilityNodeProvider(View) for the case of no accessibility delegate been set.

Returns:

The provider.

See also: AccessibilityNodeProviderCompat

public boolean performAccessibilityAction(View host, int action, Bundle args)

Performs the specified accessibility action on the view. For possible accessibility actions look at AccessibilityNodeInfoCompat.

The default implementation behaves as View#performAccessibilityAction(int, Bundle) for the case of no accessibility delegate been set.

Parameters:

host: View on which to perform the action.
action: The action to perform.
args: Optional action arguments.

Returns:

Whether the action was performed.

See also: View#performAccessibilityAction(int, Bundle)

Source

/*
 * Copyright 2018 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 androidx.core.view;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;

import android.accessibilityservice.AccessibilityService;
import android.os.Build;
import android.os.Bundle;
import android.text.style.ClickableSpan;
import android.util.SparseArray;
import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.core.R;
import androidx.core.view.accessibility.AccessibilityClickableSpanCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
import androidx.core.view.accessibility.AccessibilityNodeProviderCompat;

import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.List;

/**
 * Helper for accessing {@link AccessibilityDelegate}.
 * <p>
 * <strong>Note:</strong> On platform versions prior to
 * {@link Build.VERSION_CODES#M API 23}, delegate methods on
 * views in the {@code android.widget.*} package are called <i>before</i>
 * host methods. This prevents certain properties such as class name from
 * being modified by overriding
 * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)},
 * as any changes will be overwritten by the host class.
 * <p>
 * Starting in {@link Build.VERSION_CODES#M API 23}, delegate
 * methods are called <i>after</i> host methods, which all properties to be
 * modified without being overwritten by the host class.
 */
public class AccessibilityDelegateCompat {

    static final class AccessibilityDelegateAdapter extends AccessibilityDelegate {
        final AccessibilityDelegateCompat mCompat;

        AccessibilityDelegateAdapter(AccessibilityDelegateCompat compat) {
            mCompat = compat;
        }

        @Override
        public boolean dispatchPopulateAccessibilityEvent(View host,
                AccessibilityEvent event) {
            return mCompat.dispatchPopulateAccessibilityEvent(host, event);
        }

        @Override
        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
            mCompat.onInitializeAccessibilityEvent(host, event);
        }

        @Override
        public void onInitializeAccessibilityNodeInfo(
                View host, AccessibilityNodeInfo info) {
            AccessibilityNodeInfoCompat nodeInfoCompat = AccessibilityNodeInfoCompat.wrap(info);
            nodeInfoCompat.setScreenReaderFocusable(ViewCompat.isScreenReaderFocusable(host));
            nodeInfoCompat.setHeading(ViewCompat.isAccessibilityHeading(host));
            nodeInfoCompat.setPaneTitle(ViewCompat.getAccessibilityPaneTitle(host));
            nodeInfoCompat.setStateDescription(ViewCompat.getStateDescription(host));
            mCompat.onInitializeAccessibilityNodeInfo(host, nodeInfoCompat);
            nodeInfoCompat.addSpansToExtras(info.getText(), host);
            List<AccessibilityActionCompat> actions = getActionList(host);
            for (int i = 0; i < actions.size(); i++) {
                nodeInfoCompat.addAction(actions.get(i));
            }
        }

        @Override
        public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
            mCompat.onPopulateAccessibilityEvent(host, event);
        }

        @Override
        public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
                AccessibilityEvent event) {
            return mCompat.onRequestSendAccessibilityEvent(host, child, event);
        }

        @Override
        public void sendAccessibilityEvent(View host, int eventType) {
            mCompat.sendAccessibilityEvent(host, eventType);
        }

        @Override
        public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
            mCompat.sendAccessibilityEventUnchecked(host, event);
        }

        @Override
        public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
            AccessibilityNodeProviderCompat provider =
                    mCompat.getAccessibilityNodeProvider(host);
            return (provider != null)
                    ? (AccessibilityNodeProvider) provider.getProvider() : null;
        }

        @Override
        public boolean performAccessibilityAction(View host, int action, Bundle args) {
            return mCompat.performAccessibilityAction(host, action, args);
        }
    }

    private static final AccessibilityDelegate DEFAULT_DELEGATE = new AccessibilityDelegate();
    private final AccessibilityDelegate mOriginalDelegate;

    private final AccessibilityDelegate mBridge;

    /**
     * Creates a new instance.
     */
    public AccessibilityDelegateCompat() {
        this(DEFAULT_DELEGATE);
    }

    /**
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public AccessibilityDelegateCompat(@NonNull AccessibilityDelegate originalDelegate) {
        mOriginalDelegate = originalDelegate;
        mBridge = new AccessibilityDelegateAdapter(this);
    }

    /**
     * @return The wrapped bridge implementation.
     */
    AccessibilityDelegate getBridge() {
        return mBridge;
    }

    /**
     * Sends an accessibility event of the given type. If accessibility is not
     * enabled this method has no effect.
     * <p>
     * The default implementation behaves as {@link View#sendAccessibilityEvent(int)
     * View#sendAccessibilityEvent(int)} for the case of no accessibility delegate
     * been set.
     * </p>
     *
     * @param host The View hosting the delegate.
     * @param eventType The type of the event to send.
     *
     * @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int)
     */
    public void sendAccessibilityEvent(@NonNull View host, int eventType) {
        mOriginalDelegate.sendAccessibilityEvent(host, eventType);
    }

    /**
     * Sends an accessibility event. This method behaves exactly as
     * {@link #sendAccessibilityEvent(View, int)} but takes as an argument an
     * empty {@link AccessibilityEvent} and does not perform a check whether
     * accessibility is enabled.
     * <p>
     * The default implementation behaves as
     * {@link View#sendAccessibilityEventUnchecked(AccessibilityEvent)
     * View#sendAccessibilityEventUnchecked(AccessibilityEvent)} for
     * the case of no accessibility delegate been set.
     * </p>
     *
     * @param host The View hosting the delegate.
     * @param event The event to send.
     *
     * @see View#sendAccessibilityEventUnchecked(AccessibilityEvent)
     *      View#sendAccessibilityEventUnchecked(AccessibilityEvent)
     */
    public void sendAccessibilityEventUnchecked(@NonNull View host,
            @NonNull AccessibilityEvent event) {
        mOriginalDelegate.sendAccessibilityEventUnchecked(host, event);
    }

    /**
     * Dispatches an {@link AccessibilityEvent} to the host {@link View} first and then
     * to its children for adding their text content to the event.
     * <p>
     * The default implementation behaves as
     * {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
     * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)} for
     * the case of no accessibility delegate been set.
     * </p>
     *
     * @param host The View hosting the delegate.
     * @param event The event.
     * @return True if the event population was completed.
     *
     * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
     *      View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
     */
    public boolean dispatchPopulateAccessibilityEvent(@NonNull View host,
            @NonNull AccessibilityEvent event) {
        return mOriginalDelegate.dispatchPopulateAccessibilityEvent(host, event);
    }

    /**
     * Gives a chance to the host View to populate the accessibility event with its
     * text content.
     * <p>
     * The default implementation behaves as
     * {@link ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)
     * ViewCompat#onPopulateAccessibilityEvent(AccessibilityEvent)} for
     * the case of no accessibility delegate been set.
     * </p>
     *
     * @param host The View hosting the delegate.
     * @param event The accessibility event which to populate.
     *
     * @see ViewCompat#onPopulateAccessibilityEvent(View ,AccessibilityEvent)
     *      ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)
     */
    public void onPopulateAccessibilityEvent(@NonNull View host,
            @NonNull AccessibilityEvent event) {
        mOriginalDelegate.onPopulateAccessibilityEvent(host, event);
    }

    /**
     * Initializes an {@link AccessibilityEvent} with information about the
     * the host View which is the event source.
     * <p>
     * The default implementation behaves as
     * {@link ViewCompat#onInitializeAccessibilityEvent(View v, AccessibilityEvent event)
     * ViewCompat#onInitalizeAccessibilityEvent(View v, AccessibilityEvent event)} for
     * the case of no accessibility delegate been set.
     * </p>
     *
     * @param host The View hosting the delegate.
     * @param event The event to initialize.
     *
     * @see ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)
     *      ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)
     */
    public void onInitializeAccessibilityEvent(@NonNull View host,
            @NonNull AccessibilityEvent event) {
        mOriginalDelegate.onInitializeAccessibilityEvent(host, event);
    }

    /**
     * Initializes an {@link AccessibilityNodeInfoCompat} with information about the host view.
     * <p>
     * The default implementation behaves as
     * {@link ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
     * ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)} for
     * the case of no accessibility delegate been set.
     * </p>
     *
     * @param host The View hosting the delegate.
     * @param info The instance to initialize.
     *
     * @see ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
     *      ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
     */
    public void onInitializeAccessibilityNodeInfo(@NonNull View host,
            @NonNull AccessibilityNodeInfoCompat info) {
        mOriginalDelegate.onInitializeAccessibilityNodeInfo(
                host, info.unwrap());
    }

    /**
     * Called when a child of the host View has requested sending an
     * {@link AccessibilityEvent} and gives an opportunity to the parent (the host)
     * to augment the event.
     * <p>
     * The default implementation behaves as
     * {@link ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
     * ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)} for
     * the case of no accessibility delegate been set.
     * </p>
     *
     * @param host The View hosting the delegate.
     * @param child The child which requests sending the event.
     * @param event The event to be sent.
     * @return True if the event should be sent
     *
     * @see ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
     *      ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
     */
    public boolean onRequestSendAccessibilityEvent(@NonNull ViewGroup host, @NonNull View child,
            @NonNull AccessibilityEvent event) {
        return mOriginalDelegate.onRequestSendAccessibilityEvent(host, child, event);
    }

    /**
     * Gets the provider for managing a virtual view hierarchy rooted at this View
     * and reported to {@link AccessibilityService}s
     * that explore the window content.
     * <p>
     * The default implementation behaves as
     * {@link ViewCompat#getAccessibilityNodeProvider(View) ViewCompat#getAccessibilityNodeProvider(View)}
     * for the case of no accessibility delegate been set.
     * </p>
     *
     * @return The provider.
     *
     * @see AccessibilityNodeProviderCompat
     */
    @Nullable
    public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(@NonNull View host) {
        Object provider = mOriginalDelegate.getAccessibilityNodeProvider(host);
        if (provider != null) {
            return new AccessibilityNodeProviderCompat(provider);
        }
        return null;
    }

    /**
     * Performs the specified accessibility action on the view. For
     * possible accessibility actions look at {@link AccessibilityNodeInfoCompat}.
     * <p>
     * The default implementation behaves as
     * {@link View#performAccessibilityAction(int, Bundle)
     *  View#performAccessibilityAction(int, Bundle)} for the case of
     *  no accessibility delegate been set.
     * </p>
     *
     *
     * @param host View on which to perform the action.
     * @param action The action to perform.
     * @param args Optional action arguments.
     * @return Whether the action was performed.
     *
     * @see View#performAccessibilityAction(int, Bundle)
     *      View#performAccessibilityAction(int, Bundle)
     */
    public boolean performAccessibilityAction(@NonNull View host, int action,
            @Nullable Bundle args) {
        boolean success = false;
        List<AccessibilityActionCompat> actions = getActionList(host);
        for (int i = 0; i < actions.size(); i++) {
            AccessibilityActionCompat actionCompat = actions.get(i);
            if (actionCompat.getId() == action) {
                success = actionCompat.perform(host, args);
                break;
            }
        }
        if (!success) {
            success = mOriginalDelegate.performAccessibilityAction(host, action, args);
        }
        if (!success && action == R.id.accessibility_action_clickable_span && args != null) {
            success = performClickableSpanAction(
                    args.getInt(AccessibilityClickableSpanCompat.SPAN_ID, -1), host);
        }
        return success;
    }

    @SuppressWarnings("unchecked")
    private boolean performClickableSpanAction(int clickableSpanId, View host) {
        SparseArray<WeakReference<ClickableSpan>> spans =
                (SparseArray<WeakReference<ClickableSpan>>)
                        host.getTag(R.id.tag_accessibility_clickable_spans);
        if (spans != null) {
            WeakReference<ClickableSpan> reference = spans.get(clickableSpanId);
            if (reference != null) {
                ClickableSpan span = reference.get();
                if (isSpanStillValid(span, host)) {
                    span.onClick(host);
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isSpanStillValid(ClickableSpan span, View view) {
        if (span != null) {
            AccessibilityNodeInfo info = view.createAccessibilityNodeInfo();
            ClickableSpan[] spans = AccessibilityNodeInfoCompat.getClickableSpans(info.getText());
            for (int i = 0; spans != null && i < spans.length; i++) {
                if (span.equals(spans[i])) {
                    return true;
                }
            }
        }
        return false;
    }

    @SuppressWarnings("unchecked")
    static List<AccessibilityActionCompat> getActionList(View view) {
        List<AccessibilityActionCompat> actions = (List<AccessibilityActionCompat>)
                view.getTag(R.id.tag_accessibility_actions);
        return actions == null ? Collections.emptyList() : actions;
    }
}