Merge changes from topic "cherrypicker-L54300000962624816:N96600001397456171" into main

* changes:
  Enable PrecomputedText in MessagingLayouts
  Create data extraction helper for Messaging Layouts
  Use PrecomputedText in MessagingLayouts
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 5b6b360..42be784 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -149,6 +149,7 @@
     private View mAppNameDivider;
     private TouchDelegateComposite mTouchDelegate = new TouchDelegateComposite(this);
     private ArrayList<MessagingLinearLayout.MessagingChild> mToRecycle = new ArrayList<>();
+    private boolean mPrecomputedTextEnabled = false;
 
     public ConversationLayout(@NonNull Context context) {
         super(context);
@@ -389,36 +390,37 @@
      */
     @RemotableViewMethod(asyncImpl = "setDataAsync")
     public void setData(Bundle extras) {
+        bind(parseMessagingData(extras, /* usePrecomputedText= */ false));
+    }
+
+    @NonNull
+    private MessagingData parseMessagingData(Bundle extras, boolean usePrecomputedText) {
         Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
-        List<Notification.MessagingStyle.Message> newMessages
-                = Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
+        List<Notification.MessagingStyle.Message> newMessages =
+                Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
         Parcelable[] histMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
-        List<Notification.MessagingStyle.Message> newHistoricMessages
-                = Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages);
+        List<Notification.MessagingStyle.Message> newHistoricMessages =
+                Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages);
 
         // mUser now set (would be nice to avoid the side effect but WHATEVER)
         final Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON, Person.class);
         // Append remote input history to newMessages (again, side effect is lame but WHATEVS)
         RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[])
-                extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, android.app.RemoteInputHistoryItem.class);
+                extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS,
+                        RemoteInputHistoryItem.class);
         addRemoteInputHistoryToMessages(newMessages, history);
 
         boolean showSpinner =
                 extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
         int unreadCount = extras.getInt(Notification.EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT);
 
-        // convert MessagingStyle.Message to MessagingMessage, re-using ones from a previous binding
-        // if they exist
         final List<MessagingMessage> newMessagingMessages =
-                createMessages(newMessages, /* isHistoric= */false,
-                        /* usePrecomputedText= */false);
+                createMessages(newMessages, /* isHistoric= */false, usePrecomputedText);
         final List<MessagingMessage> newHistoricMessagingMessages =
-                createMessages(newHistoricMessages, /* isHistoric= */true,
-                        /* usePrecomputedText= */false);
-        // bind it, baby
-        bindViews(user, showSpinner, unreadCount,
-                newMessagingMessages,
-                newHistoricMessagingMessages);
+                createMessages(newHistoricMessages, /* isHistoric= */true, usePrecomputedText);
+
+        return new MessagingData(user, showSpinner, unreadCount,
+                newHistoricMessagingMessages, newMessagingMessages);
     }
 
     /**
@@ -430,7 +432,33 @@
      */
     @NonNull
     public Runnable setDataAsync(Bundle extras) {
-        return () -> setData(extras);
+        if (!mPrecomputedTextEnabled) {
+            return () -> setData(extras);
+        }
+
+        final MessagingData messagingData =
+                parseMessagingData(extras, /* usePrecomputedText= */ true);
+
+        return () -> {
+            finalizeInflate(messagingData.getHistoricMessagingMessages());
+            finalizeInflate(messagingData.getNewMessagingMessages());
+
+            bind(messagingData);
+        };
+    }
+
+    /**
+     * enable/disable precomputed text usage
+     * @hide
+     */
+    public void setPrecomputedTextEnabled(boolean precomputedTextEnabled) {
+        mPrecomputedTextEnabled = precomputedTextEnabled;
+    }
+
+    private void finalizeInflate(List<MessagingMessage> historicMessagingMessages) {
+        for (MessagingMessage messagingMessage : historicMessagingMessages) {
+            messagingMessage.finalizeInflate();
+        }
     }
 
     @Override
@@ -460,17 +488,12 @@
         }
     }
 
