Merge "Fixing wrong accessibility focus when opening a floating view" into ub-launcher3-edmonton

This commit is contained in:
TreeHugger Robot 2018-05-16 20:25:07 +00:00 committed by Android (Google) Code Review
commit 0d1db51ce8
12 changed files with 109 additions and 74 deletions

View File

@ -244,6 +244,11 @@
<!-- Title for a bottom sheet that shows widgets for a particular app --> <!-- Title for a bottom sheet that shows widgets for a particular app -->
<string name="widgets_bottom_sheet_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> widgets</string> <string name="widgets_bottom_sheet_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> widgets</string>
<!-- Accessibility title for the popup containing a list of widgets. [CHAR_LIMIT=50] -->
<string name="widgets_list">Widgets list</string>
<!-- Text announced by accessibility when the popup containing the list of widgets is closed. [CHAR_LIMIT=100] -->
<string name="widgets_list_closed">Widgets list closed</string>
<!-- Strings for accessibility actions --> <!-- Strings for accessibility actions -->
<!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] --> <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
<string name="action_add_to_workspace">Add to Home screen</string> <string name="action_add_to_workspace">Add to Home screen</string>

View File

@ -16,10 +16,18 @@
package com.android.launcher3; package com.android.launcher3;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Pair;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.widget.LinearLayout; import android.widget.LinearLayout;
@ -123,6 +131,25 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
return false; return false;
} }
protected void announceAccessibilityChanges() {
Pair<View, String> targetInfo = getAccessibilityTarget();
if (targetInfo == null || !isAccessibilityEnabled(getContext())) {
return;
}
sendCustomAccessibilityEvent(
targetInfo.first, TYPE_WINDOW_STATE_CHANGED, targetInfo.second);
if (mIsOpen) {
sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
}
BaseDraggingActivity.fromContext(getContext()).getDragLayer()
.sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
}
protected Pair<View, String> getAccessibilityTarget() {
return null;
}
protected static <T extends AbstractFloatingView> T getOpenView( protected static <T extends AbstractFloatingView> T getOpenView(
BaseDraggingActivity activity, @FloatingViewType int type) { BaseDraggingActivity activity, @FloatingViewType int type) {
BaseDragLayer dragLayer = activity.getDragLayer(); BaseDragLayer dragLayer = activity.getDragLayer();

View File

@ -28,8 +28,6 @@ import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
@ -126,18 +124,6 @@ public class DragLayer extends BaseDragLayer<Launcher> {
return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event); return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
} }
public boolean isEventOverHotseat(MotionEvent ev) {
return isEventOverView(mActivity.getHotseat(), ev);
}
private boolean isEventOverFolder(Folder folder, MotionEvent ev) {
return isEventOverView(folder, ev);
}
private boolean isEventOverDropTargetBar(MotionEvent ev) {
return isEventOverView(mActivity.getDropTargetBar(), ev);
}
@Override @Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) { protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
ViewScrim scrim = ViewScrim.get(child); ViewScrim scrim = ViewScrim.get(child);
@ -157,24 +143,29 @@ public class DragLayer extends BaseDragLayer<Launcher> {
return super.findActiveController(ev); return super.findActiveController(ev);
} }
private boolean isEventOverAccessibleDropTargetBar(MotionEvent ev) {
return isInAccessibleDrag() && isEventOverView(mActivity.getDropTargetBar(), ev);
}
@Override @Override
public boolean onInterceptHoverEvent(MotionEvent ev) { public boolean onInterceptHoverEvent(MotionEvent ev) {
if (mActivity == null || mActivity.getWorkspace() == null) { if (mActivity == null || mActivity.getWorkspace() == null) {
return false; return false;
} }
Folder currentFolder = Folder.getOpen(mActivity); AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
if (currentFolder == null) { if (!(topView instanceof Folder)) {
return false; return false;
} else { } else {
AccessibilityManager accessibilityManager = (AccessibilityManager) AccessibilityManager accessibilityManager = (AccessibilityManager)
getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
if (accessibilityManager.isTouchExplorationEnabled()) { if (accessibilityManager.isTouchExplorationEnabled()) {
Folder currentFolder = (Folder) topView;
final int action = ev.getAction(); final int action = ev.getAction();
boolean isOverFolderOrSearchBar; boolean isOverFolderOrSearchBar;
switch (action) { switch (action) {
case MotionEvent.ACTION_HOVER_ENTER: case MotionEvent.ACTION_HOVER_ENTER:
isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) || isOverFolderOrSearchBar = isEventOverView(topView, ev) ||
(isInAccessibleDrag() && isEventOverDropTargetBar(ev)); isEventOverAccessibleDropTargetBar(ev);
if (!isOverFolderOrSearchBar) { if (!isOverFolderOrSearchBar) {
sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
mHoverPointClosesFolder = true; mHoverPointClosesFolder = true;
@ -183,8 +174,8 @@ public class DragLayer extends BaseDragLayer<Launcher> {
mHoverPointClosesFolder = false; mHoverPointClosesFolder = false;
break; break;
case MotionEvent.ACTION_HOVER_MOVE: case MotionEvent.ACTION_HOVER_MOVE:
isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) || isOverFolderOrSearchBar = isEventOverView(topView, ev) ||
(isInAccessibleDrag() && isEventOverDropTargetBar(ev)); isEventOverAccessibleDropTargetBar(ev);
if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) { if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) {
sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
mHoverPointClosesFolder = true; mHoverPointClosesFolder = true;
@ -219,18 +210,8 @@ public class DragLayer extends BaseDragLayer<Launcher> {
@Override @Override
public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
// Shortcuts can appear above folder if (isInAccessibleDrag() && child instanceof DropTargetBar) {
View topView = AbstractFloatingView.getTopOpenView(mActivity); return true;
if (topView != null) {
if (child == topView) {
return super.onRequestSendAccessibilityEvent(child, event);
}
if (isInAccessibleDrag() && child instanceof DropTargetBar) {
return super.onRequestSendAccessibilityEvent(child, event);
}
// Skip propagating onRequestSendAccessibilityEvent for all other children
// which are not topView
return false;
} }
return super.onRequestSendAccessibilityEvent(child, event); return super.onRequestSendAccessibilityEvent(child, event);
} }
@ -239,11 +220,9 @@ public class DragLayer extends BaseDragLayer<Launcher> {
public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) { public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
View topView = AbstractFloatingView.getTopOpenView(mActivity); View topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null) { if (topView != null) {
// Only add the top view as a child for accessibility when it is open addAccessibleChildToList(topView, childrenForAccessibility);
childrenForAccessibility.add(topView);
if (isInAccessibleDrag()) { if (isInAccessibleDrag()) {
childrenForAccessibility.add(mActivity.getDropTargetBar()); addAccessibleChildToList(mActivity.getDropTargetBar(), childrenForAccessibility);
} }
} else { } else {
super.addChildrenForAccessibility(childrenForAccessibility); super.addChildrenForAccessibility(childrenForAccessibility);

View File

@ -31,6 +31,7 @@ import android.text.InputType;
import android.text.Selection; import android.text.Selection;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import android.view.ActionMode; import android.view.ActionMode;
import android.view.FocusFinder; import android.view.FocusFinder;
import android.view.KeyEvent; import android.view.KeyEvent;
@ -516,15 +517,11 @@ public class Folder extends AbstractFloatingView implements DragSource,
public void onAnimationStart(Animator animation) { public void onAnimationStart(Animator animation) {
mFolderIcon.setBackgroundVisible(false); mFolderIcon.setBackgroundVisible(false);
mFolderIcon.drawLeaveBehindIfExists(); mFolderIcon.drawLeaveBehindIfExists();
sendCustomAccessibilityEvent(
Folder.this,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
mContent.getAccessibilityDescription());
} }
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
mState = STATE_OPEN; mState = STATE_OPEN;
announceAccessibilityChanges();
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened"); mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened");
mContent.setFocusOnFirstChild(); mContent.setFocusOnFirstChild();
@ -574,11 +571,6 @@ public class Folder extends AbstractFloatingView implements DragSource,
} }
mContent.verifyVisibleHighResIcons(mContent.getNextPage()); mContent.verifyVisibleHighResIcons(mContent.getNextPage());
// Notify the accessibility manager that this folder "window" has appeared and occluded
// the workspace items
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
dragLayer.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
} }
public void beginExternalDrag() { public void beginExternalDrag() {
@ -612,6 +604,7 @@ public class Folder extends AbstractFloatingView implements DragSource,
animateClosed(); animateClosed();
} else { } else {
closeComplete(false); closeComplete(false);
post(this::announceAccessibilityChanges);
} }
// Notify the accessibility manager that this folder "window" has disappeared and no // Notify the accessibility manager that this folder "window" has disappeared and no
@ -626,18 +619,18 @@ public class Folder extends AbstractFloatingView implements DragSource,
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
closeComplete(true); closeComplete(true);
} announceAccessibilityChanges();
@Override
public void onAnimationStart(Animator animation) {
sendCustomAccessibilityEvent(
Folder.this,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
getContext().getString(R.string.folder_closed));
} }
}); });
startAnimation(a); startAnimation(a);
} }
@Override
protected Pair<View, String> getAccessibilityTarget() {
return Pair.create(mContent, mIsOpen ? mContent.getAccessibilityDescription()
: getContext().getString(R.string.folder_closed));
}
private void closeComplete(boolean wasAnimated) { private void closeComplete(boolean wasAnimated) {
// TODO: Clear all active animations. // TODO: Clear all active animations.
DragLayer parent = (DragLayer) getParent(); DragLayer parent = (DragLayer) getParent();

View File

@ -367,6 +367,7 @@ public abstract class ArrowPopup extends AbstractFloatingView {
openAnim.addListener(new AnimatorListenerAdapter() { openAnim.addListener(new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
announceAccessibilityChanges();
mOpenCloseAnimator = null; mOpenCloseAnimator = null;
} }
}); });

