Add "Sensitive Content" public view

This view, used when sensitive content is detected, and the standard
public view does not apply, keeps the notificaiton title and icon, but
replaces the notification content.

Test: atest
Flag: android.app.redact_sensitive_content_notifications_on_lockscreen
NotificationContentInflaterTest,NotificationRowContentBinderImplTest
Bug: 358403414

Change-Id: I8da06736284a3c3d551f2bea0332b4cc32eccf6e
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() {