+    private void bind(MessagingData messagingData) {
+        setUser(messagingData.getUser());
+        setUnreadCount(messagingData.getUnreadCount());
 
-    private void bindViews(Person user,
-            boolean showSpinner, int unreadCount, List<MessagingMessage> newMessagingMessages,
-            List<MessagingMessage> newHistoricMessagingMessages) {
-        setUser(user);
-        setUnreadCount(unreadCount);
-        bind(showSpinner, newMessagingMessages, newHistoricMessagingMessages);
-    }
-
-    private void bind(boolean showSpinner, List<MessagingMessage> messages,
-            List<MessagingMessage> historicMessages) {
+        List<MessagingMessage> messages = messagingData.getNewMessagingMessages();
+        List<MessagingMessage> historicMessages = messagingData.getHistoricMessagingMessages();
         // Copy our groups, before they get clobbered
         ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups);
 
@@ -483,7 +506,7 @@
 
         // Let's now create the views and reorder them accordingly
         //   side-effect: updates mGroups, mAddedGroups
-        createGroupViews(groups, senders, showSpinner);
+        createGroupViews(groups, senders, messagingData.getShowSpinner());
 
         // Let's first check which groups were removed altogether and remove them in one animation
         removeGroups(oldGroups);
@@ -585,7 +608,7 @@
 
             // When collapsed, we're displaying the image message in a dedicated container
             // on the right of the layout instead of inline. Let's add the isolated image there
-            MessagingGroup messagingGroup = mGroups.get(mGroups.size() -1);
+            MessagingGroup messagingGroup = mGroups.get(mGroups.size() - 1);
             MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
             if (isolatedMessage != null) {
                 newMessage = isolatedMessage.getView();
@@ -1042,7 +1065,7 @@
             }
             if (visibleChildren > 0 && group.getVisibility() == GONE) {
                 group.setVisibility(VISIBLE);
-            } else if (visibleChildren == 0 && group.getVisibility() != GONE)   {
+            } else if (visibleChildren == 0 && group.getVisibility() != GONE) {
                 group.setVisibility(GONE);
             }
         }
@@ -1259,7 +1282,7 @@
         public boolean onTouchEvent(MotionEvent event) {
             float x = event.getX();
             float y = event.getY();
-            for (TouchDelegate delegate: mDelegates) {
+            for (TouchDelegate delegate : mDelegates) {
                 event.setLocation(x, y);
                 if (delegate.onTouchEvent(event)) {
                     return true;
diff --git a/core/java/com/android/internal/widget/MessagingData.java b/core/java/com/android/internal/widget/MessagingData.java
new file mode 100644
index 0000000..85b0201
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingData.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.app.Person;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+final class MessagingData {
+    private final Person mUser;
+    private final boolean mShowSpinner;
+    private final List<MessagingMessage> mHistoricMessagingMessages;
+    private final List<MessagingMessage> mNewMessagingMessages;
+    private final int mUnreadCount;
+
+    MessagingData(Person user, boolean showSpinner,
+            List<MessagingMessage> historicMessagingMessages,
+            List<MessagingMessage> newMessagingMessages) {
+        this(user, showSpinner, /* unreadCount= */0,
+                historicMessagingMessages, newMessagingMessages);
+    }
+
+    MessagingData(Person user, boolean showSpinner,
+            int unreadCount,
+            List<MessagingMessage> historicMessagingMessages,
+            List<MessagingMessage> newMessagingMessages) {
+        mUser = user;
+        mShowSpinner = showSpinner;
+        mUnreadCount = unreadCount;
+        mHistoricMessagingMessages = historicMessagingMessages;
+        mNewMessagingMessages = newMessagingMessages;
+    }
+
+    public Person getUser() {
+        return mUser;
+    }
+
+    public boolean getShowSpinner() {
+        return mShowSpinner;
+    }
+
+    public List<MessagingMessage> getHistoricMessagingMessages() {
+        return mHistoricMessagingMessages;
+    }
+
+    public List<MessagingMessage> getNewMessagingMessages() {
+        return mNewMessagingMessages;
+    }
+
+    public int getUnreadCount() {
+        return mUnreadCount;
+    }
+}
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 83557cd..b6d7503 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -87,7 +87,7 @@
     private ImageResolver mImageResolver;
     private CharSequence mConversationTitle;
     private ArrayList<MessagingLinearLayout.MessagingChild> mToRecycle = new ArrayList<>();
-
+    private boolean mPrecomputedTextEnabled = false;
     public MessagingLayout(@NonNull Context context) {
         super(context);
     }
@@ -162,15 +162,23 @@
      */
     @RemotableViewMethod(asyncImpl = "setDataAsync")
     public void setData(Bundle extras) {
+        bind(parseMessagingData(extras, /* usePrecomputedText= */false));
+    }
+
+    @NonNull
+    private MessagingData parseMessagingData(Bundle extras, boolean usePrecomputedText) {
         Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
-        List<Notification.MessagingStyle.Message> newMessages
-                = Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
+        List<Notification.MessagingStyle.Message> newMessages =
+                Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
         Parcelable[] histMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
-        List<Notification.MessagingStyle.Message> newHistoricMessages
-                = Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages);
-        setUser(extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON, android.app.Person.class));
-        RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[])
-                extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, android.app.RemoteInputHistoryItem.class);
+        List<Notification.MessagingStyle.Message> newHistoricMessages =
+                Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages);
+        setUser(extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON,
+                Person.class));
+        RemoteInputHistoryItem[] history =
+                (RemoteInputHistoryItem[]) extras.getParcelableArray(
+                        Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS,
+                        RemoteInputHistoryItem.class);
         addRemoteInputHistoryToMessages(newMessages, history);
 
         final Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON, Person.class);
