From b6ab098bad4b126eaaaa3aaa5a512fefc4e0749b Mon Sep 17 00:00:00 2001
From: Adam Powell Implementations of {@link #performAccessibilityAction(int, Bundle)} may internally
+ * call this method to delegate an accessibility action to a supporting parent. If the parent
+ * returns true from its
+ * {@link ViewParent#onNestedPrePerformAccessibilityAction(View, int, android.os.Bundle)}
+ * method this method will return true to signify that the action was consumed. This method is useful for implementing nested scrolling child views. If
+ * {@link #isNestedScrollingEnabled()} returns true and the action is a scrolling action
+ * a custom view implementation may invoke this method to allow a parent to consume the
+ * scroll first. If this method returns true the custom view should skip its own scrolling
+ * behavior.
The default implementation will delegate + * {@link AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD} and + * {@link AccessibilityNodeInfo#ACTION_SCROLL_FORWARD} to nested scrolling parents if + * {@link #isNestedScrollingEnabled() nested scrolling is enabled} on this view.
+ * * @param action The action to perform. * @param arguments Optional action arguments. * @return Whether the action was performed. @@ -8188,6 +8221,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide Until we've refactored all accessibility delegation methods. */ public boolean performAccessibilityActionInternal(int action, Bundle arguments) { + if (isNestedScrollingEnabled() + && (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD + || action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)) { + if (dispatchNestedPrePerformAccessibilityAction(action, arguments)) { + return true; + } + } + switch (action) { case AccessibilityNodeInfo.ACTION_CLICK: { if (isClickable()) { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 22c51859ea31d..6678ff223b14d 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -32,6 +32,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.os.Build; +import android.os.Bundle; import android.os.Parcelable; import android.os.SystemClock; import android.util.AttributeSet; @@ -2921,6 +2922,22 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + /** + * {@inheritDoc} + * + *Subclasses should always call super.onNestedPrePerformAccessibilityAction
This method may be called by a target descendant view if the target wishes to give + * a view in its parent chain a chance to react to the event before normal processing occurs. + * Most commonly this will be a scroll event such as + * {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_SCROLL_FORWARD}. + * A ViewParent that supports acting as a nested scrolling parent should override this + * method and act accordingly to implement scrolling via accesibility systems.
+ * + * @param target The target view dispatching this action + * @param action Action being performed; see + * {@link android.view.accessibility.AccessibilityNodeInfo} + * @param arguments Optional action arguments + * @return true if the action was consumed by this ViewParent + */ + public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle arguments); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 34c27d78e8562..25dd9c7d6518e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6413,6 +6413,11 @@ public final class ViewRootImpl implements ViewParent, return false; } + @Override + public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) { + return false; + } + void changeCanvasOpacity(boolean opaque) { Log.d(TAG, "changeCanvasOpacity: opaque=" + opaque); if (mAttachInfo.mHardwareRenderer != null) { diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index ed7af2f3525a1..4e48454250e01 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -20,6 +20,7 @@ package com.android.internal.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; @@ -31,6 +32,8 @@ import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewParent; import android.view.ViewTreeObserver; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AnimationUtils; import android.widget.AbsListView; import android.widget.OverScroller; @@ -367,8 +370,14 @@ public class ResolverDrawerLayout extends ViewGroup { child.offsetTopAndBottom((int) dy); } } + final boolean isCollapsedOld = mCollapseOffset != 0; mCollapseOffset = newPos; mTopOffset += dy; + final boolean isCollapsedNew = newPos != 0; + if (isCollapsedOld != isCollapsedNew) { + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } postInvalidateOnAnimation(); return dy; } @@ -570,6 +579,50 @@ public class ResolverDrawerLayout extends ViewGroup { return false; } + @Override + public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) { + if (super.onNestedPrePerformAccessibilityAction(target, action, args)) { + return true; + } + + if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD && mCollapseOffset != 0) { + smoothScrollTo(0, 0); + return true; + } + return false; + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + event.setClassName(ResolverDrawerLayout.class.getName()); + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName(ResolverDrawerLayout.class.getName()); + if (isEnabled()) { + if (mCollapseOffset != 0) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); + info.setScrollable(true); + } + } + } + + @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + if (super.performAccessibilityAction(action, arguments)) { + return true; + } + + if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD && mCollapseOffset != 0) { + smoothScrollTo(0, 0); + return true; + } + return false; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int sourceWidth = MeasureSpec.getSize(widthMeasureSpec); @@ -615,7 +668,13 @@ public class ResolverDrawerLayout extends ViewGroup { mUncollapsibleHeight = heightUsed - mCollapsibleHeight; if (isLaidOut()) { + final boolean isCollapsedOld = mCollapseOffset != 0; mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); + final boolean isCollapsedNew = mCollapseOffset != 0; + if (isCollapsedOld != isCollapsedNew) { + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } } else { // Start out collapsed at first unless we restored state for otherwise mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight;