Merge "Add "Sensitive Content" public view" into main
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index cdc8bc1..a49a66f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -16,12 +16,18 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -36,7 +42,9 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
+import android.app.Person;
 import android.content.Context;
+import android.graphics.drawable.Icon;
 import android.os.AsyncTask;
 import android.os.CancellationSignal;
 import android.os.Handler;
@@ -66,6 +74,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
 import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
@@ -155,8 +164,8 @@
 
     @Test
     public void testIncreasedHeadsUpBeingUsed() {
-        BindParams params = new BindParams();
-        params.usesIncreasedHeadsUpHeight = true;
+        BindParams params = new BindParams(false, false, /* usesIncreasedHeadsUpHeight */ true,
+                REDACTION_TYPE_NONE);
         Notification.Builder builder = spy(mBuilder);
         mNotificationInflater.inflateNotificationViews(
                 mRow.getEntry(),
@@ -166,14 +175,15 @@
                 FLAG_CONTENT_VIEW_ALL,
                 builder,
                 mContext,
+                mContext,
                 mSmartReplyStateInflater);
         verify(builder).createHeadsUpContentView(true);
     }
 
     @Test
     public void testIncreasedHeightBeingUsed() {
-        BindParams params = new BindParams();
-        params.usesIncreasedHeight = true;
+        BindParams params = new BindParams(false, /* usesIncreasedHeight */ true, false,
+                REDACTION_TYPE_NONE);
         Notification.Builder builder = spy(mBuilder);
         mNotificationInflater.inflateNotificationViews(
                 mRow.getEntry(),
@@ -183,6 +193,7 @@
                 FLAG_CONTENT_VIEW_ALL,
                 builder,
                 mContext,
+                mContext,
                 mSmartReplyStateInflater);
         verify(builder).createContentView(true);
     }
@@ -207,7 +218,7 @@
         mRow.getEntry().getSbn().getNotification().contentView
                 = new RemoteViews(mContext.getPackageName(), com.android.systemui.res.R.layout.status_bar);
         inflateAndWait(true /* expectingException */, mNotificationInflater, FLAG_CONTENT_VIEW_ALL,
-                mRow);
+                REDACTION_TYPE_NONE, mRow);
         assertTrue(mRow.getPrivateLayout().getChildCount() == 0);
         verify(mRow, times(0)).onNotificationUpdated();
     }
@@ -227,7 +238,7 @@
                 mRow.getEntry(),
                 mRow,
                 FLAG_CONTENT_VIEW_ALL,
-                new BindParams(),
+                new BindParams(false, false, false, REDACTION_TYPE_NONE),
                 false /* forceInflate */,
                 null /* callback */);
         Assert.assertNull(mRow.getEntry().getRunningTask());
@@ -287,7 +298,7 @@
         mBuilder.setCustomContentView(new RemoteViews(getContext().getPackageName(),
                 R.layout.custom_view_dark));
         RemoteViews decoratedMediaView = mBuilder.createContentView();
-        Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!",
+        assertFalse("The decorated media style doesn't allow a view to be reapplied!",
                 NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
     }
 
@@ -385,7 +396,8 @@
         mRow.getPrivateLayout().removeAllViews();
         mRow.getEntry().getSbn().getNotification().contentView =
                 new RemoteViews(mContext.getPackageName(), R.layout.invalid_notification_height);
-        inflateAndWait(true, mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
+        inflateAndWait(true, mNotificationInflater, FLAG_CONTENT_VIEW_ALL, REDACTION_TYPE_NONE,
+                mRow);
         assertEquals(0, mRow.getPrivateLayout().getChildCount());
         verify(mRow, times(0)).onNotificationUpdated();
     }
@@ -455,16 +467,88 @@
         assertNull(mRow.getEntry().getPromotedNotificationContentModel());
     }
 
+    @Test
+    @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+    public void testSensitiveContentPublicView_messageStyle() throws Exception {
+        String displayName = "Display Name";
+        String messageText = "Message Text";
+        String contentText = "Content Text";
+        Icon personIcon = Icon.createWithResource(mContext,
+                com.android.systemui.res.R.drawable.ic_person);
+        Person testPerson = new Person.Builder()
+                .setName(displayName)
+                .setIcon(personIcon)
+                .build();
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle(testPerson);
+        messagingStyle.addMessage(new Notification.MessagingStyle.Message(messageText,
+                System.currentTimeMillis(), testPerson));
+        messagingStyle.setConversationType(Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL);
+        messagingStyle.setShortcutIcon(personIcon);
+        Notification messageNotif = new Notification.Builder(mContext).setSmallIcon(
+                com.android.systemui.res.R.drawable.ic_person).setStyle(messagingStyle).build();
+        ExpandableNotificationRow row = mHelper.createRow(messageNotif);
+        inflateAndWait(false, mNotificationInflater, FLAG_CONTENT_VIEW_PUBLIC,
+                REDACTION_TYPE_SENSITIVE_CONTENT, row);
+        NotificationContentView publicView = row.getPublicLayout();
+        assertNotNull(publicView);
+        // The display name should be included, but not the content or message text
+        assertFalse(hasText(publicView, messageText));
+        assertFalse(hasText(publicView, contentText));
+        assertTrue(hasText(publicView, displayName));
+    }
+
+    @Test
+    @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+    public void testSensitiveContentPublicView_nonMessageStyle() throws Exception {
+        String contentTitle = "Content Title";
+        String contentText = "Content Text";
+        Notification notif = new Notification.Builder(mContext).setSmallIcon(
+                com.android.systemui.res.R.drawable.ic_person)
+                .setContentTitle(contentTitle)
+                .setContentText(contentText)
+                .build();
+        ExpandableNotificationRow row = mHelper.createRow(notif);
+        inflateAndWait(false, mNotificationInflater, FLAG_CONTENT_VIEW_PUBLIC,
+                REDACTION_TYPE_SENSITIVE_CONTENT, row);
+        NotificationContentView publicView = row.getPublicLayout();
+        assertNotNull(publicView);
+        assertFalse(hasText(publicView, contentText));
+        assertTrue(hasText(publicView, contentTitle));
+
+        // The standard public view should not use the content title or text
+        inflateAndWait(false, mNotificationInflater, FLAG_CONTENT_VIEW_PUBLIC,
+                REDACTION_TYPE_PUBLIC, row);
+        publicView = row.getPublicLayout();
+        assertFalse(hasText(publicView, contentText));
+        assertFalse(hasText(publicView, contentTitle));
+    }
+
+    private static boolean hasText(ViewGroup parent, CharSequence text) {
+        for (int i = 0; i < parent.getChildCount(); i++) {
+            View child = parent.getChildAt(i);
+            if (child instanceof ViewGroup) {
+                if (hasText((ViewGroup) child, text)) {
+                    return true;
+                }
+            } else if (child instanceof TextView) {
+                return ((TextView) child).getText().toString().contains(text);
+            }
+        }
+        return false;
+    }
+
     private static void inflateAndWait(NotificationContentInflater inflater,
             @InflationFlag int contentToInflate,
             ExpandableNotificationRow row)
             throws Exception {
-        inflateAndWait(false /* expectingException */, inflater, contentToInflate, row);
+        inflateAndWait(false /* expectingException */, inflater, contentToInflate,
+                REDACTION_TYPE_NONE, row);
     }
 
     private static void inflateAndWait(boolean expectingException,
             NotificationContentInflater inflater,
             @InflationFlag int contentToInflate,
+            @RedactionType int redactionType,
             ExpandableNotificationRow row) throws Exception {
         CountDownLatch countDownLatch = new CountDownLatch(1);
         final ExceptionHolder exceptionHolder = new ExceptionHolder();
@@ -492,7 +576,7 @@
                 row.getEntry(),
                 row,
                 contentToInflate,
-                new BindParams(),
+                new BindParams(false, false, false, redactionType),
                 false /* forceInflate */,
                 callback /* callback */);
         assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index 9fb72fb..2aeebe3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -18,6 +18,7 @@
 import android.app.Notification
 import android.app.Person
 import android.content.Context
+import android.graphics.drawable.Icon
 import android.os.AsyncTask
 import android.os.Build
 import android.os.CancellationSignal
@@ -34,6 +35,10 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -45,6 +50,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
@@ -138,14 +144,14 @@
 
     @Test
     fun testIncreasedHeadsUpBeingUsed() {
-        val params = BindParams()
-        params.usesIncreasedHeadsUpHeight = true
+        val params =
+            BindParams(false, false, /* usesIncreasedHeadsUpHeight */ true, REDACTION_TYPE_NONE)
         val builder = spy(builder)
         notificationInflater.inflateNotificationViews(
             row.entry,
             row,
             params,
-            true /* inflateSynchronously */,
+            true, /* inflateSynchronously */
             FLAG_CONTENT_VIEW_ALL,
             builder,
             mContext,
@@ -157,14 +163,13 @@
 
     @Test
     fun testIncreasedHeightBeingUsed() {
-        val params = BindParams()
-        params.usesIncreasedHeight = true
+        val params = BindParams(false, /* usesIncreasedHeight */ true, false, REDACTION_TYPE_NONE)
         val builder = spy(builder)
         notificationInflater.inflateNotificationViews(
             row.entry,
             row,
             params,
-            true /* inflateSynchronously */,
+            true, /* inflateSynchronously */
             FLAG_CONTENT_VIEW_ALL,
             builder,
             mContext,
@@ -193,15 +198,18 @@
         row.entry.sbn.notification.contentView =
             RemoteViews(mContext.packageName, R.layout.status_bar)
         inflateAndWait(
-            true /* expectingException */,
+            true, /* expectingException */
             notificationInflater,
             FLAG_CONTENT_VIEW_ALL,
+            REDACTION_TYPE_NONE,
             row,
         )
         Assert.assertTrue(row.privateLayout.childCount == 0)
         verify(row, times(0)).onNotificationUpdated()
     }
 
+    @Test fun testInflationOfSensitiveContentPublicView() {}
+
     @Test
     fun testAsyncTaskRemoved() {
         row.entry.abortTask()
@@ -217,8 +225,8 @@
             row.entry,
             row,
             FLAG_CONTENT_VIEW_ALL,
-            BindParams(),
-            false /* forceInflate */,
+            BindParams(false, false, false, REDACTION_TYPE_NONE),
+            false, /* forceInflate */
             null, /* callback */
         )
         Assert.assertNull(row.entry.runningTask)
@@ -431,7 +439,7 @@
                 mContext.packageName,
                 com.android.systemui.tests.R.layout.invalid_notification_height,
             )
-        inflateAndWait(true, notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
+        inflateAndWait(true, notificationInflater, FLAG_CONTENT_VIEW_ALL, REDACTION_TYPE_NONE, row)
         Assert.assertEquals(0, row.privateLayout.childCount.toLong())
         verify(row, times(0)).onNotificationUpdated()
     }
@@ -440,7 +448,13 @@
     @Test
     fun testInflatePublicSingleLineView() {
         row.publicLayout.removeAllViews()
-        inflateAndWait(false, notificationInflater, FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, row)
+        inflateAndWait(
+            false,
+            notificationInflater,
+            FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE,
+            REDACTION_TYPE_NONE,
+            row,
+        )
         Assert.assertNotNull(row.publicLayout.mSingleLineView)
         Assert.assertTrue(row.publicLayout.mSingleLineView is HybridNotificationView)
     }
@@ -461,6 +475,7 @@
             false,
             notificationInflater,
             FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE,
+            REDACTION_TYPE_NONE,
             messagingRow,
         )
         Assert.assertNotNull(messagingRow.publicLayout.mSingleLineView)
@@ -530,6 +545,80 @@
         Assert.assertNull(row.entry.promotedNotificationContentModel)
     }
 
+    @Test
+    @Throws(java.lang.Exception::class)
+    @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+    fun testSensitiveContentPublicView_messageStyle() {
+        val displayName = "Display Name"
+        val messageText = "Message Text"
+        val contentText = "Content Text"
+        val personIcon = Icon.createWithResource(mContext, R.drawable.ic_person)
+        val testPerson = Person.Builder().setName(displayName).setIcon(personIcon).build()
+        val messagingStyle = Notification.MessagingStyle(testPerson)
+        messagingStyle.addMessage(
+            Notification.MessagingStyle.Message(messageText, System.currentTimeMillis(), testPerson)
+        )
+        messagingStyle.setConversationType(Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL)
+        messagingStyle.setShortcutIcon(personIcon)
+        val messageNotif =
+            Notification.Builder(mContext)
+                .setSmallIcon(R.drawable.ic_person)
+                .setStyle(messagingStyle)
+                .build()
+        val newRow: ExpandableNotificationRow = testHelper.createRow(messageNotif)
+        inflateAndWait(
+            false,
+            notificationInflater,
+            FLAG_CONTENT_VIEW_PUBLIC,
+            REDACTION_TYPE_SENSITIVE_CONTENT,
+            newRow,
+        )
+        // The display name should be included, but not the content or message text
+        val publicView = newRow.publicLayout
+        Assert.assertNotNull(publicView)
+        Assert.assertFalse(hasText(publicView, messageText))
+        Assert.assertFalse(hasText(publicView, contentText))
+        Assert.assertTrue(hasText(publicView, displayName))
+    }
+
+    @Test
+    @Throws(java.lang.Exception::class)
+    @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+    fun testSensitiveContentPublicView_nonMessageStyle() {
+        val contentTitle = "Content Title"
+        val contentText = "Content Text"
+        val notif =
+            Notification.Builder(mContext)
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentTitle(contentTitle)
+                .setContentText(contentText)
+                .build()
+        val newRow: ExpandableNotificationRow = testHelper.createRow(notif)
+        inflateAndWait(
+            false,
+            notificationInflater,
+            FLAG_CONTENT_VIEW_PUBLIC,
+            REDACTION_TYPE_SENSITIVE_CONTENT,
+            newRow,
+        )
+        var publicView = newRow.publicLayout
+        Assert.assertNotNull(publicView)
+        Assert.assertFalse(hasText(publicView, contentText))
+        Assert.assertTrue(hasText(publicView, contentTitle))
+
+        // The standard public view should not use the content title or text
+        inflateAndWait(
+            false,
+            notificationInflater,
+            FLAG_CONTENT_VIEW_PUBLIC,
+            REDACTION_TYPE_PUBLIC,
+            newRow,
+        )
+        publicView = newRow.publicLayout
+        Assert.assertFalse(hasText(publicView, contentText))
+        Assert.assertFalse(hasText(publicView, contentTitle))
+    }
+
     private class ExceptionHolder {
         var exception: Exception? = null
     }
@@ -568,13 +657,20 @@
             @InflationFlag contentToInflate: Int,
             row: ExpandableNotificationRow,
         ) {
-            inflateAndWait(false /* expectingException */, inflater, contentToInflate, row)
+            inflateAndWait(
+                false /* expectingException */,
+                inflater,
+                contentToInflate,
+                REDACTION_TYPE_NONE,
+                row,
+            )
         }
 
         private fun inflateAndWait(
             expectingException: Boolean,
             inflater: NotificationRowContentBinderImpl,
             @InflationFlag contentToInflate: Int,
+            @RedactionType redactionType: Int,
             row: ExpandableNotificationRow,
         ) {
             val countDownLatch = CountDownLatch(1)
@@ -603,12 +699,26 @@
                 row.entry,
                 row,
                 contentToInflate,
-                BindParams(),
-                false /* forceInflate */,
+                BindParams(false, false, false, redactionType),
+                false, /* forceInflate */
                 callback, /* callback */
             )
             Assert.assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS))
             exceptionHolder.exception?.let { throw it }
         }
+
+        fun hasText(parent: ViewGroup, text: CharSequence): Boolean {
+            for (i in 0 until parent.childCount) {
+                val child = parent.getChildAt(i)
+                if (child is ViewGroup) {
+                    if (hasText(child, text)) {
+                        return true
+                    }
+                } else if (child is TextView) {
+                    return child.text.toString().contains(text)
+                }
+            }
+            return false
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 80e8f55..d83acf3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.collection.inflation;
 
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -186,6 +187,9 @@
         params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
         if (AsyncHybridViewInflation.isEnabled()) {
             params.markContentViewsFreeable(FLAG_CONTENT_VIEW_SINGLE_LINE);
+            if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
+                params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE);
+            }
         }
         mRowContentBindStage.requestRebind(entry, null);
     }
@@ -256,10 +260,10 @@
         params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED);
         params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
         params.setUseMinimized(isMinimized);
-        // TODO b/358403414: use the different types of redaction
-        boolean needsRedaction = inflaterParams.getRedactionType() != REDACTION_TYPE_NONE;
+        int redactionType = inflaterParams.getRedactionType();
 
-        if (needsRedaction) {
+        params.setRedactionType(redactionType);
+        if (redactionType != REDACTION_TYPE_NONE) {
             params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
         } else {
             params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
@@ -276,8 +280,8 @@
         }
 
         if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
-
-            if (inflaterParams.isChildInGroup() && needsRedaction) {
+            if (inflaterParams.isChildInGroup()
+                    && redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
                 params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE);
             } else {
                 params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 6eeb80d..878a4aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
@@ -25,6 +26,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
+import android.app.Notification.MessagingStyle;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.ApplicationInfo;
@@ -161,9 +163,7 @@
                 entry,
                 mConversationProcessor,
                 row,
-                bindParams.isMinimized,
-                bindParams.usesIncreasedHeight,
-                bindParams.usesIncreasedHeadsUpHeight,
+                bindParams,
                 callback,
                 mRemoteInputManager.getRemoteViewsOnClickHandler(),
                 /* isMediaFlagEnabled = */ mIsMediaInQS,
@@ -187,13 +187,13 @@
             boolean inflateSynchronously,
             @InflationFlag int reInflateFlags,
             Notification.Builder builder,
+            Context systemUiContext,
             Context packageContext,
             SmartReplyStateInflater smartRepliesInflater) {
         InflationProgress result = createRemoteViews(reInflateFlags,
                 builder,
-                bindParams.isMinimized,
-                bindParams.usesIncreasedHeight,
-                bindParams.usesIncreasedHeadsUpHeight,
+                bindParams,
+                systemUiContext,
                 packageContext,
                 row,
                 mNotifLayoutInflaterFactoryProvider,
@@ -411,8 +411,8 @@
     }
 
     private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
-            Notification.Builder builder, boolean isMinimized, boolean usesIncreasedHeight,
-            boolean usesIncreasedHeadsUpHeight, Context packageContext,
+            Notification.Builder builder, BindParams bindParams, Context systemUiContext,
+            Context packageContext,
             ExpandableNotificationRow row,
             NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider,
             HeadsUpStyleProvider headsUpStyleProvider,
@@ -423,13 +423,13 @@
 
             if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
                 logger.logAsyncTaskProgress(entryForLogging, "creating contracted remote view");
-                result.newContentView = createContentView(builder, isMinimized,
-                        usesIncreasedHeight);
+                result.newContentView = createContentView(builder, bindParams.isMinimized,
+                        bindParams.usesIncreasedHeight);
             }
 
             if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
                 logger.logAsyncTaskProgress(entryForLogging, "creating expanded remote view");
-                result.newExpandedView = createExpandedView(builder, isMinimized);
+                result.newExpandedView = createExpandedView(builder, bindParams.isMinimized);
             }
 
             if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
@@ -439,13 +439,20 @@
                     result.newHeadsUpView = builder.createCompactHeadsUpContentView();
                 } else {
                     result.newHeadsUpView = builder.createHeadsUpContentView(
-                            usesIncreasedHeadsUpHeight);
+                            bindParams.usesIncreasedHeadsUpHeight);
                 }
             }
 
             if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
                 logger.logAsyncTaskProgress(entryForLogging, "creating public remote view");
-                result.newPublicView = builder.makePublicContentView(isMinimized);
+                if (LockscreenOtpRedaction.isEnabled()
+                        && bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+                    result.newPublicView = createSensitiveContentMessageNotification(
+                            row.getEntry().getSbn().getNotification(), builder.getStyle(),
+                            systemUiContext, packageContext).createContentView(true);
+                } else {
+                    result.newPublicView = builder.makePublicContentView(bindParams.isMinimized);
+                }
             }
 
             if (AsyncGroupHeaderViewInflation.isEnabled()) {
@@ -473,6 +480,42 @@
         });
     }
 
+    private static Notification.Builder createSensitiveContentMessageNotification(
+            Notification original,
+            Notification.Style originalStyle,
+            Context systemUiContext,
+            Context packageContext) {
+        Notification.Builder redacted =
+                new Notification.Builder(packageContext, original.getChannelId());
+        redacted.setContentTitle(original.extras.getCharSequence(Notification.EXTRA_TITLE));
+        CharSequence redactedMessage = systemUiContext.getString(
+                R.string.redacted_notification_single_line_text
+        );
+
+        if (originalStyle instanceof MessagingStyle oldStyle) {
+            MessagingStyle newStyle = new MessagingStyle(oldStyle.getUser());
+            newStyle.setConversationTitle(oldStyle.getConversationTitle());
+            newStyle.setGroupConversation(false);
+            newStyle.setConversationType(oldStyle.getConversationType());
+            newStyle.setShortcutIcon(oldStyle.getShortcutIcon());
+            newStyle.setBuilder(redacted);
+            MessagingStyle.Message latestMessage =
+                    MessagingStyle.findLatestIncomingMessage(oldStyle.getMessages());
+            if (latestMessage != null) {
+                MessagingStyle.Message newMessage = new MessagingStyle.Message(redactedMessage,
+                        latestMessage.getTimestamp(), latestMessage.getSenderPerson());
+                newStyle.addMessage(newMessage);
+            }
+            redacted.setStyle(newStyle);
+        } else {
+            redacted.setContentText(redactedMessage);
+        }
+        redacted.setLargeIcon(original.getLargeIcon());
+        redacted.setSmallIcon(original.getSmallIcon());
+        return redacted;
+    }
+
+
     private static void setNotifsViewsInflaterFactory(InflationProgress result,
             ExpandableNotificationRow row,
             NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
@@ -1118,10 +1161,8 @@
         private final NotificationEntry mEntry;
         private final Context mContext;
         private final boolean mInflateSynchronously;
-        private final boolean mIsMinimized;
-        private final boolean mUsesIncreasedHeight;
+        private final BindParams mBindParams;
         private final InflationCallback mCallback;
-        private final boolean mUsesIncreasedHeadsUpHeight;
         private final @InflationFlag int mReInflateFlags;
         private final NotifRemoteViewCache mRemoteViewCache;
         private final Executor mInflationExecutor;
@@ -1145,9 +1186,7 @@
                 NotificationEntry entry,
                 ConversationNotificationProcessor conversationProcessor,
                 ExpandableNotificationRow row,
-                boolean isMinimized,
-                boolean usesIncreasedHeight,
-                boolean usesIncreasedHeadsUpHeight,
+                BindParams bindParams,
                 InflationCallback callback,
                 RemoteViews.InteractionHandler remoteViewClickHandler,
                 boolean isMediaFlagEnabled,
@@ -1164,9 +1203,7 @@
             mRemoteViewCache = cache;
             mSmartRepliesInflater = smartRepliesInflater;
             mContext = mRow.getContext();
-            mIsMinimized = isMinimized;
-            mUsesIncreasedHeight = usesIncreasedHeight;
-            mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
+            mBindParams = bindParams;
             mRemoteViewClickHandler = remoteViewClickHandler;
             mCallback = callback;
             mConversationProcessor = conversationProcessor;
@@ -1236,8 +1273,7 @@
                         mEntry, recoveredBuilder, mLogger);
             }
             InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