@@ -178,10 +186,12 @@
                 extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
 
         final List<MessagingMessage> historicMessagingMessages = createMessages(newHistoricMessages,
-                /* isHistoric= */true, /* usePrecomputedText= */ false);
+                /* isHistoric= */true, usePrecomputedText);
         final List<MessagingMessage> newMessagingMessages =
-                createMessages(newMessages, /* isHistoric= */false, /* usePrecomputedText= */false);
-        bindViews(user, showSpinner, historicMessagingMessages, newMessagingMessages);
+                createMessages(newMessages, /* isHistoric */false, usePrecomputedText);
+
+        return new MessagingData(user, showSpinner,
+                historicMessagingMessages, newMessagingMessages);
     }
 
     /**
@@ -193,7 +203,32 @@
      */
     @NonNull
     public Runnable setDataAsync(Bundle extras) {
-        return () -> setData(extras);
+        if (!mPrecomputedTextEnabled) {
+            return () -> setData(extras);
+        }
+
+        final MessagingData messagingData =
+                parseMessagingData(extras, /* usePrecomputedText= */true);
+
+        return () -> {
+            finalizeInflate(messagingData.getHistoricMessagingMessages());
+            finalizeInflate(messagingData.getNewMessagingMessages());
+            bind(messagingData);
+        };
+    }
+
+    /**
+     * enable/disable precomputed text usage
+     * @hide
+     */
+    public void setPrecomputedTextEnabled(boolean precomputedTextEnabled) {
+        mPrecomputedTextEnabled = precomputedTextEnabled;
+    }
+
+    private void finalizeInflate(List<MessagingMessage> historicMessagingMessages) {
+        for (MessagingMessage messagingMessage: historicMessagingMessages) {
+            messagingMessage.finalizeInflate();
+        }
     }
 
     @Override
@@ -218,17 +253,13 @@
         }
     }
 
-    private void bindViews(Person user, boolean showSpinner,
-            List<MessagingMessage> historicMessagingMessages,
-            List<MessagingMessage> newMessagingMessages) {
-        setUser(user);
-        bind(showSpinner, historicMessagingMessages, newMessagingMessages);
-    }
+    private void bind(MessagingData messagingData) {
+        setUser(messagingData.getUser());
 
-    private void bind(boolean showSpinner, List<MessagingMessage> historicMessages,
-            List<MessagingMessage> messages) {
+        List<MessagingMessage> historicMessages = messagingData.getHistoricMessagingMessages();
+        List<MessagingMessage> messages = messagingData.getNewMessagingMessages();
         ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups);
-        addMessagesToGroups(historicMessages, messages, showSpinner);
+        addMessagesToGroups(historicMessages, messages, messagingData.getShowSpinner());
 
         // Let's first check which groups were removed altogether and remove them in one animation
         removeGroups(oldGroups);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
index b002330..c276827 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
@@ -20,7 +20,9 @@
 import android.util.AttributeSet
 import android.view.View
 import android.widget.TextView
+import com.android.internal.widget.ConversationLayout
 import com.android.internal.widget.ImageFloatingTextView
+import com.android.internal.widget.MessagingLayout
 import javax.inject.Inject
 
 class PrecomputedTextViewFactory @Inject constructor() : NotifRemoteViewsFactory {
@@ -35,6 +37,10 @@
             TextView::class.java.simpleName -> PrecomputedTextView(context, attrs)
             ImageFloatingTextView::class.java.name ->
                 PrecomputedImageFloatingTextView(context, attrs)
+            MessagingLayout::class.java.name ->
+                MessagingLayout(context, attrs).apply { setPrecomputedTextEnabled(true) }
+            ConversationLayout::class.java.name ->
+                ConversationLayout(context, attrs).apply { setPrecomputedTextEnabled(true) }
             else -> null
         }
     }