Merge changes from topic "bubblebar-a11y" into main

* changes:
  Move bubble bar strings
  Add content description to bubble bar and bubbles
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index cf987c3..340d25b 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -332,4 +332,14 @@
 
     <!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
     <string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
+
+    <!-- Strings for bubble bar -->
+    <!-- Fallback name for a bubble if it does have a title [CHAR_LIMIT=none] -->
+    <string name="bubble_bar_bubble_fallback_description">Bubble</string>
+    <!-- content description for the overflow bubble [CHAR_LIMIT=none] -->
+    <string name="bubble_bar_overflow_description">Overflow</string>
+    <!-- Content description for a bubble bar bubble. [CHAR_LIMIT=none] -->
+    <string name="bubble_bar_bubble_description"><xliff:g id="notification_title" example="some title">%1$s</xliff:g> from <xliff:g id="app_name" example="YouTube">%2$s</xliff:g></string>
+    <!-- Content description for bubble bar when it has multiple bubbles. [CHAR_LIMIT=NONE] -->
+    <string name="bubble_bar_description_multiple_bubbles"><xliff:g id="bubble_bar_bubble_description" example="some title from YouTube">%1$s</xliff:g> and <xliff:g id="bubble_count" example="4">%2$d</xliff:g> more</string>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 23e52e6..7eeea84 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -623,6 +623,8 @@
         }
         super.addView(child, index, params);
         updateWidth();
+        updateBubbleAccessibilityStates();
+        updateContentDescription();
     }
 
     // TODO: (b/283309949) animate it
@@ -634,6 +636,8 @@
             mBubbleBarBackground.showArrow(false);
         }
         updateWidth();
+        updateBubbleAccessibilityStates();
+        updateContentDescription();
     }
 
     private void updateWidth() {
@@ -799,6 +803,7 @@
                 }
             }
             updateChildrenRenderNodeProperties(mBubbleBarLocation);
+            updateContentDescription();
         }
     }
 
@@ -927,6 +932,7 @@
             } else {
                 mWidthAnimator.reverse();
             }
+            updateBubbleAccessibilityStates();
         }
     }
 
@@ -998,6 +1004,47 @@
         return mIsAnimatingNewBubble;
     }
 
+    private boolean hasOverview() {
+        // Overview is always the last bubble
+        View lastChild = getChildAt(getChildCount() - 1);
+        if (lastChild instanceof BubbleView bubbleView) {
+            return bubbleView.getBubble() instanceof BubbleBarOverflow;
+        }
+        return false;
+    }
+
+    private void updateBubbleAccessibilityStates() {
+        final int childA11y;
+        if (mIsBarExpanded) {
+            // Bar is expanded, focus on the bubbles
+            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+            childA11y = View.IMPORTANT_FOR_ACCESSIBILITY_YES;
+        } else {
+            // Bar is collapsed, only focus on the bar
+            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+            childA11y = View.IMPORTANT_FOR_ACCESSIBILITY_NO;
+        }
+        for (int i = 0; i < getChildCount(); i++) {
+            getChildAt(i).setImportantForAccessibility(childA11y);
+            // Only allowing focusing on bubbles when bar is expanded. Otherwise, in talkback mode,
+            // bubbles can be navigates to in collapsed mode.
+            getChildAt(i).setFocusable(mIsBarExpanded);
+        }
+    }
+
+    private void updateContentDescription() {
+        View firstChild = getChildAt(0);
+        CharSequence contentDesc = firstChild != null ? firstChild.getContentDescription() : "";
+
+        // Don't count overflow if it exists
+        int bubbleCount = getChildCount() - (hasOverview() ? 1 : 0);
+        if (bubbleCount > 1) {
+            contentDesc = getResources().getString(R.string.bubble_bar_description_multiple_bubbles,
+                    contentDesc, bubbleCount - 1);
+        }
+        setContentDescription(contentDesc);
+    }
+
     /** Interface for BubbleBarView to communicate with its controller. */
     interface Controller {
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index 2e37dc7..2f92fbb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -21,6 +21,7 @@
 import android.graphics.Canvas;
 import android.graphics.Outline;
 import android.graphics.Rect;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -187,6 +188,16 @@
         mAppIcon.setImageBitmap(bubble.getBadge());
         mDotColor = bubble.getDotColor();
         mDotRenderer = new DotRenderer(mBubbleSize, bubble.getDotPath(), DEFAULT_PATH_SIZE);
+        String contentDesc = bubble.getInfo().getTitle();
+        if (TextUtils.isEmpty(contentDesc)) {
+            contentDesc = getResources().getString(R.string.bubble_bar_bubble_fallback_description);
+        }
+        String appName = bubble.getInfo().getAppName();
+        if (!TextUtils.isEmpty(appName)) {
+            contentDesc = getResources().getString(R.string.bubble_bar_bubble_description,
+                    contentDesc, appName);
+        }
+        setContentDescription(contentDesc);
     }
 
     /**
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index 49e54fb..cc579ab 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -392,7 +392,8 @@
             overflowView.setOverflow(BubbleBarOverflow(overflowView), bitmap)
             bubbleBarView.addView(overflowView)
 
-            val bubbleInfo = BubbleInfo("key", 0, null, null, 0, context.packageName, null, false)
+            val bubbleInfo =
+                BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false)
             bubbleView =
                 inflater.inflate(R.layout.bubblebar_item_view, bubbleBarView, false) as BubbleView
             bubble =
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 207d246..a9cca6d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -498,8 +498,4 @@
     <string name="ps_add_button_label">Install</string>
     <!-- Content description for install app icon -->
     <string name="ps_add_button_content_description">Install apps to Private Space</string>
-
-    <!-- Strings for bubble bar -->
-    <!-- content description for the overflow bubble [CHAR_LIMIT=none] -->
-    <string name="bubble_bar_overflow_description">Overflow</string>
 </resources>