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