diff options
-rw-r--r-- | api/current.txt | 12 | ||||
-rw-r--r-- | core/java/android/view/View.java | 352 | ||||
-rw-r--r-- | core/java/android/view/ViewGroup.java | 28 | ||||
-rw-r--r-- | core/java/android/widget/AdapterView.java | 1 | ||||
-rw-r--r-- | core/java/android/widget/ImageView.java | 1 | ||||
-rw-r--r-- | core/java/android/widget/TabWidget.java | 1 |
6 files changed, 383 insertions, 12 deletions
diff --git a/api/current.txt b/api/current.txt index dfd8a33..80375be 100644 --- a/api/current.txt +++ b/api/current.txt @@ -22950,6 +22950,7 @@ package android.view { method public void scrollTo(int, int); method public void sendAccessibilityEvent(int); method public void sendAccessibilityEventUnchecked(android.view.accessibility.AccessibilityEvent); + method public void setAccessibilityDelegate(android.view.View.AccessibilityDelegate); method public void setActivated(boolean); method public void setAlpha(float); method public void setAnimation(android.view.animation.Animation); @@ -23126,6 +23127,17 @@ package android.view { field public static android.util.Property Y; } + public static class View.AccessibilityDelegate { + ctor public View.AccessibilityDelegate(); + method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); + method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); + method public void onInitializeAccessibilityNodeInfo(android.view.View, android.view.accessibility.AccessibilityNodeInfo); + method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); + method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent); + method public void sendAccessibilityEvent(android.view.View, int); + method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent); + } + public static class View.BaseSavedState extends android.view.AbsSavedState { ctor public View.BaseSavedState(android.os.Parcel); ctor public View.BaseSavedState(android.os.Parcelable); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index fd60813..1454110 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2545,6 +2545,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal private boolean mSendingHoverAccessibilityEvents; /** + * Delegate for injecting accessiblity functionality. + */ + AccessibilityDelegate mAccessibilityDelegate; + + /** * Text direction is inherited thru {@link ViewGroup} * @hide */ @@ -3761,14 +3766,34 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * and last calls * {@link ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)} * on its parent to resuest sending of the event to interested parties. + * <p> + * If an {@link AccessibilityDelegate} has been specified via calling + * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its + * {@link AccessibilityDelegate#sendAccessibilityEvent(View, int)} is + * responsible for handling this call. + * </p> * * @param eventType The type of the event to send. * * @see #onInitializeAccessibilityEvent(AccessibilityEvent) * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent) * @see ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent) + * @see AccessibilityDelegate */ public void sendAccessibilityEvent(int eventType) { + if (mAccessibilityDelegate != null) { + mAccessibilityDelegate.sendAccessibilityEvent(this, eventType); + } else { + sendAccessibilityEventInternal(eventType); + } + } + + /** + * @see #sendAccessibilityEvent(int) + * + * Note: Called from the default {@link AccessibilityDelegate}. + */ + void sendAccessibilityEventInternal(int eventType) { if (AccessibilityManager.getInstance(mContext).isEnabled()) { sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType)); } @@ -3777,13 +3802,32 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * This method behaves exactly as {@link #sendAccessibilityEvent(int)} but * takes as an argument an empty {@link AccessibilityEvent} and does not - * perfrom a check whether accessibility is enabled. + * perform a check whether accessibility is enabled. + * <p> + * If an {@link AccessibilityDelegate} has been specified via calling + * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its + * {@link AccessibilityDelegate#sendAccessibilityEventUnchecked(View, AccessibilityEvent)} + * is responsible for handling this call. + * </p> * * @param event The event to send. * * @see #sendAccessibilityEvent(int) */ public void sendAccessibilityEventUnchecked(AccessibilityEvent event) { + if (mAccessibilityDelegate != null) { + mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event); + } else { + sendAccessibilityEventUncheckedInternal(event); + } + } + + /** + * @see #sendAccessibilityEventUnchecked(AccessibilityEvent) + * + * Note: Called from the default {@link AccessibilityDelegate}. + */ + void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) { if (!isShown()) { return; } @@ -3798,18 +3842,36 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * to its children for adding their text content to the event. Note that the * event text is populated in a separate dispatch path since we add to the * event not only the text of the source but also the text of all its descendants. - * </p> * A typical implementation will call * {@link #onPopulateAccessibilityEvent(AccessibilityEvent)} on the this view * and then call the {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)} * on each child. Override this method if custom population of the event text * content is required. + * <p> + * If an {@link AccessibilityDelegate} has been specified via calling + * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its + * {@link AccessibilityDelegate#dispatchPopulateAccessibilityEvent(View, AccessibilityEvent)} + * is responsible for handling this call. + * </p> * * @param event The event. * * @return True if the event population was completed. */ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + if (mAccessibilityDelegate != null) { + return mAccessibilityDelegate.dispatchPopulateAccessibilityEvent(this, event); + } else { + return dispatchPopulateAccessibilityEventInternal(event); + } + } + + /** + * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent) + * + * Note: Called from the default {@link AccessibilityDelegate}. + */ + boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { onPopulateAccessibilityEvent(event); return false; } @@ -3832,6 +3894,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * event.getText().add(selectedDateUtterance); * } * </code></pre></p> + * <p> + * If an {@link AccessibilityDelegate} has been specified via calling + * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its + * {@link AccessibilityDelegate#onPopulateAccessibilityEvent(View, AccessibilityEvent)} + * is responsible for handling this call. + * </p> * * @param event The accessibility event which to populate. * @@ -3839,13 +3907,27 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent) */ public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + if (mAccessibilityDelegate != null) { + mAccessibilityDelegate.onPopulateAccessibilityEvent(this, event); + } else { + onPopulateAccessibilityEventInternal(event); + } } /** - * Initializes an {@link AccessibilityEvent} with information about the - * the type of the event and this View which is the event source. In other - * words, the source of an accessibility event is the view whose state - * change triggered firing the event. + * @see #onPopulateAccessibilityEvent(AccessibilityEvent) + * + * Note: Called from the default {@link AccessibilityDelegate}. + */ + void onPopulateAccessibilityEventInternal(AccessibilityEvent event) { + + } + + /** + * Initializes an {@link AccessibilityEvent} with information about + * this View which is the event source. In other words, the source of + * an accessibility event is the view whose state change triggered firing + * the event. * <p> * Example: Setting the password property of an event in addition * to properties set by the super implementation. @@ -3855,12 +3937,32 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * event.setPassword(true); * } * </code></pre></p> - * @param event The event to initialeze. + * <p> + * If an {@link AccessibilityDelegate} has been specified via calling + * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its + * {@link AccessibilityDelegate#onInitializeAccessibilityEvent(View, AccessibilityEvent)} + * is responsible for handling this call. + * </p> + * + * @param event The event to initialize. * * @see #sendAccessibilityEvent(int) * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent) */ public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + if (mAccessibilityDelegate != null) { + mAccessibilityDelegate.onInitializeAccessibilityEvent(this, event); + } else { + onInitializeAccessibilityEventInternal(event); + } + } + + /** + * @see #onInitializeAccessibilityEvent(AccessibilityEvent) + * + * Note: Called from the default {@link AccessibilityDelegate}. + */ + void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { event.setSource(this); event.setClassName(getClass().getName()); event.setPackageName(getContext().getPackageName()); @@ -3929,9 +4031,29 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Subclasses should override this method, call the super implementation, * and set additional attributes. * </p> + * <p> + * If an {@link AccessibilityDelegate} has been specified via calling + * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its + * {@link AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfo)} + * is responsible for handling this call. + * </p> + * * @param info The instance to initialize. */ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + if (mAccessibilityDelegate != null) { + mAccessibilityDelegate.onInitializeAccessibilityNodeInfo(this, info); + } else { + onInitializeAccessibilityNodeInfoInternal(info); + } + } + + /** + * @see #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) + * + * Note: Called from the default {@link AccessibilityDelegate}. + */ + void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { Rect bounds = mAttachInfo.mTmpInvalRect; getDrawingRect(bounds); info.setBoundsInParent(bounds); @@ -3975,6 +4097,19 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** + * Sets a delegate for implementing accessibility support via compositon as + * opposed to inheritance. The delegate's primary use is for implementing + * backwards compatible widgets. For more details see {@link AccessibilityDelegate}. + * + * @param delegate The delegate instance. + * + * @see AccessibilityDelegate + */ + public void setAccessibilityDelegate(AccessibilityDelegate delegate) { + mAccessibilityDelegate = delegate; + } + + /** * Gets the unique identifier of this view on the screen for accessibility purposes. * If this {@link View} is not attached to any window, {@value #NO_ID} is returned. * @@ -10098,7 +10233,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Setting a solid background color for the drawing cache's bitmaps will improve - * perfromance and memory usage. Note, though that this should only be used if this + * performance and memory usage. Note, though that this should only be used if this * view will always be drawn on top of a solid color. * * @param color The background color to use for the drawing cache's bitmap @@ -14313,4 +14448,205 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal mIsPending = false; } } + + /** + * <p> + * This class represents a delegate that can be registered in a {@link View} + * to enhance accessibility support via composition rather via inheritance. + * It is specifically targeted to widget developers that extend basic View + * classes i.e. classes in package android.view, that would like their + * applications to be backwards compatible. + * </p> + * <p> + * A scenario in which a developer would like to use an accessibility delegate + * is overriding a method introduced in a later API version then the minimal API + * version supported by the application. For example, the method + * {@link View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} is not available + * in API version 4 when the accessibility APIs were first introduced. If a + * developer would like his application to run on API version 4 devices (assuming + * all other APIs used by the application are version 4 or lower) and take advantage + * of this method, instead of overriding the method which would break the application's + * backwards compatibility, he can override the corresponding method in this + * delegate and register the delegate in the target View if the API version of + * the system is high enough i.e. the API version is same or higher to the API + * version that introduced + * {@link View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)}. + * </p> + * <p> + * Here is an example implementation: + * </p> + * <code><pre><p> + * if (Build.VERSION.SDK_INT >= 14) { + * // If the API version is equal of higher than the version in + * // which onInitializeAccessibilityNodeInfo was introduced we + * // register a delegate with a customized implementation. + * View view = findViewById(R.id.view_id); + * view.setAccessibilityDelegate(new AccessibilityDelegate() { + * public void onInitializeAccessibilityNodeInfo(View host, + * AccessibilityNodeInfo info) { + * // Let the default implementation populate the info. + * super.onInitializeAccessibilityNodeInfo(host, info); + * // Set some other information. + * info.setEnabled(host.isEnabled()); + * } + * }); + * } + * </code></pre></p> + * <p> + * This delegate contains methods that correspond to the accessibility methods + * in View. If a delegate has been specified the implementation in View hands + * off handling to the corresponding method in this delegate. The default + * implementation the delegate methods behaves exactly as the corresponding + * method in View for the case of no accessibility delegate been set. Hence, + * to customize the behavior of a View method, clients can override only the + * corresponding delegate method without altering the behavior of the rest + * accessibility related methods of the host view. + * </p> + */ + public static class AccessibilityDelegate { + + /** + * 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(View host, int eventType) { + host.sendAccessibilityEventInternal(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(View host, AccessibilityEvent event) { + host.sendAccessibilityEventUncheckedInternal(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(View host, AccessibilityEvent event) { + return host.dispatchPopulateAccessibilityEventInternal(event); + } + + /** + * Gives a chance to the host View to populate the accessibility event with its + * text content. + * <p> + * The default implementation behaves as + * {@link View#onPopulateAccessibilityEvent(AccessibilityEvent) + * View#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 View#onPopulateAccessibilityEvent(AccessibilityEvent) + * View#onPopulateAccessibilityEvent(AccessibilityEvent) + */ + public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { + host.onPopulateAccessibilityEventInternal(event); + } + + /** + * Initializes an {@link AccessibilityEvent} with information about the + * the host View which is the event source. + * <p> + * The default implementation behaves as + * {@link View#onInitializeAccessibilityEvent(AccessibilityEvent) + * View#onInitializeAccessibilityEvent(AccessibilityEvent)} for + * the case of no accessibility delegate been set. + * </p> + * + * @param host The View hosting the delegate. + * @param event The event to initialize. + * + * @see View#onInitializeAccessibilityEvent(AccessibilityEvent) + * View#onInitializeAccessibilityEvent(AccessibilityEvent) + */ + public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { + host.onInitializeAccessibilityEventInternal(event); + } + + /** + * Initializes an {@link AccessibilityNodeInfo} with information about the host view. + * <p> + * The default implementation behaves as + * {@link View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) + * View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} for + * the case of no accessibility delegate been set. + * </p> + * + * @param host The View hosting the delegate. + * @param info The instance to initialize. + * + * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) + * View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) + */ + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + host.onInitializeAccessibilityNodeInfoInternal(info); + } + + /** + * 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 ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent) + * ViewGroup#onRequestSendAccessibilityEvent(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 ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent) + * ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent) + */ + public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, + AccessibilityEvent event) { + return host.onRequestSendAccessibilityEventInternal(child, event); + } + } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 0e420d6..1bd0782 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -34,6 +34,7 @@ import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; +import android.view.View.AccessibilityDelegate; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Animation; @@ -606,6 +607,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Called when a child has requested sending an {@link AccessibilityEvent} and * gives an opportunity to its parent to augment the event. + * <p> + * If an {@link AccessibilityDelegate} has been specified via calling + * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its + * {@link AccessibilityDelegate#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)} + * is responsible for handling this call. + * </p> * * @param child The child which requests sending the event. * @param event The event to be sent. @@ -614,6 +621,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @see #requestSendAccessibilityEvent(View, AccessibilityEvent) */ public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { + if (mAccessibilityDelegate != null) { + return mAccessibilityDelegate.onRequestSendAccessibilityEvent(this, child, event); + } else { + return onRequestSendAccessibilityEventInternal(child, event); + } + } + + /** + * @see #onRequestSendAccessibilityEvent(View, AccessibilityEvent) + * + * Note: Called from the default {@link View.AccessibilityDelegate}. + */ + boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) { return true; } @@ -2142,9 +2162,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { // We first get a chance to populate the event. - onPopulateAccessibilityEvent(event); + super.dispatchPopulateAccessibilityEventInternal(event); // Let our children have a shot in populating the event. for (int i = 0, count = getChildCount(); i < count; i++) { View child = getChildAt(i); @@ -2159,8 +2179,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); + void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfoInternal(info); // If the view is not the topmost one in the view hierarchy and it is // marked as the logical root of a view hierarchy, do not go any deeper. if ((!(getParent() instanceof ViewRootImpl)) && (mPrivateFlags & IS_ROOT_NAMESPACE) != 0) { diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 2d10bbe..72db8e8 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -896,6 +896,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { @Override public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(event); // We send selection events only from AdapterView to avoid // generation of such event for each child. getSelectedView().dispatchPopulateAccessibilityEvent(event); diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index b92130d..a5d6c9a 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -212,6 +212,7 @@ public class ImageView extends View { @Override public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(event); CharSequence contentDescription = getContentDescription(); if (!TextUtils.isEmpty(contentDescription)) { event.getText().add(contentDescription); diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java index 191c4ca..80bfe99 100644 --- a/core/java/android/widget/TabWidget.java +++ b/core/java/android/widget/TabWidget.java @@ -489,6 +489,7 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { public void sendAccessibilityEventUnchecked(AccessibilityEvent event) { // this class fires events only when tabs are focused or selected if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && isFocused()) { + event.recycle(); return; } super.sendAccessibilityEventUnchecked(event); |