Fixing wrong accessibility focus when opening a floating view

> Using common logic for announcing a floating view for widgets and folders

Bug: 79091095
Bug: 79748886
Change-Id: Ibb3fe48e68e724f50d69f51a03d3b35ad0baf625
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 097c341..5a1c158 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -16,10 +16,18 @@
 
 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.content.Context;
 import android.support.annotation.IntDef;
 import android.util.AttributeSet;
+import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -123,6 +131,25 @@
         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(
             BaseDraggingActivity activity, @FloatingViewType int type) {
         BaseDragLayer dragLayer = activity.getDragLayer();
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 7fef904..53e9e2d 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -28,8 +28,6 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -126,18 +124,6 @@
         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
     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
         ViewScrim scrim = ViewScrim.get(child);
@@ -157,24 +143,29 @@
         return super.findActiveController(ev);
     }
 
+    private boolean isEventOverAccessibleDropTargetBar(MotionEvent ev) {
+        return isInAccessibleDrag() && isEventOverView(mActivity.getDropTargetBar(), ev);
+    }
+
     @Override
     public boolean onInterceptHoverEvent(MotionEvent ev) {
         if (mActivity == null || mActivity.getWorkspace() == null) {
             return false;
         }
-        Folder currentFolder = Folder.getOpen(mActivity);
-        if (currentFolder == null) {
+        AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+        if (!(topView instanceof Folder)) {
             return false;
         } else {
             AccessibilityManager accessibilityManager = (AccessibilityManager)
                     getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
             if (accessibilityManager.isTouchExplorationEnabled()) {
+                Folder currentFolder = (Folder) topView;
                 final int action = ev.getAction();
                 boolean isOverFolderOrSearchBar;
                 switch (action) {
                     case MotionEvent.ACTION_HOVER_ENTER:
-                        isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
-                                (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
+                        isOverFolderOrSearchBar = isEventOverView(topView, ev) ||
+                                isEventOverAccessibleDropTargetBar(ev);
                         if (!isOverFolderOrSearchBar) {
                             sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
                             mHoverPointClosesFolder = true;
@@ -183,8 +174,8 @@
                         mHoverPointClosesFolder = false;
                         break;
                     case MotionEvent.ACTION_HOVER_MOVE:
-                        isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
-                                (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
+                        isOverFolderOrSearchBar = isEventOverView(topView, ev) ||
+                                isEventOverAccessibleDropTargetBar(ev);
                         if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) {
                             sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
                             mHoverPointClosesFolder = true;
@@ -219,18 +210,8 @@
 
     @Override
     public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
-        // Shortcuts can appear above folder
-        View topView = AbstractFloatingView.getTopOpenView(mActivity);
-        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;
+        if (isInAccessibleDrag() && child instanceof DropTargetBar) {
+            return true;
         }
         return super.onRequestSendAccessibilityEvent(child, event);
     }
@@ -239,11 +220,9 @@
     public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
         View topView = AbstractFloatingView.getTopOpenView(mActivity);
         if (topView != null) {
-            // Only add the top view as a child for accessibility when it is open
-            childrenForAccessibility.add(topView);
-
+            addAccessibleChildToList(topView, childrenForAccessibility);
             if (isInAccessibleDrag()) {
-                childrenForAccessibility.add(mActivity.getDropTargetBar());
+                addAccessibleChildToList(mActivity.getDropTargetBar(), childrenForAccessibility);
             }
         } else {
             super.addChildrenForAccessibility(childrenForAccessibility);
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 99c800d..b49952f 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -31,6 +31,7 @@
 import android.text.Selection;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Pair;
 import android.view.ActionMode;
 import android.view.FocusFinder;
 import android.view.KeyEvent;
@@ -516,15 +517,11 @@
             public void onAnimationStart(Animator animation) {
                 mFolderIcon.setBackgroundVisible(false);
                 mFolderIcon.drawLeaveBehindIfExists();
-
-                sendCustomAccessibilityEvent(
-                        Folder.this,
-                        AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
-                        mContent.getAccessibilityDescription());
             }
             @Override
             public void onAnimationEnd(Animator animation) {
                 mState = STATE_OPEN;
+                announceAccessibilityChanges();
 
                 mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened");
                 mContent.setFocusOnFirstChild();
@@ -574,11 +571,6 @@
         }
 
         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() {
@@ -612,6 +604,7 @@
             animateClosed();
         } else {
             closeComplete(false);
+            post(this::announceAccessibilityChanges);
         }
 
         // Notify the accessibility manager that this folder "window" has disappeared and no
@@ -626,18 +619,18 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 closeComplete(true);
-            }
-            @Override
-            public void onAnimationStart(Animator animation) {
-                sendCustomAccessibilityEvent(
-                        Folder.this,
-                        AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
-                        getContext().getString(R.string.folder_closed));
+                announceAccessibilityChanges();
             }
         });
         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) {
         // TODO: Clear all active animations.
         DragLayer parent = (DragLayer) getParent();
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 5589f17..9098777 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -367,6 +367,7 @@
         openAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
+                announceAccessibilityChanges();
                 mOpenCloseAnimator = null;
             }
         });
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 763eb6f..f276fbf 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -34,6 +34,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.util.AttributeSet;
+import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -263,9 +264,7 @@
 
         ItemInfo originalItemInfo = (ItemInfo) originalIcon.getTag();
         if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-            setAccessibilityPaneTitle(getContext().getString(mNumNotifications == 0 ?
-                    R.string.action_deep_shortcut :
-                    R.string.shortcuts_menu_with_notifications_description));
+            setAccessibilityPaneTitle(getTitleForAccessibility());
         }
 
         mLauncher.getDragController().addDragListener(this);
@@ -281,6 +280,17 @@
                 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
     protected void getTargetObjectLocation(Rect outPos) {
         mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mOriginalIcon, outPos);
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 7c4529d..c8d1457 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -81,6 +81,7 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mSwipeDetector.finishedScrolling();
+                announceAccessibilityChanges();
             }
         });
     }
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 66d9498..2f142ac 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -117,12 +117,20 @@
         View topView = AbstractFloatingView.getTopOpenView(mActivity);
         if (topView != null) {
             // Only add the top view as a child for accessibility when it is open
-            childrenForAccessibility.add(topView);
+            addAccessibleChildToList(topView, childrenForAccessibility);
         } else {
             super.addChildrenForAccessibility(childrenForAccessibility);
         }
     }
 