-                    recoveredBuilder, mIsMinimized, mUsesIncreasedHeight,
-                    mUsesIncreasedHeadsUpHeight, packageContext, mRow,
+                    recoveredBuilder, mBindParams, mContext, packageContext, mRow,
                     mNotifLayoutInflaterFactoryProvider, mHeadsUpStyleProvider, mLogger);
 
             mLogger.logAsyncTaskProgress(mEntry,
@@ -1320,7 +1356,7 @@
                 mCancellationSignal = apply(
                         mInflationExecutor,
                         mInflateSynchronously,
-                        mIsMinimized,
+                        mBindParams.isMinimized,
                         result,
                         mReInflateFlags,
                         mRemoteViewCache,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
index 07384af..1cef879 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -141,20 +143,33 @@
      */
     class BindParams {
 
+        public BindParams(boolean minimized, boolean increasedHeight,
+                boolean increasedHeadsUpHeight, int redaction) {
+            isMinimized = minimized;
+            usesIncreasedHeight = increasedHeight;
+            usesIncreasedHeadsUpHeight = increasedHeadsUpHeight;
+            redactionType = redaction;
+        }
+
         /**
          * Bind a minimized version of the content views.
          */
-        public boolean isMinimized;
+        public final boolean isMinimized;
 
         /**
          * Use increased height when binding contracted view.
          */
-        public boolean usesIncreasedHeight;
+        public final boolean usesIncreasedHeight;
 
         /**
          * Use increased height when binding heads up views.
          */
-        public boolean usesIncreasedHeadsUpHeight;
+        public final boolean usesIncreasedHeadsUpHeight;
+
+        /**
+         * Controls the type of public view to show, if a public view is requested
+         */
+        public final @RedactionType int redactionType;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index 7dcb2de..e4e1398 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -17,6 +17,7 @@
 
 import android.annotation.SuppressLint
 import android.app.Notification
+import android.app.Notification.MessagingStyle
 import android.content.Context
 import android.content.ContextWrapper
 import android.content.pm.ApplicationInfo
@@ -42,6 +43,7 @@
 import com.android.systemui.dagger.qualifiers.NotifInflation
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.InflationTask
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT
 import com.android.systemui.statusbar.NotificationRemoteInputManager
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
 import com.android.systemui.statusbar.notification.InflationException
@@ -142,9 +144,7 @@
                 entry,
                 conversationProcessor,
                 row,
-                bindParams.isMinimized,
-                bindParams.usesIncreasedHeight,
-                bindParams.usesIncreasedHeadsUpHeight,
+                bindParams,
                 callback,
                 remoteInputManager.remoteViewsOnClickHandler,
                 /* isMediaFlagEnabled = */ smartReplyStateInflater,
@@ -178,10 +178,8 @@
                 reInflateFlags = reInflateFlags,
                 entry = entry,
                 builder = builder,
-                isMinimized = bindParams.isMinimized,
-                usesIncreasedHeight = bindParams.usesIncreasedHeight,
-                usesIncreasedHeadsUpHeight = bindParams.usesIncreasedHeadsUpHeight,
-                systemUIContext = systemUIContext,
+                bindParams,
+                systemUiContext = systemUIContext,
                 packageContext = packageContext,
                 row = row,
                 notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
@@ -370,9 +368,7 @@
         private val entry: NotificationEntry,
         private val conversationProcessor: ConversationNotificationProcessor,
         private val row: ExpandableNotificationRow,
-        private val isMinimized: Boolean,
-        private val usesIncreasedHeight: Boolean,
-        private val usesIncreasedHeadsUpHeight: Boolean,
+        private val bindParams: BindParams,
         private val callback: InflationCallback?,
         private val remoteViewClickHandler: InteractionHandler?,
         private val smartRepliesInflater: SmartReplyStateInflater,
@@ -440,10 +436,8 @@
                     reInflateFlags = reInflateFlags,
                     entry = entry,
                     builder = recoveredBuilder,
-                    isMinimized = isMinimized,
-                    usesIncreasedHeight = usesIncreasedHeight,
-                    usesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight,
-                    systemUIContext = context,
+                    bindParams = bindParams,
+                    systemUiContext = context,
                     packageContext = packageContext,
                     row = row,
                     notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
@@ -513,7 +507,7 @@
                         apply(
                             inflationExecutor,
                             inflateSynchronously,
-                            isMinimized,
+                            bindParams.isMinimized,
                             progress,
                             reInflateFlags,
                             remoteViewCache,
@@ -670,10 +664,8 @@
             @InflationFlag reInflateFlags: Int,
             entry: NotificationEntry,
             builder: Notification.Builder,
-            isMinimized: Boolean,
-            usesIncreasedHeight: Boolean,
-            usesIncreasedHeadsUpHeight: Boolean,
-            systemUIContext: Context,
+            bindParams: BindParams,
+            systemUiContext: Context,
             packageContext: Context,
             row: ExpandableNotificationRow,
             notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
@@ -705,9 +697,10 @@
                 createRemoteViews(
                     reInflateFlags = reInflateFlags,
                     builder = builder,
-                    isMinimized = isMinimized,
-                    usesIncreasedHeight = usesIncreasedHeight,
-                    usesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight,
+                    bindParams = bindParams,
+                    entry = entry,
+                    systemUiContext = systemUiContext,
+                    packageContext = packageContext,
                     row = row,
                     notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
                     headsUpStyleProvider = headsUpStyleProvider,
@@ -724,7 +717,7 @@
                         notification = entry.sbn.notification,
                         messagingStyle = messagingStyle,
                         builder = builder,
-                        systemUiContext = systemUIContext,
+                        systemUiContext = systemUiContext,
                     )
                 } else null
 
@@ -735,7 +728,7 @@
                 ) {
                     logger.logAsyncTaskProgress(entry, "inflating public single line view model")
                     SingleLineViewInflater.inflateRedactedSingleLineViewModel(
-                        systemUIContext,
+                        systemUiContext,
                         entry.ranking.isConversation,
                     )
                 } else null
@@ -761,12 +754,50 @@
             )
         }
 