View File

@ -34,6 +34,7 @@ import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Pair;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
@ -263,9 +264,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
ItemInfo originalItemInfo = (ItemInfo) originalIcon.getTag(); ItemInfo originalItemInfo = (ItemInfo) originalIcon.getTag();
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
setAccessibilityPaneTitle(getContext().getString(mNumNotifications == 0 ? setAccessibilityPaneTitle(getTitleForAccessibility());
R.string.action_deep_shortcut :
R.string.shortcuts_menu_with_notifications_description));
} }
mLauncher.getDragController().addDragListener(this); mLauncher.getDragController().addDragListener(this);
@ -281,6 +280,17 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
this, shortcutIds, mShortcuts, notificationKeys)); this, shortcutIds, mShortcuts, notificationKeys));
} }
private String getTitleForAccessibility() {
return getContext().getString(mNumNotifications == 0 ?
R.string.action_deep_shortcut :
R.string.shortcuts_menu_with_notifications_description);
}
@Override
protected Pair<View, String> getAccessibilityTarget() {
return Pair.create(this, "");
}
@Override @Override
protected void getTargetObjectLocation(Rect outPos) { protected void getTargetObjectLocation(Rect outPos) {
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mOriginalIcon, outPos); mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mOriginalIcon, outPos);

View File

