Merge "Group chat flyout support"
diff --git a/packages/SystemUI/res/layout/bubble_flyout.xml b/packages/SystemUI/res/layout/bubble_flyout.xml
index 5f773f4..7ab0a0c 100644
--- a/packages/SystemUI/res/layout/bubble_flyout.xml
+++ b/packages/SystemUI/res/layout/bubble_flyout.xml
@@ -15,25 +15,50 @@
   -->
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <FrameLayout
+    <LinearLayout
         android:id="@+id/bubble_flyout_text_container"
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
+        android:orientation="horizontal"
         android:clipToPadding="false"
-        android:paddingLeft="@dimen/bubble_flyout_padding_x"
-        android:paddingRight="@dimen/bubble_flyout_padding_x"
+        android:clipChildren="false"
+        android:paddingStart="@dimen/bubble_flyout_padding_x"
+        android:paddingEnd="@dimen/bubble_flyout_padding_x"
         android:paddingTop="@dimen/bubble_flyout_padding_y"
         android:paddingBottom="@dimen/bubble_flyout_padding_y"
         android:translationZ="@dimen/bubble_flyout_elevation">
 
-        <TextView
-            android:id="@+id/bubble_flyout_text"
+        <ImageView
+            android:id="@+id/bubble_flyout_avatar"
+            android:layout_width="30dp"
+            android:layout_height="30dp"
+            android:layout_marginEnd="@dimen/bubble_flyout_avatar_message_space"
+            android:scaleType="centerInside"
+            android:src="@drawable/ic_create_bubble"/>
+
+        <LinearLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:fontFamily="@*android:string/config_bodyFontFamily"
-            android:maxLines="2"
-            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
+            android:orientation="vertical">
 
-    </FrameLayout>
+            <TextView
+                android:id="@+id/bubble_flyout_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
+                android:maxLines="1"
+                android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
+
+            <TextView
+                android:id="@+id/bubble_flyout_text"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:fontFamily="@*android:string/config_bodyFontFamily"
+                android:maxLines="2"
+                android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
+
+        </LinearLayout>
+
+    </LinearLayout>
 
 </merge>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 26af4ec..65ca9f2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1097,13 +1097,15 @@
     <!-- How much the bubble flyout text container is elevated. -->
     <dimen name="bubble_flyout_elevation">4dp</dimen>
     <!-- How much padding is around the left and right sides of the flyout text. -->
-    <dimen name="bubble_flyout_padding_x">16dp</dimen>
+    <dimen name="bubble_flyout_padding_x">12dp</dimen>
     <!-- How much padding is around the top and bottom of the flyout text. -->
-    <dimen name="bubble_flyout_padding_y">8dp</dimen>
+    <dimen name="bubble_flyout_padding_y">10dp</dimen>
     <!-- Size of the triangle that points from the flyout to the bubble stack. -->
     <dimen name="bubble_flyout_pointer_size">6dp</dimen>
     <!-- How much space to leave between the flyout (tip of the arrow) and the bubble stack. -->
     <dimen name="bubble_flyout_space_from_bubble">8dp</dimen>
+    <!-- How much space to leave between the flyout text and the avatar displayed in the flyout. -->
+    <dimen name="bubble_flyout_avatar_message_space">6dp</dimen>
     <!-- Padding between status bar and bubbles when displayed in expanded state -->
     <dimen name="bubble_padding_top">16dp</dimen>
     <!-- Size of individual bubbles. -->
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index b89305e..2d9775d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -32,20 +32,17 @@
 import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.os.Parcelable;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.List;
 import java.util.Objects;
 
 /**
@@ -85,6 +82,18 @@
     /** Whether flyout text should be suppressed, regardless of any other flags or state. */
     private boolean mSuppressFlyout;
 
+    /**
+     * Presentational info about the flyout.
+     */
+    public static class FlyoutMessage {
+        @Nullable public Drawable senderAvatar;
+        @Nullable public CharSequence senderName;
+        @Nullable public CharSequence message;
+        @Nullable public boolean isGroupChat;
+    }
+
+    private FlyoutMessage mFlyoutMessage;
+
     public static String groupId(NotificationEntry entry) {
         UserHandle user = entry.getSbn().getUser();
         return user.getIdentifier() + "|" + entry.getSbn().getPackageName();
@@ -194,6 +203,7 @@
 
         mShortcutInfo = info.shortcutInfo;
         mAppName = info.appName;
+        mFlyoutMessage = info.flyoutMessage;
 
         mExpandedView.update(this);
         mIconView.update(this, info.badgedBubbleImage, info.dotColor, info.dotPath);
@@ -307,6 +317,10 @@
         mSuppressFlyout = suppressFlyout;
     }
 