+        private fun createSensitiveContentMessageNotification(
+            original: Notification,
+            originalStyle: Notification.Style?,
+            sysUiContext: Context,
+            packageContext: Context,
+        ): Notification.Builder {
+            val redacted = Notification.Builder(packageContext, original.channelId)
+            redacted.setContentTitle(original.extras.getCharSequence(Notification.EXTRA_TITLE))
+            val redactedMessage =
+                sysUiContext.getString(R.string.redacted_notification_single_line_text)
+
+            if (originalStyle is MessagingStyle) {
+                val newStyle = MessagingStyle(originalStyle.user)
+                newStyle.conversationTitle = originalStyle.conversationTitle
+                newStyle.isGroupConversation = false
+                newStyle.conversationType = originalStyle.conversationType
+                newStyle.shortcutIcon = originalStyle.shortcutIcon
+                newStyle.setBuilder(redacted)
+                val latestMessage = MessagingStyle.findLatestIncomingMessage(originalStyle.messages)
+                if (latestMessage != null) {
+                    val newMessage =
+                        MessagingStyle.Message(
+                            redactedMessage,
+                            latestMessage.timestamp,
+                            latestMessage.senderPerson,
+                        )
+                    newStyle.addMessage(newMessage)
+                }
+                redacted.style = newStyle
+            } else {
+                redacted.setContentText(redactedMessage)
+            }
+            redacted.setLargeIcon(original.getLargeIcon())
+            redacted.setSmallIcon(original.smallIcon)
+            return redacted
+        }
+
         private fun createRemoteViews(
             @InflationFlag reInflateFlags: Int,
             builder: Notification.Builder,
-            isMinimized: Boolean,
-            usesIncreasedHeight: Boolean,
-            usesIncreasedHeadsUpHeight: Boolean,
+            bindParams: BindParams,
+            entry: NotificationEntry,
+            systemUiContext: Context,
+            packageContext: Context,
             row: ExpandableNotificationRow,
             notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
             headsUpStyleProvider: HeadsUpStyleProvider,
@@ -780,7 +811,11 @@
                             entryForLogging,
                             "creating contracted remote view",
                         )
-                        createContentView(builder, isMinimized, usesIncreasedHeight)
+                        createContentView(
+                            builder,
+                            bindParams.isMinimized,
+                            bindParams.usesIncreasedHeight,
+                        )
                     } else null
                 val expanded =
                     if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) {
@@ -788,7 +823,7 @@
                             entryForLogging,
                             "creating expanded remote view",
                         )
-                        createExpandedView(builder, isMinimized)
+                        createExpandedView(builder, bindParams.isMinimized)
                     } else null
                 val headsUp =
                     if (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0) {
@@ -800,13 +835,26 @@
                         if (isHeadsUpCompact) {
                             builder.createCompactHeadsUpContentView()
                         } else {
-                            builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight)
+                            builder.createHeadsUpContentView(bindParams.usesIncreasedHeadsUpHeight)
                         }
                     } else null
                 val public =
                     if (reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC != 0) {
                         logger.logAsyncTaskProgress(entryForLogging, "creating public remote view")
-                        builder.makePublicContentView(isMinimized)
+                        if (
+                            LockscreenOtpRedaction.isEnabled &&
+                                bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT
+                        ) {
+                            createSensitiveContentMessageNotification(
+                                    entry.sbn.notification,
+                                    builder.style,
+                                    systemUiContext,
+                                    packageContext,
+                                )
+                                .createContentView(bindParams.usesIncreasedHeight)
+                        } else {
+                            builder.makePublicContentView(bindParams.isMinimized)
+                        }
                     } else null
                 val normalGroupHeader =
                     if (
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
index 427fb66..bc44cb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
@@ -31,6 +33,7 @@
     private boolean mUseIncreasedHeadsUpHeight;
     private boolean mViewsNeedReinflation;
     private @InflationFlag int mContentViews = DEFAULT_INFLATION_FLAGS;
+    private @RedactionType int mRedactionType = REDACTION_TYPE_NONE;
 
     /**
      * Content views that are out of date and need to be rebound.
@@ -58,6 +61,20 @@
     }
 
     /**
+     * @return What type of redaction should be used by the public view (if requested)
+     */
+    public @RedactionType int getRedactionType() {
+        return mRedactionType;
+    }
+
+    /**
+     * Set the redaction type, which controls what sort of public view is shown.
+     */
+    public void setRedactionType(@RedactionType int redactionType) {
+        mRedactionType = redactionType;
+    }
+
+    /**
      * Set whether content should use an increased height version of its contracted view.
      */
     public void setUseIncreasedCollapsedHeight(boolean useIncreasedHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
index 89fcda9..53f7416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
@@ -72,10 +72,8 @@
         // Bind/unbind with parameters
         mBinder.unbindContent(entry, row, contentToUnbind);
 
-        BindParams bindParams = new BindParams();
-        bindParams.isMinimized = params.useMinimized();
-        bindParams.usesIncreasedHeight = params.useIncreasedHeight();
-        bindParams.usesIncreasedHeadsUpHeight = params.useIncreasedHeadsUpHeight();
+        BindParams bindParams = new BindParams(params.useMinimized(), params.useIncreasedHeight(),
+                params.useIncreasedHeadsUpHeight(), params.getRedactionType());
         boolean forceInflate = params.needsReinflation();
 
         InflationCallback inflationCallback = new InflationCallback() {