+    protected void addAccessibleChildToList(View child, ArrayList<View> outList) {
+        if (child.isImportantForAccessibility()) {
+            outList.add(child);
+        } else {
+            child.addChildrenForAccessibility(outList);
+        }
+    }
+
     @Override
     public void onViewRemoved(View child) {
         super.onViewRemoved(child);
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index a258485..5ce7e04 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Pair;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -72,7 +73,7 @@
 
         mLauncher.getDragLayer().addView(this);
         mIsOpen = false;
-        open(true);
+        animateOpen();
     }
 
     @Override
@@ -129,20 +130,16 @@
         return widget;
     }
 
-    private void open(boolean animate) {
+    private void animateOpen() {
         if (mIsOpen || mOpenCloseAnimator.isRunning()) {
             return;
         }
         mIsOpen = true;
         setupNavBarColor();
-        if (animate) {
-            mOpenCloseAnimator.setValues(
-                    PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
-            mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-            mOpenCloseAnimator.start();
-        } else {
-            setTranslationShift(TRANSLATION_SHIFT_OPENED);
-        }
+        mOpenCloseAnimator.setValues(
+                PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+        mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mOpenCloseAnimator.start();
     }
 
     @Override
@@ -170,4 +167,10 @@
     protected int getElementsRowCount() {
         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));
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
index a622624..e94d81d 100644
--- a/src/com/android/launcher3/widget/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java
@@ -21,8 +21,10 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.View;
 import android.view.animation.AnimationUtils;
 
 import com.android.launcher3.Insettable;
@@ -55,6 +57,7 @@
         mAdapter = new WidgetsListAdapter(context,
                 LayoutInflater.from(context), apps.getWidgetCache(), apps.getIconCache(),
                 this, this);
+
     }
 
     public WidgetsFullSheet(Context context, AttributeSet attrs) {
@@ -77,6 +80,12 @@
     }
 
     @Override
+    protected Pair<View, String> getAccessibilityTarget() {
+        return Pair.create(mRecyclerView, getContext().getString(
+                mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
+    }
+
+    @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mLauncher.getAppWidgetHost().addProviderChangeListener(this);
@@ -149,10 +158,6 @@
     }
 
     private void open(boolean animate) {
-        if (mIsOpen) {
-            return;
-        }
-        mIsOpen = true;
         if (animate) {
             if (mLauncher.getDragLayer().getInsets().bottom > 0) {
                 mContent.setAlpha(0);
@@ -180,6 +185,7 @@
         } else {
             setTranslationShift(TRANSLATION_SHIFT_OPENED);
             mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
+            post(this::announceAccessibilityChanges);
         }
     }
 
@@ -212,6 +218,7 @@
     public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
         WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
                 .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
+        sheet.mIsOpen = true;
         launcher.getDragLayer().addView(sheet);
         sheet.open(animate);
         return sheet;
diff --git a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
index bc85db6..8f269a6 100644
--- a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
+++ b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
@@ -29,7 +29,8 @@
     public WidgetsRowViewHolder(ViewGroup v) {
         super(v);
 
-        cellContainer = (ViewGroup) v.findViewById(R.id.widgets_cell_list);
-        title = (BubbleTextView) v.findViewById(R.id.section);
+        cellContainer = v.findViewById(R.id.widgets_cell_list);
+        title = v.findViewById(R.id.section);
+        title.setAccessibilityDelegate(null);
     }
 }