+    FlyoutMessage getFlyoutMessage() {
+        return mFlyoutMessage;
+    }
+
     /**
      * Returns whether the notification for this bubble is a foreground service. It shows that this
      * is an ongoing bubble.
@@ -368,72 +382,6 @@
         return intent;
     }
 
-    /**
-     * Returns our best guess for the most relevant text summary of the latest update to this
-     * notification, based on its type. Returns null if there should not be an update message.
-     */
-    CharSequence getUpdateMessage(Context context) {
-        final Notification underlyingNotif = mEntry.getSbn().getNotification();
-        final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
-
-        try {
-            if (Notification.BigTextStyle.class.equals(style)) {
-                // Return the big text, it is big so probably important. If it's not there use the
-                // normal text.
-                CharSequence bigText =
-                        underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
-                return !TextUtils.isEmpty(bigText)
-                        ? bigText
-                        : underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
-            } else if (Notification.MessagingStyle.class.equals(style)) {
-                final List<Notification.MessagingStyle.Message> messages =
-                        Notification.MessagingStyle.Message.getMessagesFromBundleArray(
-                                (Parcelable[]) underlyingNotif.extras.get(
-                                        Notification.EXTRA_MESSAGES));
-
-                final Notification.MessagingStyle.Message latestMessage =
-                        Notification.MessagingStyle.findLatestIncomingMessage(messages);
-
-                if (latestMessage != null) {
-                    final CharSequence personName = latestMessage.getSenderPerson() != null
-                            ? latestMessage.getSenderPerson().getName()
-                            : null;
-
-                    // Prepend the sender name if available since group chats also use messaging
-                    // style.
-                    if (!TextUtils.isEmpty(personName)) {
-                        return context.getResources().getString(
-                                R.string.notification_summary_message_format,
-                                personName,
-                                latestMessage.getText());
-                    } else {
-                        return latestMessage.getText();
-                    }
-                }
-            } else if (Notification.InboxStyle.class.equals(style)) {
-                CharSequence[] lines =
-                        underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES);
-
-                // Return the last line since it should be the most recent.
-                if (lines != null && lines.length > 0) {
-                    return lines[lines.length - 1];
-                }
-            } else if (Notification.MediaStyle.class.equals(style)) {
-                // Return nothing, media updates aren't typically useful as a text update.
-                return null;
-            } else {
-                // Default to text extra.
-                return underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
-            }
-        } catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) {
-            // No use crashing, we'll just return null and the caller will assume there's no update
-            // message.
-            e.printStackTrace();
-        }
-
-        return null;
-    }
-
     private int getDimenForPackageUser(Context context, int resId, String pkg, int userId) {
         PackageManager pm = context.getPackageManager();
         Resources r;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
index 78e98eb..4194352 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
@@ -32,11 +32,13 @@
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.graphics.drawable.ShapeDrawable;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
@@ -65,7 +67,9 @@
     private final float mCornerRadius;
 
     private final ViewGroup mFlyoutTextContainer;
-    private final TextView mFlyoutText;
+    private final ImageView mSenderAvatar;
+    private final TextView mSenderText;
+    private final TextView mMessageText;
 
     /** Values related to the 'new' dot which we use to figure out where to collapse the flyout. */
     private final float mNewDotRadius;
@@ -142,7 +146,9 @@
         LayoutInflater.from(context).inflate(R.layout.bubble_flyout, this, true);
 
         mFlyoutTextContainer = findViewById(R.id.bubble_flyout_text_container);
-        mFlyoutText = mFlyoutTextContainer.findViewById(R.id.bubble_flyout_text);
+        mSenderText = findViewById(R.id.bubble_flyout_name);
+        mSenderAvatar = findViewById(R.id.bubble_flyout_avatar);
+        mMessageText = mFlyoutTextContainer.findViewById(R.id.bubble_flyout_text);
 
         final Resources res = getResources();
         mFlyoutPadding = res.getDimensionPixelSize(R.dimen.bubble_flyout_padding_x);
@@ -204,9 +210,34 @@
 
     /** Configures the flyout, collapsed into to dot form. */
     void setupFlyoutStartingAsDot(
-            CharSequence updateMessage, PointF stackPos, float parentWidth,
-            boolean arrowPointingLeft, int dotColor, @Nullable Runnable onLayoutComplete,
-            @Nullable Runnable onHide, float[] dotCenter, boolean hideDot) {
+            Bubble.FlyoutMessage flyoutMessage,
+            PointF stackPos,
+            float parentWidth,
+            boolean arrowPointingLeft,
+            int dotColor,
+            @Nullable Runnable onLayoutComplete,
+            @Nullable Runnable onHide,
+            float[] dotCenter,
+            boolean hideDot) {
+
+        if (flyoutMessage.senderAvatar != null && flyoutMessage.isGroupChat) {
+            mSenderAvatar.setVisibility(VISIBLE);
+            mSenderAvatar.setImageDrawable(flyoutMessage.senderAvatar);
+        } else {
+            mSenderAvatar.setVisibility(GONE);
+            mSenderAvatar.setTranslationX(0);
+            mMessageText.setTranslationX(0);
+            mSenderText.setTranslationX(0);
+        }
+
+        // Name visibility
+        if (!TextUtils.isEmpty(flyoutMessage.senderName)) {
+            mSenderText.setText(flyoutMessage.senderName);
+            mSenderText.setVisibility(VISIBLE);
+        } else {
+            mSenderText.setVisibility(GONE);
+        }
+
         mArrowPointingLeft = arrowPointingLeft;
         mDotColor = dotColor;
         mOnHide = onHide;
@@ -217,15 +248,15 @@
         // Set the flyout TextView's max width in terms of percent, and then subtract out the
         // padding so that the entire flyout view will be the desired width (rather than the
         // TextView being the desired width + extra padding).
-        mFlyoutText.setMaxWidth(
+        mMessageText.setMaxWidth(
                 (int) (parentWidth * FLYOUT_MAX_WIDTH_PERCENT) - mFlyoutPadding * 2);
-        mFlyoutText.setText(updateMessage);
+        mMessageText.setText(flyoutMessage.message);
 
         // Wait for the TextView to lay out so we know its line count.
         post(() -> {
             float restingTranslationY;
             // Multi line flyouts get top-aligned to the bubble.
-            if (mFlyoutText.getLineCount() > 1) {
+            if (mMessageText.getLineCount() > 1) {
                 restingTranslationY = stackPos.y + mBubbleIconTopPadding;
             } else {
                 // Single line flyouts are vertically centered with respect to the bubble.
@@ -289,11 +320,20 @@
         mPercentStillFlyout = (1f - mPercentTransitionedToDot);
 
         // Move and fade out the text.
-        mFlyoutText.setTranslationX(
-                (mArrowPointingLeft ? -getWidth() : getWidth()) * mPercentTransitionedToDot);
-        mFlyoutText.setAlpha(clampPercentage(
+        final float translationX = mPercentTransitionedToDot
+                * (mArrowPointingLeft ? -getWidth() : getWidth());
+        final float alpha = clampPercentage(
                 (mPercentStillFlyout - (1f - BubbleStackView.FLYOUT_DRAG_PERCENT_DISMISS))
-                        / BubbleStackView.FLYOUT_DRAG_PERCENT_DISMISS));
+                        / BubbleStackView.FLYOUT_DRAG_PERCENT_DISMISS);
+
+        mMessageText.setTranslationX(translationX);
+        mMessageText.setAlpha(alpha);
+
+        mSenderText.setTranslationX(translationX);
+        mSenderText.setAlpha(alpha);
+
+        mSenderAvatar.setTranslationX(translationX);
+        mSenderAvatar.setAlpha(alpha);
 
         // Reduce the elevation towards that of the topmost bubble.
         setTranslationZ(
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 02314bf..54a42a6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -1377,9 +1377,10 @@
      */
     @VisibleForTesting
     void animateInFlyoutForBubble(Bubble bubble) {
-        final CharSequence updateMessage = bubble.getUpdateMessage(getContext());
+        Bubble.FlyoutMessage flyoutMessage = bubble.getFlyoutMessage();
         final BadgedImageView bubbleView = bubble.getIconView();
-        if (updateMessage == null
+        if (flyoutMessage == null
+                || flyoutMessage.message == null
                 || !bubble.showFlyout()
                 || isExpanded()
                 || mIsExpansionAnimating
@@ -1432,8 +1433,8 @@
                 };
                 mFlyout.postDelayed(mAnimateInFlyout, 200);
             };
-            mFlyout.setupFlyoutStartingAsDot(
-                    updateMessage, mStackAnimationController.getStackPosition(), getWidth(),
+            mFlyout.setupFlyoutStartingAsDot(flyoutMessage,
+                    mStackAnimationController.getStackPosition(), getWidth(),
                     mStackAnimationController.isStackOnLeftSide(),
                     bubble.getIconView().getDotColor() /* dotColor */,
                     expandFlyoutAfterDelay /* onLayoutComplete */,
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 8924cd6..e705584 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -21,6 +21,9 @@
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.Person;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -31,7 +34,9 @@
 import android.graphics.Path;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
+import android.os.Parcelable;
 import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.PathParser;
 import android.view.LayoutInflater;
@@ -41,8 +46,10 @@
 import com.android.internal.graphics.ColorUtils;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.lang.ref.WeakReference;
+import java.util.List;
 
 /**
  * Simple task to inflate views & load necessary info to display a bubble.
@@ -98,6 +105,9 @@
         }
     }
 
+    /**
+     * Info necessary to render a bubble.
+     */
     static class BubbleViewInfo {
         BadgedImageView imageView;
         BubbleExpandedView expandedView;
@@ -106,6 +116,7 @@
         Bitmap badgedBubbleImage;
         int dotColor;
         Path dotPath;
+        Bubble.FlyoutMessage flyoutMessage;
 
         @Nullable
         static BubbleViewInfo populate(Context c, BubbleStackView stackView,
@@ -184,7 +195,80 @@
             info.dotPath = iconPath;
             info.dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color,
                     Color.WHITE, WHITE_SCRIM_ALPHA);
+
+            // Flyout
+            info.flyoutMessage = extractFlyoutMessage(c, b.getEntry());
             return info;
         }
     }
+
+
+    /**
+     * Returns our best guess for the most relevant text summary of the latest update to this
+     * notification, based on its type. Returns null if there should not be an update message.
+     */
+    @NonNull
+    static Bubble.FlyoutMessage extractFlyoutMessage(Context context,
+            NotificationEntry entry) {
+        final Notification underlyingNotif = entry.getSbn().getNotification();
+        final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
+
+        Bubble.FlyoutMessage bubbleMessage = new Bubble.FlyoutMessage();
+        bubbleMessage.isGroupChat = underlyingNotif.extras.getBoolean(
+                Notification.EXTRA_IS_GROUP_CONVERSATION);
+        try {
+            if (Notification.BigTextStyle.class.equals(style)) {
+                // Return the big text, it is big so probably important. If it's not there use the
+                // normal text.
+                CharSequence bigText =
+                        underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
+                bubbleMessage.message = !TextUtils.isEmpty(bigText)
+                        ? bigText
+                        : underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
+                return bubbleMessage;
+            } else if (Notification.MessagingStyle.class.equals(style)) {
+                final List<Notification.MessagingStyle.Message> messages =
+                        Notification.MessagingStyle.Message.getMessagesFromBundleArray(
+                                (Parcelable[]) underlyingNotif.extras.get(
+                                        Notification.EXTRA_MESSAGES));
+
+                final Notification.MessagingStyle.Message latestMessage =
+                        Notification.MessagingStyle.findLatestIncomingMessage(messages);
+                if (latestMessage != null) {
+                    bubbleMessage.message = latestMessage.getText();
+                    Person sender = latestMessage.getSenderPerson();
+                    bubbleMessage.senderName = sender != null
+                            ? sender.getName()
+                            : null;
+                    bubbleMessage.senderAvatar = sender != null
+                            ? sender.getIcon().loadDrawable(context)
+                            : null;
+                    return bubbleMessage;
+                }
+            } else if (Notification.InboxStyle.class.equals(style)) {
+                CharSequence[] lines =
+                        underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES);
+
+                // Return the last line since it should be the most recent.
+                if (lines != null && lines.length > 0) {
+                    bubbleMessage.message = lines[lines.length - 1];
+                    return bubbleMessage;
+                }
+            } else if (Notification.MediaStyle.class.equals(style)) {
+                // Return nothing, media updates aren't typically useful as a text update.
+                return bubbleMessage;
+            } else {
+                // Default to text extra.
+                bubbleMessage.message =
+                        underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
+                return bubbleMessage;
+            }
+        } catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) {
+            // No use crashing, we'll just return null and the caller will assume there's no update
+            // message.
+            e.printStackTrace();
+        }
+
+        return bubbleMessage;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java
index 376ecf7..fd6e2ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java
@@ -45,14 +45,22 @@
 public class BubbleFlyoutViewTest extends SysuiTestCase {
     private BubbleFlyoutView mFlyout;
     private TextView mFlyoutText;
+    private TextView mSenderName;
     private float[] mDotCenter = new float[2];
+    private Bubble.FlyoutMessage mFlyoutMessage;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+
+        mFlyoutMessage = new Bubble.FlyoutMessage();
+        mFlyoutMessage.senderName = "Josh";
+        mFlyoutMessage.message = "Hello";
+
         mFlyout = new BubbleFlyoutView(getContext());
 
         mFlyoutText = mFlyout.findViewById(R.id.bubble_flyout_text);
+        mSenderName = mFlyout.findViewById(R.id.bubble_flyout_name);
         mDotCenter[0] = 30;
         mDotCenter[1] = 30;
     }
@@ -60,19 +68,21 @@
     @Test
     public void testShowFlyout_isVisible() {
         mFlyout.setupFlyoutStartingAsDot(
-                "Hello", new PointF(100, 100), 500, true, Color.WHITE, null, null, mDotCenter,
+                mFlyoutMessage,
+                new PointF(100, 100), 500, true, Color.WHITE, null, null, mDotCenter,
                 false);
         mFlyout.setVisibility(View.VISIBLE);
 
         assertEquals("Hello", mFlyoutText.getText());
+        assertEquals("Josh", mSenderName.getText());
         assertEquals(View.VISIBLE, mFlyout.getVisibility());
     }
 
     @Test
     public void testFlyoutHide_runsCallback() {
         Runnable after = Mockito.mock(Runnable.class);
-        mFlyout.setupFlyoutStartingAsDot(
-                "Hello", new PointF(100, 100), 500, true, Color.WHITE, null, after, mDotCenter,
+        mFlyout.setupFlyoutStartingAsDot(mFlyoutMessage,
+                new PointF(100, 100), 500, true, Color.WHITE, null, after, mDotCenter,
                 false);
         mFlyout.hideFlyout();
 
@@ -81,8 +91,8 @@
 
     @Test
     public void testSetCollapsePercent() {
-        mFlyout.setupFlyoutStartingAsDot(
-                "Hello", new PointF(100, 100), 500, true, Color.WHITE, null, null, mDotCenter,
+        mFlyout.setupFlyoutStartingAsDot(mFlyoutMessage,
+                new PointF(100, 100), 500, true, Color.WHITE, null, null, mDotCenter,
                 false);
         mFlyout.setVisibility(View.VISIBLE);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
index 3c42fd1..02f721c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -45,7 +45,6 @@
     private Notification mNotif;
 
     private NotificationEntry mEntry;
-    private Bubble mBubble;
     private Bundle mExtras;
 
     @Before
@@ -58,7 +57,6 @@
         mEntry = new NotificationEntryBuilder()
                 .setNotification(mNotif)
                 .build();
-        mBubble = new Bubble(mEntry);
     }
 
     @Test
@@ -66,7 +64,8 @@
         final String msg = "Hello there!";
         doReturn(Notification.Style.class).when(mNotif).getNotificationStyle();
         mExtras.putCharSequence(Notification.EXTRA_TEXT, msg);
-        assertEquals(msg, mBubble.getUpdateMessage(mContext));
+        assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mContext,
+                mEntry).message);
     }
 
     @Test
@@ -77,7 +76,8 @@
         mExtras.putCharSequence(Notification.EXTRA_BIG_TEXT, msg);
 
         // Should be big text, not the small text.
-        assertEquals(msg, mBubble.getUpdateMessage(mContext));
+        assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mContext,
+                mEntry).message);
     }
 
     @Test
@@ -85,7 +85,8 @@
         doReturn(Notification.MediaStyle.class).when(mNotif).getNotificationStyle();
 
         // Media notifs don't get update messages.
-        assertNull(mBubble.getUpdateMessage(mContext));
+        assertNull(BubbleViewInfoTask.extractFlyoutMessage(mContext,
+                mEntry).message);
     }
 
     @Test
@@ -100,7 +101,8 @@
                         "Really? I prefer them that way."});
 
         // Should be the last one only.
-        assertEquals("Really? I prefer them that way.", mBubble.getUpdateMessage(mContext));
+        assertEquals("Really? I prefer them that way.",
+                BubbleViewInfoTask.extractFlyoutMessage(mContext, mEntry).message);
     }
 
     @Test
@@ -115,6 +117,10 @@
                                 "Oh, hello!", 0, "Mady").toBundle()});
 
         // Should be the last one only.
-        assertEquals("Mady: Oh, hello!", mBubble.getUpdateMessage(mContext));
+        assertEquals("Oh, hello!",
+                BubbleViewInfoTask.extractFlyoutMessage(mContext, mEntry).message);
+        assertEquals("Mady",
+                BubbleViewInfoTask.extractFlyoutMessage(mContext,
+                        mEntry).senderName);
     }
 }