Add screen for notification history

Bug: 137396965
Test: manual

(cherry picked from commit 84b593d592a74aeddfce453bc8700180192199ed)

Change-Id: Icac4000bd7323583f8a5f462ccb40826a4151765
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9bb810d..e70d468 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1193,6 +1193,15 @@
                 android:value="com.android.settings.notification.history.NotificationStation" />
         </activity>
 
+        <activity
+            android:name=".notification.history.NotificationHistoryActivity"
+            android:label="@string/notification_history_title">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".notification.zen.ZenModeVoiceActivity"
                 android:theme="@*android:style/Theme.DeviceDefault.Settings.Dialog.NoActionBar"
                 android:label="@string/zen_mode_settings_title">
diff --git a/res/drawable/ic_clear.xml b/res/drawable/ic_clear.xml
new file mode 100644
index 0000000..224425f
--- /dev/null
+++ b/res/drawable/ic_clear.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+    <!--
+        Copyright (C) 2020 The Android Open Source Project
+
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+            http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:width="24dp"
+    android:height="24dp">
+    <path
+        android:pathData="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12Z"
+        android:fillColor="#FFFFFF" />
+</vector>
+
diff --git a/res/drawable/ic_snooze.xml b/res/drawable/ic_snooze.xml
new file mode 100644
index 0000000..d7bbbaf
--- /dev/null
+++ b/res/drawable/ic_snooze.xml
@@ -0,0 +1,24 @@
+<!--
+  Copyright (C) 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M9,11h3.63L9,15.2L9,17h6v-2h-3.63L15,10.8L15,9L9,9v2zM16.056,3.346l1.282,-1.535 4.607,3.85 -1.28,1.54zM3.336,7.19l-1.28,-1.536L6.662,1.81l1.28,1.536zM12,6c3.86,0 7,3.14 7,7s-3.14,7 -7,7 -7,-3.14 -7,-7 3.14,-7 7,-7m0,-2c-4.97,0 -9,4.03 -9,9s4.03,9 9,9 9,-4.03 9,-9 -4.03,-9 -9,-9z"/>
+</vector>
diff --git a/res/drawable/ic_today.xml b/res/drawable/ic_today.xml
new file mode 100644
index 0000000..1d39610
--- /dev/null
+++ b/res/drawable/ic_today.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2020 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19,3h-1L18,1h-2v2L8,3L8,1L6,1v2L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19L5,8h14v11zM9,9.5c-1.38,0 -2.5,1.12 -2.5,2.5s1.12,2.5 2.5,2.5 2.5,-1.12 2.5,-2.5S10.38,9.5 9,9.5z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/rounded_bg.xml b/res/drawable/rounded_bg.xml
new file mode 100644
index 0000000..fcdd761
--- /dev/null
+++ b/res/drawable/rounded_bg.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <corners
+        android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+        android:topLeftRadius="?android:attr/dialogCornerRadius"
+        android:bottomRightRadius="?android:attr/dialogCornerRadius"
+        android:topRightRadius="?android:attr/dialogCornerRadius"
+    />
+</shape>
\ No newline at end of file
diff --git a/res/layout/notification_history.xml b/res/layout/notification_history.xml
new file mode 100644
index 0000000..91a59a8
--- /dev/null
+++ b/res/layout/notification_history.xml
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/scroll"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="16dp"
+    android:background="@*android:color/material_grey_50">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <!-- TODO: header switch -->
+        <LinearLayout
+            android:id="@+id/snoozed_list"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_marginBottom="10dp">
+            <LinearLayout
+                android:id="@+id/snooze_header"
+                android:layout_height="48dp"
+                android:layout_width="match_parent"
+                android:gravity="center_vertical"
+                android:orientation="horizontal">
+                <ImageView
+                    android:src="@drawable/ic_snooze"
+                    android:tint="?android:attr/textColorPrimary"
+                    android:padding="6dp"
+                    android:layout_height="36dp"
+                    android:layout_width="36dp" />
+                <TextView
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:text="@string/notification_history_snooze"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:paddingStart="@dimen/notification_history_header_drawable_start" />
+            </LinearLayout>
+            <FrameLayout
+                android:id="@+id/list_container"
+                android:layout_width="wrap_content"
+                android:layout_height="300dp"
+                android:clipChildren="true"
+                android:elevation="3dp"
+                android:background="@drawable/rounded_bg">
+
+                <androidx.recyclerview.widget.RecyclerView
+                    android:id="@+id/notification_list"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:clipChildren="true"
+                    settings:fastScrollEnabled="true"
+                    settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
+                    settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
+                    settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
+                    settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/>
+
+            </FrameLayout>
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/recently_dismissed_list"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_marginBottom="10dp">
+            <LinearLayout
+                android:id="@+id/dismissed_header"
+                android:layout_height="48dp"
+                android:layout_width="match_parent"
+                android:gravity="center_vertical"
+                android:orientation="horizontal">
+                <ImageView
+                    android:src="@drawable/ic_clear"
+                    android:tint="?android:attr/textColorPrimary"
+                    android:padding="6dp"
+                    android:layout_height="36dp"
+                    android:layout_width="36dp" />
+                <TextView
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:text="@string/notification_history_dismiss"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:paddingStart="@dimen/notification_history_header_drawable_start" />
+            </LinearLayout>
+            <FrameLayout
+                android:id="@+id/list_container"
+                android:layout_width="wrap_content"
+                android:layout_height="300dp"
+                android:elevation="3dp"
+                android:clipChildren="true"
+                android:background="@drawable/rounded_bg">
+
+                <androidx.recyclerview.widget.RecyclerView
+                    android:id="@+id/notification_list"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:clipChildren="true"
+                    settings:fastScrollEnabled="true"
+                    settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
+                    settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
+                    settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
+                    settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/>
+
+            </FrameLayout>
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/today_list"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+            <LinearLayout
+                android:id="@+id/app_header"
+                android:layout_height="48dp"
+                android:layout_width="match_parent"
+                android:gravity="center_vertical"
+                android:orientation="horizontal">
+                <ImageView
+                    android:src="@drawable/ic_today"
+                    android:tint="?android:attr/textColorPrimary"
+                    android:padding="6dp"
+                    android:layout_height="36dp"
+                    android:layout_width="36dp" />
+                <TextView
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:text="@string/notification_history_today"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:paddingStart="@dimen/notification_history_header_drawable_start" />
+            </LinearLayout>
+            <LinearLayout
+                android:id="@+id/apps"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:elevation="3dp"
+                android:orientation="vertical"
+                android:background="@drawable/rounded_bg">
+                <!-- app based recycler views added here -->
+            </LinearLayout>
+        </LinearLayout>
+
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/res/layout/notification_history_app_layout.xml b/res/layout/notification_history_app_layout.xml
new file mode 100644
index 0000000..6f26789
--- /dev/null
+++ b/res/layout/notification_history_app_layout.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+    <!-- Copyright (C) 2020 The Android Open Source Project
+
+         Licensed under the Apache License, Version 2.0 (the "License");
+         you may not use this file except in compliance with the License.
+         You may obtain a copy of the License at
+
+              http://www.apache.org/licenses/LICENSE-2.0
+
+         Unless required by applicable law or agreed to in writing, software
+         distributed under the License is distributed on an "AS IS" BASIS,
+         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+         See the License for the specific language governing permissions and
+         limitations under the License.
+    -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <RelativeLayout
+        android:id="@+id/app_header"
+        android:layout_height="48dp"
+        android:layout_width="match_parent">
+        <ImageView
+            android:id="@+id/icon"
+            android:padding="6dp"
+            android:layout_centerVertical="true"
+            android:layout_height="36dp"
+            android:layout_width="36dp" />
+        <TextView
+            android:id="@+id/label"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_centerVertical="true"
+
+            android:textColor="@color/material_grey_900"
+            android:layout_toEndOf="@id/icon"
+            android:paddingStart="@dimen/notification_history_header_drawable_start" />
+        <ImageButton
+            android:id="@+id/expand"
+            android:layout_alignParentEnd="true"
+            android:layout_centerVertical="true"
+            android:layout_height="48dp"
+            android:layout_width="48dp"
+            android:src="@*android:drawable/ic_expand_more"/>
+    </RelativeLayout>
+
+    <FrameLayout
+        android:id="@+id/list_container"
+        android:layout_width="match_parent"
+        android:clipChildren="true"
+        android:layout_height="300dp">
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/notification_list"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:clipChildren="true"
+            settings:fastScrollEnabled="true"
+            settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
+            settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
+            settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
+            settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/>
+
+    </FrameLayout>
+
+    <View
+        android:id="@+id/divider"
+        android:layout_width="match_parent"
+        android:layout_height="0.5dp"
+        android:background="@color/material_grey_300" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/notification_history_log_row.xml b/res/layout/notification_history_log_row.xml
new file mode 100644
index 0000000..4c38167
--- /dev/null
+++ b/res/layout/notification_history_log_row.xml
@@ -0,0 +1,93 @@
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="6dp"
+    android:paddingTop="6dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:orientation="vertical"
+    android:background="?android:attr/selectableItemBackground">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_marginBottom="6dp">
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="@*android:dimen/status_bar_icon_size"
+            android:gravity="center_vertical">
+
+            <TextView
+                android:id="@+id/title"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                android:ellipsize="end"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textColor="@color/material_grey_900"
+                android:textAlignment="viewStart"/>
+
+            <ImageView
+                android:id="@+id/alerted_icon"
+                android:layout_width="@*android:dimen/status_bar_icon_size"
+                android:layout_height="@*android:dimen/status_bar_icon_size"
+                android:layout_centerVertical="true"
+                android:layout_marginStart="6dp"
+                android:paddingTop="1dp"
+                android:scaleType="fitCenter"
+                android:visibility="gone"
+                android:layout_toEndOf="@id/title"
+                android:tint="?android:attr/textColorSecondary"
+                android:src="@drawable/ic_notifications_alert"/>
+
+            <DateTimeView
+                android:id="@+id/timestamp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:layout_centerVertical="true"
+                android:ellipsize="end"
+                android:singleLine="true"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textAlignment="viewEnd"
+                />
+        </RelativeLayout>
+
+        <TextView
+            android:id="@+id/text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="left|center_vertical"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAlignment="viewStart" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="0.5dp"
+            android:layout_marginTop="17dp"
+            android:background="@color/material_grey_300" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/notification_sbn_log_row.xml b/res/layout/notification_sbn_log_row.xml
new file mode 100644
index 0000000..3c51abd
--- /dev/null
+++ b/res/layout/notification_sbn_log_row.xml
@@ -0,0 +1,142 @@
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="6dp"
+    android:paddingTop="6dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:orientation="vertical"
+    android:background="?android:attr/selectableItemBackground"
+    >
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        >
+
+        <ImageView
+            android:id="@+id/icon"
+            android:layout_width="@*android:dimen/status_bar_icon_size"
+            android:layout_height="@*android:dimen/status_bar_icon_size"
+            android:layout_centerVertical="true"
+            android:layout_toEndOf="@+id/pkgicon"
+            android:layout_marginStart="0dp"
+            android:layout_marginEnd="8dp"
+            android:contentDescription="@null"
+            android:adjustViewBounds="true"
+            android:tint="?android:attr/textColorPrimary"
+            android:maxHeight="@*android:dimen/status_bar_icon_size"
+            android:maxWidth="@*android:dimen/status_bar_icon_size"
+            android:scaleType="fitCenter" />
+
+        <TextView
+            android:id="@+id/pkgname"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_toEndOf="@id/icon"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textStyle="bold"
+            android:textAlignment="viewStart" />
+
+        <ImageView
+            android:id="@+id/alerted_icon"
+            android:layout_width="@*android:dimen/status_bar_icon_size"
+            android:layout_height="@*android:dimen/status_bar_icon_size"
+            android:layout_centerVertical="true"
+            android:layout_marginStart="6dp"
+            android:paddingTop="1dp"
+            android:scaleType="fitCenter"
+            android:visibility="gone"
+            android:layout_toEndOf="@id/pkgname"
+            android:tint="?android:attr/textColorSecondary"
+            android:src="@drawable/ic_notifications_alert"
+        />
+
+        <ImageView
+            android:id="@+id/profile_badge"
+            android:layout_width="@*android:dimen/status_bar_icon_size"
+            android:layout_height="@*android:dimen/status_bar_icon_size"
+            android:layout_centerVertical="true"
+            android:layout_marginEnd="6dp"
+            android:paddingTop="1dp"
+            android:scaleType="fitCenter"
+            android:visibility="gone"
+            android:layout_toStartOf="@id/timestamp"
+        />
+
+        <DateTimeView
+            android:id="@+id/timestamp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingTop="13dp"
+            android:paddingBottom="13dp"
+            android:layout_alignBottom="@android:id/widget_frame"
+            android:layout_alignParentEnd="true"
+            android:layout_alignTop="@android:id/widget_frame"
+            android:layout_centerVertical="true"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAlignment="viewEnd"
+            />
+    </RelativeLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_marginStart="30dp"
+        android:layout_marginBottom="6dp"
+        >
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="left|center_vertical"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textAlignment="viewStart"
+        />
+
+        <TextView
+            android:id="@+id/text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="left|center_vertical"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAlignment="viewStart"
+        />
+
+    </LinearLayout>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="0.5dp"
+        android:layout_marginTop="17dp"
+        android:background="@color/material_grey_300" />
+</LinearLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b8afd60..e00eecb 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -81,6 +81,8 @@
     <dimen name="notification_importance_button_text">16sp</dimen>
     <dimen name="notification_importance_button_padding">14dp</dimen>
 
