diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index cf987c3db3..340d25b0f9 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -332,4 +332,14 @@
%1$s and %2$s
+
+
+
+ Bubble
+
+ Overflow
+
+ %1$s from %2$s
+
+ %1$s and %2$d more
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 23e52e6f71..7eeea8431d 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 @@ public class BubbleBarView extends FrameLayout {
}
super.addView(child, index, params);
updateWidth();
+ updateBubbleAccessibilityStates();
+ updateContentDescription();
}
// TODO: (b/283309949) animate it
@@ -634,6 +636,8 @@ public class BubbleBarView extends FrameLayout {
mBubbleBarBackground.showArrow(false);
}
updateWidth();
+ updateBubbleAccessibilityStates();
+ updateContentDescription();
}
private void updateWidth() {
@@ -799,6 +803,7 @@ public class BubbleBarView extends FrameLayout {
}
}
updateChildrenRenderNodeProperties(mBubbleBarLocation);
+ updateContentDescription();
}
}
@@ -927,6 +932,7 @@ public class BubbleBarView extends FrameLayout {
} else {
mWidthAnimator.reverse();
}
+ updateBubbleAccessibilityStates();
}
}
@@ -998,6 +1004,47 @@ public class BubbleBarView extends FrameLayout {
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 2e37dc7695..2f92fbbaba 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.Bitmap;
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 @@ public class BubbleView extends ConstraintLayout {
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 49e54fb1f1..cc579abc9e 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 @@ class BubbleBarViewAnimatorTest {
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 207d246933..a9cca6de41 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -498,8 +498,4 @@
Install
Install apps to Private Space
-
-
-
- Overflow