Talkback for focus on collapsed bubble stack
Bug: 131610000
Test: manual
Change-Id: I6aa79dfea751c47c86e93f56f7916dd56dbc003f
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 8aad0f8..f60e95e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -18,6 +18,9 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.view.LayoutInflater;
@@ -37,6 +40,7 @@
private final String mKey;
private final String mGroupId;
+ private String mAppName;
private final BubbleExpandedView.OnBubbleBlockedListener mListener;
private boolean mInflated;
@@ -45,6 +49,7 @@
BubbleExpandedView expandedView;
private long mLastUpdated;
private long mLastAccessed;
+ private PackageManager mPm;
public static String groupId(NotificationEntry entry) {
UserHandle user = entry.notification.getUser();
@@ -53,16 +58,33 @@
/** Used in tests when no UI is required. */
@VisibleForTesting(visibility = PRIVATE)
- Bubble(NotificationEntry e) {
- this (e, null);
+ Bubble(Context context, NotificationEntry e) {
+ this (context, e, null);
}
- Bubble(NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener) {
+ Bubble(Context context, NotificationEntry e,
+ BubbleExpandedView.OnBubbleBlockedListener listener) {
entry = e;
mKey = e.key;
mLastUpdated = e.notification.getPostTime();
mGroupId = groupId(e);
mListener = listener;
+
+ mPm = context.getPackageManager();
+ ApplicationInfo info;
+ try {
+ info = mPm.getApplicationInfo(
+ entry.notification.getPackageName(),
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (info != null) {
+ mAppName = String.valueOf(mPm.getApplicationLabel(info));
+ }
+ } catch (PackageManager.NameNotFoundException unused) {
+ mAppName = entry.notification.getPackageName();
+ }
}
public String getKey() {
@@ -77,6 +99,10 @@
return entry.notification.getPackageName();
}
+ public String getAppName() {
+ return mAppName;
+ }
+
boolean isInflated() {
return mInflated;
}
@@ -97,9 +123,9 @@
expandedView = (BubbleExpandedView) inflater.inflate(
R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
- expandedView.setEntry(entry, stackView);
-
+ expandedView.setEntry(entry, stackView, mAppName);
expandedView.setOnBlockedListener(mListener);
+
mInflated = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 48edf67..aed117b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -193,7 +193,7 @@
if (shouldCollapse) {
collapseStack();
}
- updateVisibility();
+ updateStack();
}
}
@@ -534,10 +534,11 @@
}
}
+ // Runs on state change.
@Override
public void apply() {
mNotificationEntryManager.updateNotifications();
- updateVisibility();
+ updateStack();
if (DEBUG) {
Log.d(TAG, "[BubbleData]");
@@ -554,34 +555,31 @@
};
/**
- * Lets any listeners know if bubble state has changed.
+ * Updates the visibility of the bubbles based on current state.
+ * Does not un-bubble, just hides or un-hides. Notifies any
+ * {@link BubbleStateChangeListener}s of visibility changes.
+ * Updates stack description for TalkBack focus.
*/
- private void updateBubblesShowing() {
+ public void updateStack() {
if (mStackView == null) {
return;
}
-
- boolean hadBubbles = mStatusBarWindowController.getBubblesShowing();
- boolean hasBubblesShowing = hasBubbles() && mStackView.getVisibility() == VISIBLE;
- mStatusBarWindowController.setBubblesShowing(hasBubblesShowing);
- if (mStateChangeListener != null && hadBubbles != hasBubblesShowing) {
- mStateChangeListener.onHasBubblesChanged(hasBubblesShowing);
- }
- }
-
- /**
- * Updates the visibility of the bubbles based on current state.
- * Does not un-bubble, just hides or un-hides. Will notify any
- * {@link BubbleStateChangeListener}s if visibility changes.
- */
- public void updateVisibility() {
if (mStatusBarStateListener.getCurrentState() == SHADE && hasBubbles()) {
// Bubbles only appear in unlocked shade
mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE);
} else if (mStackView != null) {
mStackView.setVisibility(INVISIBLE);
}
- updateBubblesShowing();
+
+ // Let listeners know if bubble state changed.
+ boolean hadBubbles = mStatusBarWindowController.getBubblesShowing();
+ boolean hasBubblesShowing = hasBubbles() && mStackView.getVisibility() == VISIBLE;
+ mStatusBarWindowController.setBubblesShowing(hasBubblesShowing);
+ if (mStateChangeListener != null && hadBubbles != hasBubblesShowing) {
+ mStateChangeListener.onHasBubblesChanged(hasBubblesShowing);
+ }
+
+ mStackView.updateContentDescription();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 9156e06..1858244 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -178,7 +178,7 @@
Bubble bubble = getBubbleWithKey(entry.key);
if (bubble == null) {
// Create a new bubble
- bubble = new Bubble(entry, this::onBubbleBlocked);
+ bubble = new Bubble(mContext, entry, this::onBubbleBlocked);
doAdd(bubble);
trim();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 39867c3..fa137a1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -244,9 +244,10 @@
/**
* Sets the notification entry used to populate this view.
*/
- public void setEntry(NotificationEntry entry, BubbleStackView stackView) {
+ public void setEntry(NotificationEntry entry, BubbleStackView stackView, String appName) {
mStackView = stackView;
mEntry = entry;
+ mAppName = appName;
ApplicationInfo info;
try {
@@ -257,12 +258,10 @@
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE);
if (info != null) {
- mAppName = String.valueOf(mPm.getApplicationLabel(info));
mAppIcon = mPm.getApplicationIcon(info);
}
} catch (PackageManager.NameNotFoundException e) {
- // Ahh... just use package name
- mAppName = entry.notification.getPackageName();
+ // Do nothing.
}
if (mAppIcon == null) {
mAppIcon = mPm.getDefaultActivityIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 4fef157..4ad3a33 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -23,6 +23,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
+import android.app.Notification;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.ColorMatrix;
@@ -553,6 +554,43 @@
return false;
}
+ /**
+ * Update content description for a11y TalkBack.
+ */
+ public void updateContentDescription() {
+ if (mBubbleData.getBubbles().isEmpty()) {
+ return;
+ }
+ Bubble topBubble = mBubbleData.getBubbles().get(0);
+ String appName = topBubble.getAppName();
+ Notification notification = topBubble.entry.notification.getNotification();
+ CharSequence titleCharSeq = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
+ String titleStr = getResources().getString(R.string.stream_notification);
+ if (titleCharSeq != null) {
+ titleStr = titleCharSeq.toString();
+ }
+ int moreCount = mBubbleContainer.getChildCount() - 1;
+
+ // Example: Title from app name.
+ String singleDescription = getResources().getString(
+ R.string.bubble_content_description_single, titleStr, appName);
+
+ // Example: Title from app name and 4 more.
+ String stackDescription = getResources().getString(
+ R.string.bubble_content_description_stack, titleStr, appName, moreCount);
+
+ if (mIsExpanded) {
+ // TODO(b/129522932) - update content description for each bubble in expanded view.
+ } else {
+ // Collapsed stack.
+ if (moreCount > 0) {
+ mBubbleContainer.setContentDescription(stackDescription);
+ } else {
+ mBubbleContainer.setContentDescription(singleDescription);
+ }
+ }
+ }
+
private void updateSystemGestureExcludeRects() {
// Exclude the region occupied by the first BubbleView in the stack
Rect excludeZone = mSystemGestureExclusionRects.get(0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 33b2e6e..364a0f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -103,13 +103,13 @@
mEntryB3 = createBubbleEntry(1, "b3", "package.b");
mEntryC1 = createBubbleEntry(1, "c1", "package.c");
- mBubbleA1 = new Bubble(mEntryA1);
- mBubbleA2 = new Bubble(mEntryA2);
- mBubbleA3 = new Bubble(mEntryA3);
- mBubbleB1 = new Bubble(mEntryB1);
- mBubbleB2 = new Bubble(mEntryB2);
- mBubbleB3 = new Bubble(mEntryB3);
- mBubbleC1 = new Bubble(mEntryC1);
+ mBubbleA1 = new Bubble(mContext, mEntryA1);
+ mBubbleA2 = new Bubble(mContext, mEntryA2);
+ mBubbleA3 = new Bubble(mContext, mEntryA3);
+ mBubbleB1 = new Bubble(mContext, mEntryB1);
+ mBubbleB2 = new Bubble(mContext, mEntryB2);
+ mBubbleB3 = new Bubble(mContext, mEntryB3);
+ mBubbleC1 = new Bubble(mContext, mEntryC1);
mBubbleData = new BubbleData(getContext());
@@ -803,4 +803,4 @@
private static <T> List<T> listOf(T... a) {
return ImmutableList.copyOf(a);
}
-}
+}
\ No newline at end of file