+    <dimen name="notification_history_header_drawable_start">26dp</dimen>
+
     <dimen name="zen_mode_button_padding_vertical">16dp</dimen>
     <dimen name="zen_schedule_rule_checkbox_padding">7dp</dimen>
     <dimen name="zen_schedule_day_margin">17dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6dc4a4d..2efc339 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6147,6 +6147,11 @@
     <string name="admin_more_details">More details</string>
     <string name="notification_log_title">Notification log</string>
 
+    <string name="notification_history_title">Notification history</string>
+    <string name="notification_history_today">Today</string>
+    <string name="notification_history_snooze">Snoozed</string>
+    <string name="notification_history_dismiss">Recently dismissed</string>
+
     <!-- Category title for phone call's ringtone and vibration settings in the Sound Setting.
          [CHAR LIMIT=40] -->
     <string name="sound_category_call_ringtone_vibrate_title">Call ringtone &amp; vibrate</string>
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index a8396a1..f749764 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -21,6 +21,7 @@
 import android.app.INotificationManager;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
+import android.app.NotificationHistory;
 import android.app.role.RoleManager;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
@@ -367,6 +368,15 @@
         return false;
     }
 
+    public NotificationHistory getNotificationHistory(String pkg) {
+        try {
+            return sINM.getNotificationHistory(pkg);
+        } catch (Exception e) {
+            Log.w(TAG, "Error calling NoMan", e);
+        }
+        return new NotificationHistory();
+    }
+
     protected void recordAggregatedUsageEvents(Context context, AppRow appRow) {
         long now = System.currentTimeMillis();
         long startTime = now - (DateUtils.DAY_IN_MILLIS * DAYS_TO_CHECK);
diff --git a/src/com/android/settings/notification/history/HistoryLoader.java b/src/com/android/settings/notification/history/HistoryLoader.java
new file mode 100644
index 0000000..046b5ef
--- /dev/null
+++ b/src/com/android/settings/notification/history/HistoryLoader.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.history;
+
+import android.app.NotificationHistory;
+import android.app.NotificationHistory.HistoricalNotification;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+
+import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class HistoryLoader {
+    private final Context mContext;
+    private final NotificationBackend mBackend;
+    private final PackageManager mPm;
+
+    public HistoryLoader(Context context, NotificationBackend backend, PackageManager pm) {
+        mContext = context;
+        mBackend = backend;
+        mPm = pm;
+    }
+
+    public void load(OnHistoryLoaderListener listener) {
+        ThreadUtils.postOnBackgroundThread(() -> {
+            Map<String, NotificationHistoryPackage> historicalNotifications = new HashMap<>();
+            NotificationHistory history =
+                    mBackend.getNotificationHistory(mContext.getPackageName());
+
+            while (history.hasNextNotification()) {
+                HistoricalNotification hn = history.getNextNotification();
+
+                String key = hn.getPackage() + "|" + hn.getUid();
+                NotificationHistoryPackage hnsForPackage = historicalNotifications.getOrDefault(
+                        key,
+                        new NotificationHistoryPackage(hn.getPackage(), hn.getUid()));
+                hnsForPackage.notifications.add(hn);
+                historicalNotifications.put(key, hnsForPackage);
+            }
+            List<NotificationHistoryPackage> packages =
+                    new ArrayList<>(historicalNotifications.values());
+            Collections.sort(packages,
+                    (o1, o2) -> -1 * Long.compare(o1.getMostRecent(), o2.getMostRecent()));
+            for (NotificationHistoryPackage nhp : packages) {
+                ApplicationInfo info;
+                try {
+                    info = mPm.getApplicationInfoAsUser(
+                            nhp.pkgName,
+                            PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                    | PackageManager.MATCH_DISABLED_COMPONENTS
+                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                                    | PackageManager.MATCH_DIRECT_BOOT_AWARE,
+                            UserHandle.getUserId(nhp.uid));
+                    if (info != null) {
+                        nhp.label = String.valueOf(mPm.getApplicationLabel(info));
+                        nhp.icon = mPm.getUserBadgedIcon(mPm.getApplicationIcon(info),
+                                UserHandle.of(UserHandle.getUserId(nhp.uid)));
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    // app is gone, just show package name and generic icon
+                    nhp.icon = mPm.getDefaultActivityIcon();
+                }
+            }
+            ThreadUtils.postOnMainThread(() -> listener.onHistoryLoaded(packages));
+        });
+    }
+
+    interface OnHistoryLoaderListener {
+        void onHistoryLoaded(List<NotificationHistoryPackage> notificationsByPackage);
+    }
+}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryActivity.java b/src/com/android/settings/notification/history/NotificationHistoryActivity.java
new file mode 100644
index 0000000..173508b
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationHistoryActivity.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.history;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.INotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+import com.android.settings.notification.NotificationBackend;
+
+import java.util.Arrays;
+
+public class NotificationHistoryActivity extends Activity {
+
+    private static String TAG = "NotifHistory";
+
+    private ViewGroup mTodayView;
+    private ViewGroup mSnoozeView;
+    private ViewGroup mDismissView;
+    private HistoryLoader mHistoryLoader;
+    private INotificationManager mNm;
+    private PackageManager mPm;
+
+    private HistoryLoader.OnHistoryLoaderListener mOnHistoryLoaderListener = notifications -> {
+        // for each package, new header and recycler view
+        for (NotificationHistoryPackage nhp : notifications) {
+            View viewForPackage = LayoutInflater.from(this)
+                    .inflate(R.layout.notification_history_app_layout, null);
+
+            final View container = viewForPackage.findViewById(R.id.list_container);
+            container.setVisibility(View.GONE);
+            ImageButton expand = viewForPackage.findViewById(R.id.expand);
+            expand.setOnClickListener(v -> {
+                    container.setVisibility(container.getVisibility() == View.VISIBLE
+                            ? View.GONE : View.VISIBLE);
+                    expand.setImageResource(container.getVisibility() == View.VISIBLE
+                            ? R.drawable.ic_expand_less
+                            : com.android.internal.R.drawable.ic_expand_more);
+            });
+
+            TextView label = viewForPackage.findViewById(R.id.label);
+            label.setText(nhp.label != null ? nhp.label : nhp.pkgName);
+            ImageView icon = viewForPackage.findViewById(R.id.icon);
+            icon.setImageDrawable(nhp.icon);
+
+            RecyclerView rv = viewForPackage.findViewById(R.id.notification_list);
+            rv.setLayoutManager(new LinearLayoutManager(this));
+            rv.setAdapter(new NotificationHistoryAdapter());
+            ((NotificationHistoryAdapter) rv.getAdapter()).onRebuildComplete(nhp.notifications);
+            mTodayView.addView(viewForPackage);
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.notification_history);
+        mTodayView = findViewById(R.id.apps);
+        mSnoozeView = findViewById(R.id.snoozed_list);
+        mDismissView = findViewById(R.id.recently_dismissed_list);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        mPm = getPackageManager();
+
+        mHistoryLoader = new HistoryLoader(this, new NotificationBackend(), mPm);
+        mHistoryLoader.load(mOnHistoryLoaderListener);
+
+        mNm = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        try {
+            mListener.registerAsSystemService(this, new ComponentName(getPackageName(),
+                    this.getClass().getCanonicalName()), ActivityManager.getCurrentUser());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Cannot register listener", e);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        try {
+            mListener.unregisterAsSystemService();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Cannot unregister listener", e);
+        }
+        super.onPause();
+    }
+
+    private final NotificationListenerService mListener = new NotificationListenerService() {
+
+        @Override
+        public void onListenerConnected() {
+            StatusBarNotification[] snoozed = getSnoozedNotifications();
+            if (snoozed == null || snoozed.length == 0) {
+                mSnoozeView.setVisibility(View.GONE);
+            } else {
+                RecyclerView rv = mSnoozeView.findViewById(R.id.notification_list);
+                rv.setLayoutManager(new LinearLayoutManager(NotificationHistoryActivity.this));
+                rv.setAdapter(new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm));
+                ((NotificationSbnAdapter) rv.getAdapter()).onRebuildComplete(
+                        Arrays.asList(snoozed));
+            }
+
+            try {
+                StatusBarNotification[] dismissed = mNm.getHistoricalNotifications(
+                        NotificationHistoryActivity.this.getPackageName(), 10);
+                RecyclerView rv = mDismissView.findViewById(R.id.notification_list);
+                rv.setLayoutManager(new LinearLayoutManager(NotificationHistoryActivity.this));
+                rv.setAdapter(new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm));
+                ((NotificationSbnAdapter) rv.getAdapter()).onRebuildComplete(
+                        Arrays.asList(dismissed));
+                mDismissView.setVisibility(View.VISIBLE);
+            } catch (Exception e) {
+                Slog.e(TAG, "Cannot load recently dismissed", e);
+                mDismissView.setVisibility(View.GONE);
+            }
+        }
+    };
+}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryAdapter.java b/src/com/android/settings/notification/history/NotificationHistoryAdapter.java
new file mode 100644
index 0000000..f87bb20
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationHistoryAdapter.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.history;
+
+import android.app.NotificationHistory;
+import android.app.NotificationHistory.HistoricalNotification;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class NotificationHistoryAdapter extends
+        RecyclerView.Adapter<NotificationHistoryViewHolder> {
+
+    private List<HistoricalNotification> mValues;
+
+    public NotificationHistoryAdapter() {
+        mValues = new ArrayList<>();
+        setHasStableIds(true);
+    }
+
+    @Override
+    public NotificationHistoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.notification_history_log_row, parent, false);
+        return new NotificationHistoryViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(final @NonNull NotificationHistoryViewHolder holder,
+            int position) {
+        final HistoricalNotification hn = mValues.get(position);
+        holder.setTitle(hn.getTitle());
+        holder.setSummary(hn.getText());
+        holder.setPostedTime(hn.getPostedTimeMs());
+        holder.addOnClick(hn.getPackage(), hn.getUserId(), hn.getChannelId());
+    }
+
+    @Override
+    public int getItemCount() {
+        return mValues.size();
+    }
+
+    public void onRebuildComplete(List<HistoricalNotification> notifications) {
+        mValues = notifications;
+        notifyDataSetChanged();
+    }
+}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryPackage.java b/src/com/android/settings/notification/history/NotificationHistoryPackage.java
new file mode 100644
index 0000000..5170b75
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationHistoryPackage.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.history;
+
+import android.app.NotificationHistory;
+import android.graphics.drawable.Drawable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class NotificationHistoryPackage {
+    String pkgName;
+    int uid;
+    List<NotificationHistory.HistoricalNotification> notifications;
+    CharSequence label;
+    Drawable icon;
+
+    public NotificationHistoryPackage(String pkgName, int uid) {
+        this.pkgName = pkgName;
+        this.uid = uid;
+        notifications = new ArrayList<>();
+    }
+
+    public long getMostRecent() {
+        if (notifications.isEmpty()) {
+            return 0;
+        }
+        return notifications.get(0).getPostedTimeMs();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        NotificationHistoryPackage that = (NotificationHistoryPackage) o;
+        return uid == that.uid &&
+                Objects.equals(pkgName, that.pkgName) &&
+                Objects.equals(notifications, that.notifications) &&
+                Objects.equals(label, that.label) &&
+                Objects.equals(icon, that.icon);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(pkgName, uid, notifications, label, icon);
+    }
+}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java b/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java
new file mode 100644
index 0000000..35f5615
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.history;
+
+import static android.provider.Settings.EXTRA_APP_PACKAGE;
+import static android.provider.Settings.EXTRA_CHANNEL_ID;
+
+import android.content.Intent;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.View;
+import android.widget.DateTimeView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+
+public class NotificationHistoryViewHolder extends RecyclerView.ViewHolder {
+
+    private final DateTimeView mTime;
+    private final TextView mTitle;
+    private final TextView mSummary;
+
+    NotificationHistoryViewHolder(View itemView) {
+        super(itemView);
+        mTime = itemView.findViewById(R.id.timestamp);
+        mTime.setShowRelativeTime(true);
+        mTitle = itemView.findViewById(R.id.title);
+        mSummary = itemView.findViewById(R.id.text);
+    }
+
+    void setSummary(CharSequence summary) {
+        mSummary.setText(summary);
+        mSummary.setVisibility(summary != null ? View.VISIBLE : View.GONE);
+    }
+
+    void setTitle(CharSequence title) {
+        mTitle.setText(title);
+        mTitle.setVisibility(title != null ? View.VISIBLE : View.GONE);
+    }
+
+    void setPostedTime(long postedTime) {
+        mTime.setTime(postedTime);
+    }
+
+    void addOnClick(String pkg, int userId, String channelId) {
+        itemView.setOnClickListener(v -> {
+            Intent intent =  new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+                    .putExtra(EXTRA_APP_PACKAGE, pkg)
+                    .putExtra(EXTRA_CHANNEL_ID, channelId);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            itemView.getContext().startActivityAsUser(intent, UserHandle.of(userId));
+        });
+    }
+}
diff --git a/src/com/android/settings/notification/history/NotificationSbnAdapter.java b/src/com/android/settings/notification/history/NotificationSbnAdapter.java
new file mode 100644
index 0000000..80ba278
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationSbnAdapter.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.history;
+
+import static android.content.pm.PackageManager.*;
+
+import android.app.Notification;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class NotificationSbnAdapter extends
+        RecyclerView.Adapter<NotificationSbnViewHolder> {
+
+    private static final String TAG = "SbnAdapter";
+    private List<StatusBarNotification> mValues;
+    private Map<Integer, Drawable> mUserBadgeCache;
+    private final Context mContext;
+    private PackageManager mPm;
+
+    public NotificationSbnAdapter(Context context, PackageManager pm) {
+        mContext = context;
+        mPm = pm;
+        mUserBadgeCache = new HashMap<>();
+        mValues = new ArrayList<>();
+        setHasStableIds(true);
+    }
+
+    @Override
+    public NotificationSbnViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.notification_sbn_log_row, parent, false);
+        return new NotificationSbnViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(final @NonNull NotificationSbnViewHolder holder,
+            int position) {
+        final StatusBarNotification sbn = mValues.get(position);
+        holder.setIcon(loadIcon(sbn));
+        holder.setPackageName(loadPackageName(sbn.getPackageName()).toString());
+        holder.setTitle(getTitleString(sbn.getNotification()));
+        holder.setSummary(getTextString(mContext, sbn.getNotification()));
+        holder.setPostedTime(sbn.getPostTime());
+        if (!mUserBadgeCache.containsKey(sbn.getUserId())) {
+            Drawable profile = mContext.getPackageManager().getUserBadgeForDensity(
+                    UserHandle.of(sbn.getUserId()), -1);
+            mUserBadgeCache.put(sbn.getUserId(), profile);
+        }
+        holder.setProfileBadge(mUserBadgeCache.get(sbn.getUserId()));
+    }
+
+    @Override
+    public int getItemCount() {
+        return mValues.size();
+    }
+
+    public void onRebuildComplete(List<StatusBarNotification> notifications) {
+        mValues = notifications;
+        notifyDataSetChanged();
+    }
+
+    private @NonNull CharSequence loadPackageName(String pkg) {
+        try {
+            ApplicationInfo info = mPm.getApplicationInfo(pkg,
+                    MATCH_ANY_USER);
+            if (info != null) return mPm.getApplicationLabel(info);
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Cannot load package name", e);
+        }
+        return pkg;
+    }
+
+    private static String getTitleString(Notification n) {
+        CharSequence title = null;
+        if (n.extras != null) {
+            title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
+        }
+        return title == null? null : String.valueOf(title);
+    }
+
+    /**
+     * Returns the appropriate substring for this notification based on the style of notification.
+     */
+    private static String getTextString(Context appContext, Notification n) {
+        CharSequence text = null;
+        if (n.extras != null) {
+            text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
+
+            Notification.Builder nb = Notification.Builder.recoverBuilder(appContext, n);
+
+            if (nb.getStyle() instanceof Notification.BigTextStyle) {
+                text = ((Notification.BigTextStyle) nb.getStyle()).getBigText();
+            } else if (nb.getStyle() instanceof Notification.MessagingStyle) {
+                Notification.MessagingStyle ms = (Notification.MessagingStyle) nb.getStyle();
+                final List<Notification.MessagingStyle.Message> messages = ms.getMessages();
+                if (messages != null && messages.size() > 0) {
+                    text = messages.get(messages.size() - 1).getText();
+                }
+            }
+
+            if (TextUtils.isEmpty(text)) {
+                text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
+            }
+        }
+        return text == null ? null : String.valueOf(text);
+    }
+
+    private Drawable loadIcon(StatusBarNotification sbn) {
+        Drawable draw = sbn.getNotification().getSmallIcon().loadDrawableAsUser(
+                sbn.getPackageContext(mContext), sbn.getUserId());
+        if (draw == null) {
+            return null;
+        }
+        draw.mutate();
+        draw.setColorFilter(sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
+        return draw;
+    }
+}
diff --git a/src/com/android/settings/notification/history/NotificationSbnViewHolder.java b/src/com/android/settings/notification/history/NotificationSbnViewHolder.java
new file mode 100644
index 0000000..79cda7f
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationSbnViewHolder.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.history;
+
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.DateTimeView;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+
+public class NotificationSbnViewHolder extends RecyclerView.ViewHolder {
+
+    private final TextView mPkgName;
+    private final ImageView mIcon;
+    private final DateTimeView mTime;
+    private final TextView mTitle;
+    private final TextView mSummary;
+    private final ImageView mProfileBadge;
+
+    NotificationSbnViewHolder(View itemView) {
+        super(itemView);
+        mPkgName = itemView.findViewById(R.id.pkgname);
+        mIcon = itemView.findViewById(R.id.icon);
+        mTime = itemView.findViewById(R.id.timestamp);
+        mTitle = itemView.findViewById(R.id.title);
+        mSummary = itemView.findViewById(R.id.text);
+        mProfileBadge = itemView.findViewById(R.id.profile_badge);
+    }
+
+    void setSummary(CharSequence summary) {
+        mSummary.setVisibility(TextUtils.isEmpty(summary) ? View.GONE : View.VISIBLE);
+        mSummary.setText(summary);
+    }
+
+    void setTitle(CharSequence title) {
+        if (title == null) {
+            return;
+        }
+        mTitle.setText(title);
+    }
+
+    void setIcon(Drawable icon) {
+        mIcon.setImageDrawable(icon);
+    }
+
+    void setPackageName(String pkg) {
+        mPkgName.setText(pkg);
+    }
+
+    void setPostedTime(long postedTime) {
+        mTime.setTime(postedTime);
+    }
+
+    void setProfileBadge(Drawable badge) {
+        mProfileBadge.setImageDrawable(badge);
+    }
+}