Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +51 −1 Original line number Diff line number Diff line Loading @@ -368,6 +368,13 @@ public class BubbleController { return; } } for (Bubble b : mBubbleData.getOverflowBubbles()) { if (task.taskId == b.getTaskId()) { promoteBubbleFromOverflow(b); mBubbleData.setExpanded(true); return; } } } }); Loading Loading @@ -815,7 +822,35 @@ public class BubbleController { setIsBubble(bubble, true /* isBubble */); } @VisibleForTesting /** * Expands and selects the provided bubble as long as it already exists in the stack or the * overflow. * * This is currently only used when opening a bubble via clicking on a conversation widget. */ public void expandStackAndSelectBubble(Bubble b) { if (b == null) { return; } if (mBubbleData.hasBubbleInStackWithKey(b.getKey())) { // already in the stack mBubbleData.setSelectedBubble(b); mBubbleData.setExpanded(true); } else if (mBubbleData.hasOverflowBubbleWithKey(b.getKey())) { // promote it out of the overflow promoteBubbleFromOverflow(b); } } /** * Expands and selects a bubble based on the provided {@link BubbleEntry}. If no bubble * exists for this entry, and it is able to bubble, a new bubble will be created. * * This is the method to use when opening a bubble via a notification or in a state where * the device might not be unlocked. * * @param entry the entry to use for the bubble. */ public void expandStackAndSelectBubble(BubbleEntry entry) { if (mIsStatusBarShade) { mNotifEntryToExpandOnShadeUnlock = null; Loading Loading @@ -1382,6 +1417,21 @@ public class BubbleController { }); } @Override public void expandStackAndSelectBubble(Bubble bubble) { mMainExecutor.execute(() -> { BubbleController.this.expandStackAndSelectBubble(bubble); }); } @Override @Nullable public Bubble getBubbleWithShortcutId(String shortcutId) { return mMainExecutor.executeBlockingForResult(() -> { return BubbleController.this.mBubbleData.getAnyBubbleWithShortcutId(shortcutId); }, Bubble.class); } @Override public void onTaskbarChanged(Bundle b) { mMainExecutor.execute(() -> { Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +29 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.LocusId; import android.content.pm.ShortcutInfo; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; Loading Loading @@ -874,6 +875,34 @@ public class BubbleData { return b; } /** @return any bubble (in the stack or the overflow) that matches the provided shortcutId. */ @Nullable Bubble getAnyBubbleWithShortcutId(String shortcutId) { if (TextUtils.isEmpty(shortcutId)) { return null; } for (int i = 0; i < mBubbles.size(); i++) { Bubble bubble = mBubbles.get(i); String bubbleShortcutId = bubble.getShortcutInfo() != null ? bubble.getShortcutInfo().getId() : bubble.getMetadataShortcutId(); if (shortcutId.equals(bubbleShortcutId)) { return bubble; } } for (int i = 0; i < mOverflowBubbles.size(); i++) { Bubble bubble = mOverflowBubbles.get(i); String bubbleShortcutId = bubble.getShortcutInfo() != null ? bubble.getShortcutInfo().getId() : bubble.getMetadataShortcutId(); if (shortcutId.equals(bubbleShortcutId)) { return bubble; } } return null; } @VisibleForTesting(visibility = PRIVATE) @Nullable public Bubble getBubbleInStackWithKey(String key) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +13 −0 Original line number Diff line number Diff line Loading @@ -118,6 +118,19 @@ public interface Bubbles { */ void expandStackAndSelectBubble(BubbleEntry entry); /** * Request the stack expand if needed, then select the specified Bubble as current. * * @param bubble the bubble to be selected */ void expandStackAndSelectBubble(Bubble bubble); /** * @return a bubble that matches the provided shortcutId, if one exists. */ @Nullable Bubble getBubbleWithShortcutId(String shortcutId); /** Called for any taskbar changes. */ void onTaskbarChanged(Bundle b); Loading packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java +46 −9 Original line number Diff line number Diff line Loading @@ -35,9 +35,11 @@ import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.bubbles.Bubble; import java.util.Optional; Loading @@ -53,14 +55,35 @@ public class LaunchConversationActivity extends Activity { private final UserManager mUserManager; private boolean mIsForTesting; private IStatusBarService mIStatusBarService; private CommandQueue mCommandQueue; private Bubble mBubble; private NotificationEntry mEntryToBubble; @Inject public LaunchConversationActivity(NotificationEntryManager notificationEntryManager, Optional<BubblesManager> bubblesManagerOptional, UserManager userManager) { Optional<BubblesManager> bubblesManagerOptional, UserManager userManager, CommandQueue commandQueue) { super(); mNotificationEntryManager = notificationEntryManager; mBubblesManagerOptional = bubblesManagerOptional; mUserManager = userManager; mCommandQueue = commandQueue; mCommandQueue.addCallback(new CommandQueue.Callbacks() { // (b/190833924) Wait for the app transition to finish before showing the bubble, // opening the bubble while the transition is happening can mess with the placement // of the bubble's surface. @Override public void appTransitionFinished(int displayId) { if (mBubblesManagerOptional.isPresent()) { if (mBubble != null) { mBubblesManagerOptional.get().expandStackAndSelectBubble(mBubble); } else if (mEntryToBubble != null) { mBubblesManagerOptional.get().expandStackAndSelectBubble(mEntryToBubble); } } mCommandQueue.removeCallback(this); } }); } @Override Loading Loading @@ -95,15 +118,29 @@ public class LaunchConversationActivity extends Activity { return; } // We can potentially bubble without a notification, so rather than rely on // notificationKey here (which could be null if there's no notification or if the // bubble is suppressing the notification), so we'll use the shortcutId for lookups. // This misses one specific case: a bubble that was never opened & still has a // visible notification, but the bubble was dismissed & aged out of the overflow. // So it wouldn't exist in the stack or overflow to be looked up BUT the notif entry // would still exist & be bubbleable. So if we don't get a bubble from the // shortcutId, fallback to notificationKey if it exists. if (mBubblesManagerOptional.isPresent()) { mBubble = mBubblesManagerOptional.get().getBubbleWithShortcutId(tileId); NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( notificationKey); if (entry != null && entry.canBubble() && mBubblesManagerOptional.isPresent()) { if (DEBUG) Log.d(TAG, "Open bubble for conversation"); mBubblesManagerOptional.get().expandStackAndSelectBubble(entry); if (mBubble != null || (entry != null && entry.canBubble())) { mEntryToBubble = entry; if (DEBUG) { Log.d(TAG, "Opening bubble: " + mBubble + ", entry: " + mEntryToBubble); } // Just opt-out and don't cancel the notification for bubbles. finish(); return; } } if (mIStatusBarService == null) { mIStatusBarService = IStatusBarService.Stub.asInterface( Loading packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +17 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.wm.shell.bubbles.Bubble; import com.android.wm.shell.bubbles.BubbleEntry; import com.android.wm.shell.bubbles.Bubbles; Loading Loading @@ -657,6 +658,22 @@ public class BubblesManager implements Dumpable { mBubbles.expandStackAndSelectBubble(notifToBubbleEntry(entry)); } /** * Request the stack expand if needed, then select the specified Bubble as current. * * @param bubble the bubble to be selected */ public void expandStackAndSelectBubble(Bubble bubble) { mBubbles.expandStackAndSelectBubble(bubble); } /** * @return a bubble that matches the provided shortcutId, if one exists. */ public Bubble getBubbleWithShortcutId(String shortcutId) { return mBubbles.getBubbleWithShortcutId(shortcutId); } /** See {@link NotifCallback}. */ public void addNotifCallback(NotifCallback callback) { mCallbacks.add(callback); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +51 −1 Original line number Diff line number Diff line Loading @@ -368,6 +368,13 @@ public class BubbleController { return; } } for (Bubble b : mBubbleData.getOverflowBubbles()) { if (task.taskId == b.getTaskId()) { promoteBubbleFromOverflow(b); mBubbleData.setExpanded(true); return; } } } }); Loading Loading @@ -815,7 +822,35 @@ public class BubbleController { setIsBubble(bubble, true /* isBubble */); } @VisibleForTesting /** * Expands and selects the provided bubble as long as it already exists in the stack or the * overflow. * * This is currently only used when opening a bubble via clicking on a conversation widget. */ public void expandStackAndSelectBubble(Bubble b) { if (b == null) { return; } if (mBubbleData.hasBubbleInStackWithKey(b.getKey())) { // already in the stack mBubbleData.setSelectedBubble(b); mBubbleData.setExpanded(true); } else if (mBubbleData.hasOverflowBubbleWithKey(b.getKey())) { // promote it out of the overflow promoteBubbleFromOverflow(b); } } /** * Expands and selects a bubble based on the provided {@link BubbleEntry}. If no bubble * exists for this entry, and it is able to bubble, a new bubble will be created. * * This is the method to use when opening a bubble via a notification or in a state where * the device might not be unlocked. * * @param entry the entry to use for the bubble. */ public void expandStackAndSelectBubble(BubbleEntry entry) { if (mIsStatusBarShade) { mNotifEntryToExpandOnShadeUnlock = null; Loading Loading @@ -1382,6 +1417,21 @@ public class BubbleController { }); } @Override public void expandStackAndSelectBubble(Bubble bubble) { mMainExecutor.execute(() -> { BubbleController.this.expandStackAndSelectBubble(bubble); }); } @Override @Nullable public Bubble getBubbleWithShortcutId(String shortcutId) { return mMainExecutor.executeBlockingForResult(() -> { return BubbleController.this.mBubbleData.getAnyBubbleWithShortcutId(shortcutId); }, Bubble.class); } @Override public void onTaskbarChanged(Bundle b) { mMainExecutor.execute(() -> { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +29 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.LocusId; import android.content.pm.ShortcutInfo; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; Loading Loading @@ -874,6 +875,34 @@ public class BubbleData { return b; } /** @return any bubble (in the stack or the overflow) that matches the provided shortcutId. */ @Nullable Bubble getAnyBubbleWithShortcutId(String shortcutId) { if (TextUtils.isEmpty(shortcutId)) { return null; } for (int i = 0; i < mBubbles.size(); i++) { Bubble bubble = mBubbles.get(i); String bubbleShortcutId = bubble.getShortcutInfo() != null ? bubble.getShortcutInfo().getId() : bubble.getMetadataShortcutId(); if (shortcutId.equals(bubbleShortcutId)) { return bubble; } } for (int i = 0; i < mOverflowBubbles.size(); i++) { Bubble bubble = mOverflowBubbles.get(i); String bubbleShortcutId = bubble.getShortcutInfo() != null ? bubble.getShortcutInfo().getId() : bubble.getMetadataShortcutId(); if (shortcutId.equals(bubbleShortcutId)) { return bubble; } } return null; } @VisibleForTesting(visibility = PRIVATE) @Nullable public Bubble getBubbleInStackWithKey(String key) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +13 −0 Original line number Diff line number Diff line Loading @@ -118,6 +118,19 @@ public interface Bubbles { */ void expandStackAndSelectBubble(BubbleEntry entry); /** * Request the stack expand if needed, then select the specified Bubble as current. * * @param bubble the bubble to be selected */ void expandStackAndSelectBubble(Bubble bubble); /** * @return a bubble that matches the provided shortcutId, if one exists. */ @Nullable Bubble getBubbleWithShortcutId(String shortcutId); /** Called for any taskbar changes. */ void onTaskbarChanged(Bundle b); Loading
packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java +46 −9 Original line number Diff line number Diff line Loading @@ -35,9 +35,11 @@ import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.bubbles.Bubble; import java.util.Optional; Loading @@ -53,14 +55,35 @@ public class LaunchConversationActivity extends Activity { private final UserManager mUserManager; private boolean mIsForTesting; private IStatusBarService mIStatusBarService; private CommandQueue mCommandQueue; private Bubble mBubble; private NotificationEntry mEntryToBubble; @Inject public LaunchConversationActivity(NotificationEntryManager notificationEntryManager, Optional<BubblesManager> bubblesManagerOptional, UserManager userManager) { Optional<BubblesManager> bubblesManagerOptional, UserManager userManager, CommandQueue commandQueue) { super(); mNotificationEntryManager = notificationEntryManager; mBubblesManagerOptional = bubblesManagerOptional; mUserManager = userManager; mCommandQueue = commandQueue; mCommandQueue.addCallback(new CommandQueue.Callbacks() { // (b/190833924) Wait for the app transition to finish before showing the bubble, // opening the bubble while the transition is happening can mess with the placement // of the bubble's surface. @Override public void appTransitionFinished(int displayId) { if (mBubblesManagerOptional.isPresent()) { if (mBubble != null) { mBubblesManagerOptional.get().expandStackAndSelectBubble(mBubble); } else if (mEntryToBubble != null) { mBubblesManagerOptional.get().expandStackAndSelectBubble(mEntryToBubble); } } mCommandQueue.removeCallback(this); } }); } @Override Loading Loading @@ -95,15 +118,29 @@ public class LaunchConversationActivity extends Activity { return; } // We can potentially bubble without a notification, so rather than rely on // notificationKey here (which could be null if there's no notification or if the // bubble is suppressing the notification), so we'll use the shortcutId for lookups. // This misses one specific case: a bubble that was never opened & still has a // visible notification, but the bubble was dismissed & aged out of the overflow. // So it wouldn't exist in the stack or overflow to be looked up BUT the notif entry // would still exist & be bubbleable. So if we don't get a bubble from the // shortcutId, fallback to notificationKey if it exists. if (mBubblesManagerOptional.isPresent()) { mBubble = mBubblesManagerOptional.get().getBubbleWithShortcutId(tileId); NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( notificationKey); if (entry != null && entry.canBubble() && mBubblesManagerOptional.isPresent()) { if (DEBUG) Log.d(TAG, "Open bubble for conversation"); mBubblesManagerOptional.get().expandStackAndSelectBubble(entry); if (mBubble != null || (entry != null && entry.canBubble())) { mEntryToBubble = entry; if (DEBUG) { Log.d(TAG, "Opening bubble: " + mBubble + ", entry: " + mEntryToBubble); } // Just opt-out and don't cancel the notification for bubbles. finish(); return; } } if (mIStatusBarService == null) { mIStatusBarService = IStatusBarService.Stub.asInterface( Loading
packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +17 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.wm.shell.bubbles.Bubble; import com.android.wm.shell.bubbles.BubbleEntry; import com.android.wm.shell.bubbles.Bubbles; Loading Loading @@ -657,6 +658,22 @@ public class BubblesManager implements Dumpable { mBubbles.expandStackAndSelectBubble(notifToBubbleEntry(entry)); } /** * Request the stack expand if needed, then select the specified Bubble as current. * * @param bubble the bubble to be selected */ public void expandStackAndSelectBubble(Bubble bubble) { mBubbles.expandStackAndSelectBubble(bubble); } /** * @return a bubble that matches the provided shortcutId, if one exists. */ public Bubble getBubbleWithShortcutId(String shortcutId) { return mBubbles.getBubbleWithShortcutId(shortcutId); } /** See {@link NotifCallback}. */ public void addNotifCallback(NotifCallback callback) { mCallbacks.add(callback); Loading