@ -81,6 +81,7 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
mSwipeDetector.finishedScrolling(); mSwipeDetector.finishedScrolling();
announceAccessibilityChanges();
} }
}); });
} }

View File

@ -117,12 +117,20 @@ public abstract class BaseDragLayer<T extends BaseDraggingActivity> extends Inse
View topView = AbstractFloatingView.getTopOpenView(mActivity); View topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null) { if (topView != null) {
// Only add the top view as a child for accessibility when it is open // Only add the top view as a child for accessibility when it is open
childrenForAccessibility.add(topView); addAccessibleChildToList(topView, childrenForAccessibility);
} else { } else {
super.addChildrenForAccessibility(childrenForAccessibility); super.addChildrenForAccessibility(childrenForAccessibility);
} }
} }
protected void addAccessibleChildToList(View child, ArrayList<View> outList) {
if (child.isImportantForAccessibility()) {
outList.add(child);
} else {
child.addChildrenForAccessibility(outList);
}
}
@Override @Override
public void onViewRemoved(View child) { public void onViewRemoved(View child) {
super.onViewRemoved(child); super.onViewRemoved(child);

View File

@ -20,6 +20,7 @@ import android.animation.PropertyValuesHolder;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Pair;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -72,7 +73,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
mLauncher.getDragLayer().addView(this); mLauncher.getDragLayer().addView(this);
mIsOpen = false; mIsOpen = false;
open(true); animateOpen();
} }
@Override @Override
@ -129,20 +130,16 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
return widget; return widget;
} }
private void open(boolean animate) { private void animateOpen() {
if (mIsOpen || mOpenCloseAnimator.isRunning()) { if (mIsOpen || mOpenCloseAnimator.isRunning()) {
return; return;
} }
mIsOpen = true; mIsOpen = true;
setupNavBarColor(); setupNavBarColor();
if (animate) { mOpenCloseAnimator.setValues(
mOpenCloseAnimator.setValues( PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mOpenCloseAnimator.start();
mOpenCloseAnimator.start();
} else {
setTranslationShift(TRANSLATION_SHIFT_OPENED);
}
} }
@Override @Override
@ -170,4 +167,10 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
protected int getElementsRowCount() { protected int getElementsRowCount() {
return 1; return 1;
} }
@Override
protected Pair<View, String> getAccessibilityTarget() {
return Pair.create(findViewById(R.id.title), getContext().getString(
mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
}
} }

View File

@ -21,8 +21,10 @@ import android.animation.PropertyValuesHolder;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Pair;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
import com.android.launcher3.Insettable; import com.android.launcher3.Insettable;
@ -55,6 +57,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mAdapter = new WidgetsListAdapter(context, mAdapter = new WidgetsListAdapter(context,
LayoutInflater.from(context), apps.getWidgetCache(), apps.getIconCache(), LayoutInflater.from(context), apps.getWidgetCache(), apps.getIconCache(),
this, this); this, this);
} }
public WidgetsFullSheet(Context context, AttributeSet attrs) { public WidgetsFullSheet(Context context, AttributeSet attrs) {
@ -76,6 +79,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
onWidgetsBound(); onWidgetsBound();
} }
@Override
protected Pair<View, String> getAccessibilityTarget() {
return Pair.create(mRecyclerView, getContext().getString(
mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
}
@Override @Override
protected void onAttachedToWindow() { protected void onAttachedToWindow() {
super.onAttachedToWindow(); super.onAttachedToWindow();
@ -149,10 +158,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet
} }
private void open(boolean animate) { private void open(boolean animate) {
if (mIsOpen) {
return;
}
mIsOpen = true;
if (animate) { if (animate) {
if (mLauncher.getDragLayer().getInsets().bottom > 0) { if (mLauncher.getDragLayer().getInsets().bottom > 0) {
mContent.setAlpha(0); mContent.setAlpha(0);
@ -180,6 +185,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
} else { } else {
setTranslationShift(TRANSLATION_SHIFT_OPENED); setTranslationShift(TRANSLATION_SHIFT_OPENED);
mAdapter.setApplyBitmapDeferred(false, mRecyclerView); mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
post(this::announceAccessibilityChanges);
} }
} }
@ -212,6 +218,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
public static WidgetsFullSheet show(Launcher launcher, boolean animate) { public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater() WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
.inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false); .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
sheet.mIsOpen = true;
launcher.getDragLayer().addView(sheet); launcher.getDragLayer().addView(sheet);
sheet.open(animate); sheet.open(animate);
return sheet; return sheet;

View File

@ -29,7 +29,8 @@ public class WidgetsRowViewHolder extends ViewHolder {
public WidgetsRowViewHolder(ViewGroup v) { public WidgetsRowViewHolder(ViewGroup v) {
super(v); super(v);
cellContainer = (ViewGroup) v.findViewById(R.id.widgets_cell_list); cellContainer = v.findViewById(R.id.widgets_cell_list);
title = (BubbleTextView) v.findViewById(R.id.section); title = v.findViewById(R.id.section);
title.setAccessibilityDelegate(null);
} }
} }

View File

@ -58,7 +58,7 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController {
@Override @Override
protected int getLogContainerTypeForNormalState() { protected int getLogContainerTypeForNormalState() {
return mLauncher.getDragLayer().isEventOverHotseat(mTouchDownEvent) ? return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent) ?
ContainerType.HOTSEAT : ContainerType.WORKSPACE; ContainerType.HOTSEAT : ContainerType.WORKSPACE;
} }