Implement new shortcut search list UI

1. Add an EditText for shortcuts search purpose.
2. Add new shortcut categories.
3. Create N to 1 shortcut mapping mechanism.
4. Fn based shortcuts shouldn't be showed.

Demo New UI: go/shortcutlist_demo

Flag: SHORTCUT_LIST_SEARCH_LAYOUT
Bug: 259352579
Test: manual and atest
Change-Id: I688365d5c408ba07b457eec66b3b5cc497390498
diff --git a/packages/SystemUI/res/drawable/ic_shortcutlist_search.xml b/packages/SystemUI/res/drawable/ic_shortcutlist_search.xml
new file mode 100644
index 0000000..1b12e74
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_shortcutlist_search.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright (C) 2022 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="48"
+        android:viewportHeight="48"
+        android:tint="?android:attr/textColorSecondary">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M39.8,41.95 L26.65,28.8Q25.15,30.1 23.15,30.825Q21.15,31.55 18.9,31.55Q13.5,31.55 9.75,27.8Q6,24.05 6,18.75Q6,13.45 9.75,9.7Q13.5,5.95 18.85,5.95Q24.15,5.95 27.875,9.7Q31.6,13.45 31.6,18.75Q31.6,20.9 30.9,22.9Q30.2,24.9 28.8,26.65L42,39.75ZM18.85,28.55Q22.9,28.55 25.75,25.675Q28.6,22.8 28.6,18.75Q28.6,14.7 25.75,11.825Q22.9,8.95 18.85,8.95Q14.75,8.95 11.875,11.825Q9,14.7 9,18.75Q9,22.8 11.875,25.675Q14.75,28.55 18.85,28.55Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_shortcutlist_search_button_cancel.xml b/packages/SystemUI/res/drawable/ic_shortcutlist_search_button_cancel.xml
new file mode 100644
index 0000000..e3b1ab2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_shortcutlist_search_button_cancel.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright (C) 2022 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="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/textColorSecondary">
+    <path
+        android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
+        android:fillColor="@android:color/white"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/shortcut_button_colored.xml b/packages/SystemUI/res/drawable/shortcut_button_colored.xml
new file mode 100644
index 0000000..a21489c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/shortcut_button_colored.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+
+<inset
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <ripple
+        android:color="?android:attr/colorControlHighlight">
+        <item>
+            <shape android:shape="rectangle">
+                <corners android:radius="16dp"/>
+                <solid android:color="?androidprv:attr/colorSurface"/>
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml b/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml
new file mode 100644
index 0000000..2ff32b6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+
+<inset
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <ripple
+        android:color="?android:attr/colorControlHighlight">
+        <item>
+            <shape android:shape="rectangle">
+                <corners android:radius="16dp"/>
+                <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/shortcut_dialog_bg.xml b/packages/SystemUI/res/drawable/shortcut_dialog_bg.xml
new file mode 100644
index 0000000..6ce3eae
--- /dev/null
+++ b/packages/SystemUI/res/drawable/shortcut_dialog_bg.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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/colorBackground"/>
+    <corners android:topLeftRadius="16dp"
+             android:topRightRadius="16dp"
+             android:bottomLeftRadius="0dp"
+             android:bottomRightRadius="0dp"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/shortcut_search_background.xml b/packages/SystemUI/res/drawable/shortcut_search_background.xml
new file mode 100644
index 0000000..66fc191
--- /dev/null
+++ b/packages/SystemUI/res/drawable/shortcut_search_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 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
+  -->
+<layer-list
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="?androidprv:attr/colorSurface" />
+            <corners android:radius="24dp" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_short_separator.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_short_separator.xml
new file mode 100644
index 0000000..530e46e
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_short_separator.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 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
+  -->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_marginTop="8dp"
+      android:layout_marginBottom="0dp"
+      style="@style/ShortcutHorizontalDivider" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_icon_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_icon_view.xml
new file mode 100644
index 0000000..a037cb2
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_icon_view.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:padding="@dimen/ksh_item_padding"
+           android:layout_marginLeft="0dp"
+           android:layout_marginRight="0dp"
+           android:scaleType="fitXY"
+           android:tint="?android:attr/textColorPrimary"
+           style="@style/ShortcutItemBackground" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_view.xml
new file mode 100644
index 0000000..12b4e15
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_view.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:padding="@dimen/ksh_item_padding"
+          android:layout_marginStart="@dimen/ksh_item_margin_start"
+          style="@style/ShortcutItemBackground"
+          android:textColor="?android:attr/textColorPrimary"
+          android:singleLine="false"
+          android:gravity="center"
+          android:textSize="@dimen/ksh_item_text_size"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_plus_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_plus_view.xml
new file mode 100644
index 0000000..727f2c1
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_plus_view.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:padding="@dimen/ksh_item_padding"
+          android:layout_marginLeft="0dp"
+          android:layout_marginRight="0dp"
+          android:text="+"
+          style="@style/ShortcutItemBackground"
+          android:textColor="?android:attr/textColorPrimary"
+          android:singleLine="true"
+          android:gravity="center"
+          android:textSize="@dimen/ksh_item_text_size"
+          android:textAllCaps="true"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_vertical_bar_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_vertical_bar_view.xml
new file mode 100644
index 0000000..00ef947
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_vertical_bar_view.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:padding="@dimen/ksh_item_padding"
+          android:layout_marginLeft="0dp"
+          android:layout_marginRight="0dp"
+          android:text="|"
+          style="@style/ShortcutItemBackground"
+          android:textColor="?android:attr/textColorPrimary"
+          android:singleLine="true"
+          android:gravity="center"
+          android:textSize="@dimen/ksh_item_text_size"
+          android:textAllCaps="true"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
new file mode 100644
index 0000000..8a66f50
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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:background="@drawable/shortcut_dialog_bg"
+    android:layout_width="@dimen/ksh_layout_width"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+    <TextView
+        android:id="@+id/shortcut_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="40dp"
+        android:layout_gravity="center_horizontal"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:textColor="?android:attr/textColorPrimary"
+        android:text="@string/keyboard_shortcut_search_list_title"/>
+
+    <FrameLayout android:layout_width="match_parent"
+                 android:layout_height="wrap_content">
+        <EditText
+            android:id="@+id/keyboard_shortcuts_search"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="24dp"
+            android:layout_marginBottom="24dp"
+            android:layout_marginStart="49dp"
+            android:layout_marginEnd="49dp"
+            android:padding="16dp"
+            android:background="@drawable/shortcut_search_background"
+            android:drawableStart="@drawable/ic_shortcutlist_search"
+            android:drawablePadding="15dp"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorPrimary"
+            android:inputType="text"
+            android:textDirection="locale"
+            android:textAlignment="viewStart"
+            android:hint="@string/keyboard_shortcut_search_list_hint"
+            android:textColorHint="?android:attr/textColorTertiary" />
+
+        <ImageView
+            android:id="@+id/keyboard_shortcuts_search_cancel"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end"
+            android:layout_marginTop="24dp"
+            android:layout_marginBottom="24dp"
+            android:layout_marginEnd="49dp"
+            android:padding="16dp"
+            android:contentDescription="@string/keyboard_shortcut_clear_text"
+            android:src="@drawable/ic_shortcutlist_search_button_cancel" />
+    </FrameLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <View
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginStart="37dp"/>
+
+        <Button
+            android:id="@+id/shortcut_system"
+            android:layout_width="120dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="12dp"
+            style="@style/ShortCutButton"
+            android:text="@string/keyboard_shortcut_search_category_system"/>
+
+        <Button
+            android:id="@+id/shortcut_input"
+            android:layout_width="120dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="12dp"
+            style="@style/ShortCutButton"
+            android:text="@string/keyboard_shortcut_search_category_input"/>
+
+        <Button
+            android:id="@+id/shortcut_open_apps"
+            android:layout_width="120dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="12dp"
+            style="@style/ShortCutButton"
+            android:text="@string/keyboard_shortcut_search_category_open_apps"/>
+
+        <Button
+            android:id="@+id/shortcut_specific_app"
+            android:layout_width="120dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="12dp"
+            style="@style/ShortCutButton"
+            android:text="@string/keyboard_shortcut_search_category_current_app"/>
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/shortcut_search_no_result"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="50dp"
+        android:layout_gravity="center_horizontal"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorPrimary"
+        android:text="@string/keyboard_shortcut_search_list_no_result"/>
+
+    <ScrollView
+        android:id="@+id/keyboard_shortcuts_scroll_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:layout_marginStart="25dp"
+        android:layout_marginEnd="25dp">
+        <LinearLayout
+            android:id="@+id/keyboard_shortcuts_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"/>
+    </ScrollView>
+    <!-- Required for stretching to full available height when the items in the scroll view
+         occupy less space then the full height -->
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 7360c79..d569279 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -118,6 +118,7 @@
     <color name="ksh_application_group_color">#fff44336</color>
     <color name="ksh_key_item_color">@color/material_grey_600</color>
     <color name="ksh_key_item_background">@color/material_grey_100</color>
+    <color name="ksh_key_item_new_background">@color/transparent</color>
 
     <color name="instant_apps_color">#ff4d5a64</color>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1720357..251bf43 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1698,34 +1698,105 @@
     <string name="keyboard_shortcut_group_system">System</string>
     <!-- User visible title for the keyboard shortcut that takes the user to the home screen. -->
     <string name="keyboard_shortcut_group_system_home">Home</string>
-    <!-- User visible title for the the keyboard shortcut that takes the user to the recents screen. -->
+    <!-- User visible title for the keyboard shortcut that takes the user to the recents screen. -->
     <string name="keyboard_shortcut_group_system_recents">Recents</string>
-    <!-- User visible title for the the keyboard shortcut that triggers the back action. -->
+    <!-- User visible title for the keyboard shortcut that triggers the back action. -->
     <string name="keyboard_shortcut_group_system_back">Back</string>
-    <!-- User visible title for the the keyboard shortcut that triggers the notification shade. -->
+    <!-- User visible title for the keyboard shortcut that triggers the notification shade. -->
     <string name="keyboard_shortcut_group_system_notifications">Notifications</string>
-    <!-- User visible title for the the keyboard shortcut that triggers the keyboard shortcuts helper. -->
+    <!-- User visible title for the keyboard shortcut that triggers the keyboard shortcuts helper. -->
     <string name="keyboard_shortcut_group_system_shortcuts_helper">Keyboard Shortcuts</string>
-    <!-- User visible title for the the keyboard shortcut that switches to the next hardware keyboard layout. [CHAR LIMIT=30] -->
+    <!-- User visible title for the keyboard shortcut that switches to the next hardware keyboard layout. -->
     <string name="keyboard_shortcut_group_system_switch_input">Switch keyboard layout</string>
 
-    <!-- User visible title for the system-wide applications keyboard shortcuts list. -->
+    <!-- Content description for the clear text button in shortcut search list. [CHAR LIMIT=NONE] -->
+    <string name="keyboard_shortcut_clear_text">Clear text</string>
+    <!-- The title for keyboard shortcut search list [CHAR LIMIT=25] -->
+    <string name="keyboard_shortcut_search_list_title">Shortcuts</string>
+    <!-- The hint for keyboard shortcut search list [CHAR LIMIT=25] -->
+    <string name="keyboard_shortcut_search_list_hint">Search shortcuts</string>
+    <!-- The description for no shortcuts results [CHAR LIMIT=25] -->
+    <string name="keyboard_shortcut_search_list_no_result">No shortcuts found</string>
+    <!-- The title of system category in shortcut search list. [CHAR LIMIT=15] -->
+    <string name="keyboard_shortcut_search_category_system">System</string>
+    <!-- The title of input category in shortcut search list. [CHAR LIMIT=15] -->
+    <string name="keyboard_shortcut_search_category_input">Input</string>
+    <!-- The title of open apps category in shortcut search list. [CHAR LIMIT=15] -->
+    <string name="keyboard_shortcut_search_category_open_apps">Open apps</string>
+    <!-- The title of current app category in shortcut search list. [CHAR LIMIT=15] -->
+    <string name="keyboard_shortcut_search_category_current_app">Current app</string>
+
+    <!-- User visible title for the keyboard shortcut that triggers the notification shade. [CHAR LIMIT=70] -->
+    <string name="group_system_access_notification_shade">Access notification shade</string>
+    <!-- User visible title for the keyboard shortcut that takes a full screenshot. [CHAR LIMIT=70] -->
+    <string name="group_system_full_screenshot">Take a full screenshot</string>
+    <!-- User visible title for the keyboard shortcut that access list of system / apps shortcuts. [CHAR LIMIT=70] -->
+    <string name="group_system_access_system_app_shortcuts">Access list of system / apps shortcuts</string>
+    <!-- User visible title for the keyboard shortcut that goes back to previous state. [CHAR LIMIT=70] -->
+    <string name="group_system_go_back">Back: go back to previous state (back button)</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the home screen. [CHAR LIMIT=70] -->
+    <string name="group_system_access_home_screen">Access home screen</string>
+    <!-- User visible title for the keyboard shortcut that triggers overview of open apps. [CHAR LIMIT=70] -->
+    <string name="group_system_overview_open_apps">Overview of open apps</string>
+    <!-- User visible title for the keyboard shortcut that cycles through recent apps (forward). [CHAR LIMIT=70] -->
+    <string name="group_system_cycle_forward">Cycle through recent apps (forward)</string>
+    <!-- User visible title for the keyboard shortcut that cycles through recent apps (back). [CHAR LIMIT=70] -->
+    <string name="group_system_cycle_back">Cycle through recent apps (back)</string>
+    <!-- User visible title for the keyboard shortcut that accesses list of all apps and search. [CHAR LIMIT=70] -->
+    <string name="group_system_access_all_apps_search">Access list of all apps and search (i.e. Search/Launcher)</string>
+    <!-- User visible title for the keyboard shortcut that hides and (re)showes taskbar. [CHAR LIMIT=70] -->
+    <string name="group_system_hide_reshow_taskbar">Hide and (re)show taskbar</string>
+    <!-- User visible title for the keyboard shortcut that accesses system settings. [CHAR LIMIT=70] -->
+    <string name="group_system_access_system_settings">Access system settings</string>
+    <!-- User visible title for the keyboard shortcut that accesses Google Assistant. [CHAR LIMIT=70] -->
+    <string name="group_system_access_google_assistant">Access Google Assistant</string>
+    <!-- User visible title for the keyboard shortcut that locks screen. [CHAR LIMIT=70] -->
+    <string name="group_system_lock_screen">Lock screen</string>
+    <!-- User visible title for the keyboard shortcut that pulls up Notes app for quick memo. [CHAR LIMIT=70] -->
+    <string name="group_system_quick_memo">Pull up Notes app for quick memo</string>
+
+    <!-- User visible title for the system multitasking keyboard shortcuts list. [CHAR LIMIT=70] -->
+    <string name="keyboard_shortcut_group_system_multitasking">System multitasking</string>
+    <!-- User visible title for the keyboard shortcut that enters split screen with current app to RHS [CHAR LIMIT=70] -->
+    <string name="system_multitasking_rhs">Enter Split screen with current app to RHS</string>
+    <!-- User visible title for the keyboard shortcut that enters split screen with current app to LHS [CHAR LIMIT=70] -->
+    <string name="system_multitasking_lhs">Enter Split screen with current app to LHS</string>
+    <!-- User visible title for the keyboard shortcut that switches from split screen to full screen [CHAR LIMIT=70] -->
+    <string name="system_multitasking_full_screen">Switch from Split screen to full screen</string>
+    <!-- User visible title for the keyboard shortcut that replaces an app from one to another during split screen [CHAR LIMIT=70] -->
+    <string name="system_multitasking_replace">During Split screen: replace an app from one to another</string>
+
+    <!-- User visible title for the input keyboard shortcuts list. [CHAR LIMIT=70] -->
+    <string name="keyboard_shortcut_group_input">Input</string>
+    <!-- User visible title for the keyboard shortcut that switches input language (next language). [CHAR LIMIT=70] -->
+    <string name="input_switch_input_language_next">Switch input language (next language)</string>
+    <!-- User visible title for the keyboard shortcut that switches input language (previous language). [CHAR LIMIT=70] -->
+    <string name="input_switch_input_language_previous">Switch input language (previous language)</string>
+    <!-- User visible title for the keyboard shortcut that accesses emoji. [CHAR LIMIT=70] -->
+    <string name="input_access_emoji">Access emoji</string>
+    <!-- User visible title for the keyboard shortcut that accesses voice typing. [CHAR LIMIT=70] -->
+    <string name="input_access_voice_typing">Access voice typing</string>
+
+    <!-- User visible title for the system-wide applications keyboard shortcuts list. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_applications">Applications</string>
-    <!-- User visible title for the keyboard shortcut that takes the user to the assist app. -->
+    <!-- User visible title for the keyboard shortcut that takes the user to the assist app. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_applications_assist">Assist</string>
-    <!-- User visible title for the keyboard shortcut that takes the user to the browser app. -->
-    <string name="keyboard_shortcut_group_applications_browser">Browser</string>
-    <!-- User visible title for the keyboard shortcut that takes the user to the contacts app. -->
+    <!-- User visible title for the keyboard shortcut that takes the user to the browser app. [CHAR LIMIT=70] -->
+    <string name="keyboard_shortcut_group_applications_browser">Browser (Chrome as default)</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the contacts app. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_applications_contacts">Contacts</string>
-    <!-- User visible title for the keyboard shortcut that takes the user to the email app. -->
-    <string name="keyboard_shortcut_group_applications_email">Email</string>
-    <!-- User visible title for the keyboard shortcut that takes the user to the SMS messaging app. -->
+    <!-- User visible title for the keyboard shortcut that takes the user to the email app. [CHAR LIMIT=70] -->
+    <string name="keyboard_shortcut_group_applications_email">Email (Gmail as default)</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the SMS messaging app. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_applications_sms">SMS</string>
-    <!-- User visible title for the keyboard shortcut that takes the user to the music app. -->
+    <!-- User visible title for the keyboard shortcut that takes the user to the music app. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_applications_music">Music</string>
-    <!-- User visible title for the keyboard shortcut that takes the user to the YouTube app. -->
-    <!-- User visible title for the keyboard shortcut that takes the user to the calendar app. -->
+    <!-- User visible title for the keyboard shortcut that takes the user to the calendar app. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_applications_calendar">Calendar</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the calculator app. [CHAR LIMIT=70] -->
+    <string name="keyboard_shortcut_group_applications_calculator">Calculator</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the maps app. [CHAR LIMIT=70] -->
+    <string name="keyboard_shortcut_group_applications_maps">Maps</string>
 
     <!-- SysUI Tuner: Label for screen about do not disturb settings [CHAR LIMIT=60] -->
     <string name="volume_and_do_not_disturb">Do Not Disturb</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4998d68..91b9837 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1388,4 +1388,23 @@
         <item name="android:lineHeight">@dimen/magnification_setting_button_line_height</item>
         <item name="android:textAlignment">center</item>
     </style>
+
+    <style name="ShortCutButton" parent="@android:style/Widget.Material.Button">
+        <item name="android:background">@drawable/shortcut_button_colored</item>
+        <item name="android:stateListAnimator">@null</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:padding">4dp</item>
+        <item name="android:textColor">?androidprv:attr/textColorSecondary</item>
+    </style>
+
+    <style name="ShortcutHorizontalDivider">
+        <item name="android:layout_width">120dp</item>
+        <item name="android:layout_height">1dp</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+        <item name="android:background">?android:attr/dividerHorizontal</item>
+    </style>
+
+    <style name="ShortcutItemBackground">
+        <item name="android:background">@color/ksh_key_item_new_background</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index d0c5007..fadabb4 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -47,6 +47,7 @@
 import com.android.systemui.shade.ShadeControllerImpl;
 import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.KeyboardShortcutsModule;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -100,7 +101,8 @@
         QSModule.class,
         ReferenceScreenshotModule.class,
         StartCentralSurfacesModule.class,
-        VolumeModule.class
+        VolumeModule.class,
+        KeyboardShortcutsModule.class
 })
 public abstract class ReferenceSystemUIModule {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
new file mode 100644
index 0000000..01e042b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -0,0 +1,1382 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
+import android.view.KeyboardShortcutInfo;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.WindowManager.KeyboardShortcutsReceiver;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.AssistUtils;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import com.google.android.material.bottomsheet.BottomSheetDialog;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Contains functionality for handling keyboard shortcuts.
+ */
+public final class KeyboardShortcutListSearch {
+    private static final String TAG = KeyboardShortcutListSearch.class.getSimpleName();
+    private static final Object sLock = new Object();
+    @VisibleForTesting static KeyboardShortcutListSearch sInstance;
+
+    private static int SHORTCUT_SYSTEM_INDEX = 0;
+    private static int SHORTCUT_INPUT_INDEX = 1;
+    private static int SHORTCUT_OPENAPPS_INDEX = 2;
+    private static int SHORTCUT_SPECIFICAPP_INDEX = 3;
+
+    private WindowManager mWindowManager;
+    private EditText mSearchEditText;
+    private String mQueryString;
+    private int mCurrentCategoryIndex = 0;
+    private Map<Integer, Boolean> mKeySearchResultMap = new HashMap<>();
+
+    private List<List<KeyboardShortcutMultiMappingGroup>> mFullShortsGroup = new ArrayList<>();
+    private List<KeyboardShortcutMultiMappingGroup> mSpecificAppGroup = new ArrayList<>();
+    private List<KeyboardShortcutMultiMappingGroup> mSystemGroup = new ArrayList<>();
+    private List<KeyboardShortcutMultiMappingGroup> mInputGroup = new ArrayList<>();
+    private List<KeyboardShortcutMultiMappingGroup> mOpenAppsGroup = new ArrayList<>();
+
+    private ArrayList<Button> mFullButtonList = new ArrayList<>();
+    private Button mButtonSystem;
+    private Button mButtonInput;
+    private Button mButtonOpenApps;
+    private Button mButtonSpecificApp;
+    private ImageView mEditTextCancel;
+    private TextView mNoSearchResults;
+
+    private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
+    private final SparseArray<String> mModifierNames = new SparseArray<>();
+    private final SparseArray<Drawable> mSpecialCharacterDrawables = new SparseArray<>();
+    private final SparseArray<Drawable> mModifierDrawables = new SparseArray<>();
+    // Ordered list of modifiers that are supported. All values in this array must exist in
+    // mModifierNames.
+    private final int[] mModifierList = new int[] {
+            KeyEvent.META_META_ON, KeyEvent.META_CTRL_ON, KeyEvent.META_ALT_ON,
+            KeyEvent.META_SHIFT_ON, KeyEvent.META_SYM_ON, KeyEvent.META_FUNCTION_ON
+    };
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    @VisibleForTesting Context mContext;
+    private final IPackageManager mPackageManager;
+
+    @VisibleForTesting BottomSheetDialog mKeyboardShortcutsBottomSheetDialog;
+    private KeyCharacterMap mKeyCharacterMap;
+    private KeyCharacterMap mBackupKeyCharacterMap;
+
+    @VisibleForTesting
+    KeyboardShortcutListSearch(Context context, WindowManager windowManager) {
+        this.mContext = new ContextThemeWrapper(
+                context, android.R.style.Theme_DeviceDefault_Settings);
+        this.mPackageManager = AppGlobals.getPackageManager();
+        if (windowManager != null) {
+            this.mWindowManager = windowManager;
+        } else {
+            this.mWindowManager = mContext.getSystemService(WindowManager.class);
+        }
+        loadResources(context);
+        createHardcodedShortcuts();
+    }
+
+    private static KeyboardShortcutListSearch getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new KeyboardShortcutListSearch(context, null);
+        }
+        return sInstance;
+    }
+
+    public static void show(Context context, int deviceId) {
+        MetricsLogger.visible(context,
+                MetricsProto.MetricsEvent.KEYBOARD_SHORTCUTS_HELPER);
+        synchronized (sLock) {
+            if (sInstance != null && !sInstance.mContext.equals(context)) {
+                dismiss();
+            }
+            getInstance(context).showKeyboardShortcuts(deviceId);
+        }
+    }
+
+    public static void toggle(Context context, int deviceId) {
+        synchronized (sLock) {
+            if (isShowing()) {
+                dismiss();
+            } else {
+                show(context, deviceId);
+            }
+        }
+    }
+
+    public static void dismiss() {
+        synchronized (sLock) {
+            if (sInstance != null) {
+                MetricsLogger.hidden(sInstance.mContext,
+                        MetricsProto.MetricsEvent.KEYBOARD_SHORTCUTS_HELPER);
+                sInstance.dismissKeyboardShortcuts();
+                sInstance = null;
+            }
+        }
+    }
+
+    private static boolean isShowing() {
+        return sInstance != null && sInstance.mKeyboardShortcutsBottomSheetDialog != null
+                && sInstance.mKeyboardShortcutsBottomSheetDialog.isShowing();
+    }
+
+    private void loadResources(Context context) {
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_HOME, context.getString(R.string.keyboard_key_home));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_BACK, context.getString(R.string.keyboard_key_back));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_DPAD_UP, context.getString(R.string.keyboard_key_dpad_up));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_DPAD_DOWN, context.getString(R.string.keyboard_key_dpad_down));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_DPAD_LEFT, context.getString(R.string.keyboard_key_dpad_left));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_DPAD_RIGHT, context.getString(R.string.keyboard_key_dpad_right));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_DPAD_CENTER, context.getString(R.string.keyboard_key_dpad_center));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_PERIOD, ".");
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_TAB, context.getString(R.string.keyboard_key_tab));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_SPACE, context.getString(R.string.keyboard_key_space));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_ENTER, context.getString(R.string.keyboard_key_enter));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_DEL, context.getString(R.string.keyboard_key_backspace));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE,
+                context.getString(R.string.keyboard_key_media_play_pause));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_MEDIA_STOP, context.getString(R.string.keyboard_key_media_stop));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_MEDIA_NEXT, context.getString(R.string.keyboard_key_media_next));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_MEDIA_PREVIOUS,
+                context.getString(R.string.keyboard_key_media_previous));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_MEDIA_REWIND,
+                context.getString(R.string.keyboard_key_media_rewind));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD,
+                context.getString(R.string.keyboard_key_media_fast_forward));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_PAGE_UP, context.getString(R.string.keyboard_key_page_up));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_PAGE_DOWN, context.getString(R.string.keyboard_key_page_down));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_A,
+                context.getString(R.string.keyboard_key_button_template, "A"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_B,
+                context.getString(R.string.keyboard_key_button_template, "B"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_C,
+                context.getString(R.string.keyboard_key_button_template, "C"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_X,
+                context.getString(R.string.keyboard_key_button_template, "X"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_Y,
+                context.getString(R.string.keyboard_key_button_template, "Y"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_Z,
+                context.getString(R.string.keyboard_key_button_template, "Z"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_L1,
+                context.getString(R.string.keyboard_key_button_template, "L1"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_R1,
+                context.getString(R.string.keyboard_key_button_template, "R1"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_L2,
+                context.getString(R.string.keyboard_key_button_template, "L2"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_R2,
+                context.getString(R.string.keyboard_key_button_template, "R2"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_START,
+                context.getString(R.string.keyboard_key_button_template, "Start"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_SELECT,
+                context.getString(R.string.keyboard_key_button_template, "Select"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_MODE,
+                context.getString(R.string.keyboard_key_button_template, "Mode"));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_FORWARD_DEL, context.getString(R.string.keyboard_key_forward_del));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_ESCAPE, "Esc");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_SYSRQ, "SysRq");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BREAK, "Break");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_SCROLL_LOCK, "Scroll Lock");
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_MOVE_HOME, context.getString(R.string.keyboard_key_move_home));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_MOVE_END, context.getString(R.string.keyboard_key_move_end));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_INSERT, context.getString(R.string.keyboard_key_insert));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F1, "F1");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F2, "F2");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F3, "F3");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F4, "F4");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F5, "F5");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F6, "F6");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F7, "F7");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F8, "F8");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F9, "F9");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F10, "F10");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F11, "F11");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F12, "F12");
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_NUM_LOCK, context.getString(R.string.keyboard_key_num_lock));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_MINUS, "-");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_GRAVE, "~");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_EQUALS, "=");
+
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_0,
+                context.getString(R.string.keyboard_key_numpad_template, "0"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_1,
+                context.getString(R.string.keyboard_key_numpad_template, "1"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_2,
+                context.getString(R.string.keyboard_key_numpad_template, "2"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_3,
+                context.getString(R.string.keyboard_key_numpad_template, "3"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_4,
+                context.getString(R.string.keyboard_key_numpad_template, "4"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_5,
+                context.getString(R.string.keyboard_key_numpad_template, "5"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_6,
+                context.getString(R.string.keyboard_key_numpad_template, "6"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_7,
+                context.getString(R.string.keyboard_key_numpad_template, "7"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_8,
+                context.getString(R.string.keyboard_key_numpad_template, "8"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_9,
+                context.getString(R.string.keyboard_key_numpad_template, "9"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_DIVIDE,
+                context.getString(R.string.keyboard_key_numpad_template, "/"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_MULTIPLY,
+                context.getString(R.string.keyboard_key_numpad_template, "*"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_SUBTRACT,
+                context.getString(R.string.keyboard_key_numpad_template, "-"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_ADD,
+                context.getString(R.string.keyboard_key_numpad_template, "+"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_DOT,
+                context.getString(R.string.keyboard_key_numpad_template, "."));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_COMMA,
+                context.getString(R.string.keyboard_key_numpad_template, ","));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_ENTER,
+                context.getString(R.string.keyboard_key_numpad_template,
+                        context.getString(R.string.keyboard_key_enter)));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_EQUALS,
+                context.getString(R.string.keyboard_key_numpad_template, "="));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN,
+                context.getString(R.string.keyboard_key_numpad_template, "("));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN,
+                context.getString(R.string.keyboard_key_numpad_template, ")"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_ZENKAKU_HANKAKU, "半角/全角");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_EISU, "英数");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_MUHENKAN, "無変換");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_HENKAN, "変換");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_KATAKANA_HIRAGANA, "かな");
+
+        mModifierNames.put(KeyEvent.META_META_ON, "Meta");
+        mModifierNames.put(KeyEvent.META_CTRL_ON, "Ctrl");
+        mModifierNames.put(KeyEvent.META_ALT_ON, "Alt");
+        mModifierNames.put(KeyEvent.META_SHIFT_ON, "Shift");
+        mModifierNames.put(KeyEvent.META_SYM_ON, "Sym");
+        mModifierNames.put(KeyEvent.META_FUNCTION_ON, "Fn");
+
+        mSpecialCharacterDrawables.put(
+                KeyEvent.KEYCODE_DEL, context.getDrawable(R.drawable.ic_ksh_key_backspace));
+        mSpecialCharacterDrawables.put(
+                KeyEvent.KEYCODE_ENTER, context.getDrawable(R.drawable.ic_ksh_key_enter));
+        mSpecialCharacterDrawables.put(
+                KeyEvent.KEYCODE_DPAD_UP, context.getDrawable(R.drawable.ic_ksh_key_up));
+        mSpecialCharacterDrawables.put(
+                KeyEvent.KEYCODE_DPAD_RIGHT, context.getDrawable(R.drawable.ic_ksh_key_right));
+        mSpecialCharacterDrawables.put(
+                KeyEvent.KEYCODE_DPAD_DOWN, context.getDrawable(R.drawable.ic_ksh_key_down));
+        mSpecialCharacterDrawables.put(
+                KeyEvent.KEYCODE_DPAD_LEFT, context.getDrawable(R.drawable.ic_ksh_key_left));
+
+        mModifierDrawables.put(
+                KeyEvent.META_META_ON, context.getDrawable(R.drawable.ic_ksh_key_meta));
+    }
+
+    private void createHardcodedShortcuts() {
+        // Add system shortcuts
+        mKeySearchResultMap.put(SHORTCUT_SYSTEM_INDEX, true);
+        mSystemGroup.add(getMultiMappingSystemShortcuts(mContext));
+        mSystemGroup.add(getSystemMultitaskingShortcuts(mContext));
+        // Add input shortcuts
+        mKeySearchResultMap.put(SHORTCUT_INPUT_INDEX, true);
+        mInputGroup.add(getMultiMappingInputShortcuts(mContext));
+        // Add open apps shortcuts
+        final List<KeyboardShortcutMultiMappingGroup> appShortcuts =
+                Arrays.asList(getDefaultMultiMappingApplicationShortcuts());
+        if (appShortcuts != null && !appShortcuts.isEmpty()) {
+            mOpenAppsGroup = appShortcuts;
+            mKeySearchResultMap.put(SHORTCUT_OPENAPPS_INDEX, true);
+        } else {
+            mKeySearchResultMap.put(SHORTCUT_OPENAPPS_INDEX, false);
+        }
+    }
+
+    /**
+     * Retrieves a {@link KeyCharacterMap} and assigns it to mKeyCharacterMap. If the given id is an
+     * existing device, that device's map is used. Otherwise, it checks first all available devices
+     * and if there is a full keyboard it uses that map, otherwise falls back to the Virtual
+     * Keyboard with its default map.
+     */
+    private void retrieveKeyCharacterMap(int deviceId) {
+        final InputManager inputManager = InputManager.getInstance();
+        mBackupKeyCharacterMap = inputManager.getInputDevice(-1).getKeyCharacterMap();
+        if (deviceId != -1) {
+            final InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+            if (inputDevice != null) {
+                mKeyCharacterMap = inputDevice.getKeyCharacterMap();
+                return;
+            }
+        }
+        final int[] deviceIds = inputManager.getInputDeviceIds();
+        for (int id : deviceIds) {
+            final InputDevice inputDevice = inputManager.getInputDevice(id);
+            // -1 is the Virtual Keyboard, with the default key map. Use that one only as last
+            // resort.
+            if (inputDevice.getId() != -1 && inputDevice.isFullKeyboard()) {
+                mKeyCharacterMap = inputDevice.getKeyCharacterMap();
+                return;
+            }
+        }
+        // Fall back to -1, the virtual keyboard.
+        mKeyCharacterMap = mBackupKeyCharacterMap;
+    }
+
+    @VisibleForTesting
+    void showKeyboardShortcuts(int deviceId) {
+        retrieveKeyCharacterMap(deviceId);
+        mWindowManager.requestAppKeyboardShortcuts(new KeyboardShortcutsReceiver() {
+            @Override
+            public void onKeyboardShortcutsReceived(
+                    final List<KeyboardShortcutGroup> result) {
+                // Add specific app shortcuts
+                if (result.isEmpty()) {
+                    mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, false);
+                } else {
+                    mSpecificAppGroup = reMapToKeyboardShortcutMultiMappingGroup(result);
+                    mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, true);
+                }
+                mFullShortsGroup.add(SHORTCUT_SYSTEM_INDEX, mSystemGroup);
+                mFullShortsGroup.add(SHORTCUT_INPUT_INDEX, mInputGroup);
+                mFullShortsGroup.add(SHORTCUT_OPENAPPS_INDEX, mOpenAppsGroup);
+                mFullShortsGroup.add(SHORTCUT_SPECIFICAPP_INDEX, mSpecificAppGroup);
+                showKeyboardShortcutSearchList(mFullShortsGroup);
+            }
+        }, deviceId);
+    }
+
+    // The original data structure is only for 1-to-1 shortcut mapping, so remap the old
+    // data structure to the new data structure for handling the N-to-1 key mapping and other
+    // complex case.
+    private List<KeyboardShortcutMultiMappingGroup> reMapToKeyboardShortcutMultiMappingGroup(
+            List<KeyboardShortcutGroup> keyboardShortcutGroups) {
+        List<KeyboardShortcutMultiMappingGroup> keyboardShortcutMultiMappingGroups =
+                new ArrayList<>();
+        for (KeyboardShortcutGroup group : keyboardShortcutGroups) {
+            CharSequence categoryTitle = group.getLabel();
+            List<ShortcutMultiMappingInfo> shortcutMultiMappingInfos = new ArrayList<>();
+            for (KeyboardShortcutInfo info : group.getItems()) {
+                shortcutMultiMappingInfos.add(new ShortcutMultiMappingInfo(info));
+            }
+            keyboardShortcutMultiMappingGroups.add(
+                    new KeyboardShortcutMultiMappingGroup(
+                            categoryTitle, shortcutMultiMappingInfos));
+        }
+        return keyboardShortcutMultiMappingGroups;
+    }
+
+    private void dismissKeyboardShortcuts() {
+        if (mKeyboardShortcutsBottomSheetDialog != null) {
+            mKeyboardShortcutsBottomSheetDialog.dismiss();
+            mKeyboardShortcutsBottomSheetDialog = null;
+        }
+    }
+
+    private KeyboardShortcutMultiMappingGroup getMultiMappingSystemShortcuts(Context context) {
+        KeyboardShortcutMultiMappingGroup systemGroup =
+                new KeyboardShortcutMultiMappingGroup(
+                        context.getString(R.string.keyboard_shortcut_group_system),
+                        new ArrayList<>());
+        List<ShortcutKeyGroupMultiMappingInfo> infoList = Arrays.asList(
+                /* Access notification shade: Meta + N */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_access_notification_shade),
+                        Arrays.asList(
+                                Pair.create(KeyEvent.KEYCODE_N, KeyEvent.META_META_ON))),
+                /* Take a full screenshot: Meta + Ctrl + S */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_full_screenshot),
+                        Arrays.asList(
+                                Pair.create(
+                                        KeyEvent.KEYCODE_S,
+                                        KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON))),
+                /* Access list of system / apps shortcuts: Meta + / */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_access_system_app_shortcuts),
+                        Arrays.asList(
+                                Pair.create(KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON))),
+                /* Back: go back to previous state (back button) */
+                /* Meta + ~, Meta + backspace, Meta + left arrow */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_go_back),
+                        Arrays.asList(
+                                Pair.create(KeyEvent.KEYCODE_GRAVE, KeyEvent.META_META_ON),
+                                Pair.create(KeyEvent.KEYCODE_DEL, KeyEvent.META_META_ON),
+                                Pair.create(KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.META_META_ON))),
+                /* Access home screen: Meta + H, Meta + Enter */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_access_home_screen),
+                        Arrays.asList(
+                                Pair.create(KeyEvent.KEYCODE_H, KeyEvent.META_META_ON),
+                                Pair.create(KeyEvent.KEYCODE_ENTER, KeyEvent.META_META_ON))),
+                /* Overview of open apps: Meta + Tab */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_overview_open_apps),
+                        Arrays.asList(
+                                Pair.create(KeyEvent.KEYCODE_TAB, KeyEvent.META_META_ON))),
+                /* Cycle through recent apps (forward): Alt + Tab */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_cycle_forward),
+                        Arrays.asList(
+                                Pair.create(KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON))),
+                /* Cycle through recent apps (back): Shift + Alt + Tab */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_cycle_back),
+                        Arrays.asList(
+                                Pair.create(
+                                        KeyEvent.KEYCODE_TAB,
+                                        KeyEvent.META_SHIFT_ON | KeyEvent.META_ALT_ON))),
+                /* Access list of all apps and search (i.e. Search/Launcher): Meta */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_access_all_apps_search),
+                        Arrays.asList(
+                                Pair.create(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.META_META_ON))),
+                /* Hide and (re)show taskbar: Meta + T */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_hide_reshow_taskbar),
+                        Arrays.asList(
+                                Pair.create(KeyEvent.KEYCODE_T, KeyEvent.META_META_ON))),
+                /* Access system settings: Meta + I */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_access_system_settings),
+                        Arrays.asList(
+                                Pair.create(KeyEvent.KEYCODE_I, KeyEvent.META_META_ON))),
+                /* Access Google Assistant: Meta + A */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_access_google_assistant),
+                        Arrays.asList(
+                                Pair.create(KeyEvent.KEYCODE_A, KeyEvent.META_META_ON))),
+                /*  Lock screen: Meta + L */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_lock_screen),
+                        Arrays.asList(
+                                Pair.create(KeyEvent.KEYCODE_L, KeyEvent.META_META_ON))),
+                /* Pull up Notes app for quick memo: Meta + Ctrl + N */
+                new ShortcutKeyGroupMultiMappingInfo(
+                        context.getString(R.string.group_system_quick_memo),
+                        Arrays.asList(
+                                Pair.create(
+                                        KeyEvent.KEYCODE_N,
+                                        KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON)))
+        );
+        for (ShortcutKeyGroupMultiMappingInfo info : infoList) {
+            systemGroup.addItem(info.getShortcutMultiMappingInfo());
+        }
+        return systemGroup;
+    }
+
+    private static class ShortcutKeyGroupMultiMappingInfo {
+        private String mLabel;
+        private List<Pair<Integer, Integer>> mKeycodeGroupList;
+
+        ShortcutKeyGroupMultiMappingInfo(
+                String label, List<Pair<Integer, Integer>> keycodeGroupList) {
+            mLabel = label;
+            mKeycodeGroupList = keycodeGroupList;
+        }
+
+        ShortcutMultiMappingInfo getShortcutMultiMappingInfo() {
+            List<ShortcutKeyGroup> shortcutKeyGroups = new ArrayList<>();
+            for (Pair<Integer, Integer> keycodeGroup : mKeycodeGroupList) {
+                shortcutKeyGroups.add(new ShortcutKeyGroup(
+                        new KeyboardShortcutInfo(
+                                mLabel,
+                                keycodeGroup.first /* keycode */,
+                                keycodeGroup.second /* modifiers*/),
+                        null));
+            }
+            ShortcutMultiMappingInfo shortcutMultiMappingInfo =
+                    new ShortcutMultiMappingInfo(mLabel, null, shortcutKeyGroups);
+            return shortcutMultiMappingInfo;
+        }
+    }
+
+    private static KeyboardShortcutMultiMappingGroup getSystemMultitaskingShortcuts(
+            Context context) {
+        KeyboardShortcutMultiMappingGroup systemMultitaskingGroup =
+                new KeyboardShortcutMultiMappingGroup(
+                        context.getString(R.string.keyboard_shortcut_group_system_multitasking),
+                        new ArrayList<>());
+
+        // System multitasking shortcuts:
+        //    Enter Split screen with current app to RHS: Meta + Ctrl + Right arrow
+        //    Enter Split screen with current app to LHS: Meta + Ctrl + Left arrow
+        //    Switch from Split screen to full screen: Meta + Ctrl + Up arrow
+        //    During Split screen: replace an app from one to another: Meta + Ctrl + Down arrow
+        String[] shortcutLabels = {
+                context.getString(R.string.system_multitasking_rhs),
+                context.getString(R.string.system_multitasking_lhs),
+                context.getString(R.string.system_multitasking_full_screen),
+                context.getString(R.string.system_multitasking_replace)
+        };
+        int[] keyCodes = {
+                KeyEvent.KEYCODE_DPAD_RIGHT,
+                KeyEvent.KEYCODE_DPAD_LEFT,
+                KeyEvent.KEYCODE_DPAD_UP,
+                KeyEvent.KEYCODE_DPAD_DOWN
+        };
+
+        for (int i = 0; i < shortcutLabels.length; i++) {
+            List<ShortcutKeyGroup> shortcutKeyGroups = Arrays.asList(new ShortcutKeyGroup(
+                    new KeyboardShortcutInfo(
+                            shortcutLabels[i],
+                            keyCodes[i],
+                            KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON),
+                    null));
+            ShortcutMultiMappingInfo shortcutMultiMappingInfo =
+                    new ShortcutMultiMappingInfo(
+                            shortcutLabels[i],
+                            null,
+                            shortcutKeyGroups);
+            systemMultitaskingGroup.addItem(shortcutMultiMappingInfo);
+        }
+        return systemMultitaskingGroup;
+    }
+
+    private static KeyboardShortcutMultiMappingGroup getMultiMappingInputShortcuts(
+            Context context) {
+        List<ShortcutMultiMappingInfo> shortcutMultiMappingInfoList = Arrays.asList(
+                /* Switch input language (next language): Ctrl + Space or Meta + Space */
+                new ShortcutMultiMappingInfo(
+                        context.getString(R.string.input_switch_input_language_next),
+                        null,
+                        Arrays.asList(
+                                new ShortcutKeyGroup(new KeyboardShortcutInfo(
+                                        context.getString(
+                                                R.string.input_switch_input_language_next),
+                                        KeyEvent.KEYCODE_SPACE, KeyEvent.META_CTRL_ON),
+                                        null),
+                                new ShortcutKeyGroup(new KeyboardShortcutInfo(
+                                        context.getString(
+                                                R.string.input_switch_input_language_next),
+                                        KeyEvent.KEYCODE_SPACE, KeyEvent.META_META_ON),
+                                        null))),
+                /* Switch input language (previous language): */
+                /* Ctrl + Shift + Space or Meta + Shift + Space */
+                new ShortcutMultiMappingInfo(
+                        context.getString(R.string.input_switch_input_language_previous),
+                        null,
+                        Arrays.asList(
+                                new ShortcutKeyGroup(new KeyboardShortcutInfo(
+                                        context.getString(
+                                                R.string.input_switch_input_language_previous),
+                                        KeyEvent.KEYCODE_SPACE,
+                                        KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON),
+                                        null),
+                                new ShortcutKeyGroup(new KeyboardShortcutInfo(
+                                        context.getString(
+                                                R.string.input_switch_input_language_previous),
+                                        KeyEvent.KEYCODE_SPACE,
+                                        KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON),
+                                        null))),
+                /* Access emoji: Meta + . */
+                new ShortcutMultiMappingInfo(
+                        context.getString(R.string.input_access_emoji),
+                        null,
+                        Arrays.asList(
+                                new ShortcutKeyGroup(new KeyboardShortcutInfo(
+                                        context.getString(R.string.input_access_emoji),
+                                        KeyEvent.KEYCODE_PERIOD,
+                                        KeyEvent.META_META_ON),
+                                        null))),
+                /* Access voice typing: Meta + V */
+                new ShortcutMultiMappingInfo(
+                        context.getString(R.string.input_access_voice_typing),
+                        null,
+                        Arrays.asList(
+                                new ShortcutKeyGroup(new KeyboardShortcutInfo(
+                                        context.getString(R.string.input_access_voice_typing),
+                                        KeyEvent.KEYCODE_V, KeyEvent.META_META_ON),
+                                        null)))
+        );
+        return new KeyboardShortcutMultiMappingGroup(
+                context.getString(R.string.keyboard_shortcut_group_input),
+                shortcutMultiMappingInfoList);
+    }
+
+    private KeyboardShortcutMultiMappingGroup getDefaultMultiMappingApplicationShortcuts() {
+        final int userId = mContext.getUserId();
+        PackageInfo assistPackageInfo = getAssistPackageInfo(mContext, mPackageManager, userId);
+        CharSequence categoryTitle =
+                mContext.getString(R.string.keyboard_shortcut_group_applications);
+        List<ShortcutMultiMappingInfo> shortcutMultiMappingInfos = new ArrayList<>();
+
+        String[] intentCategories = {
+                Intent.CATEGORY_APP_BROWSER,
+                Intent.CATEGORY_APP_CONTACTS,
+                Intent.CATEGORY_APP_EMAIL,
+                Intent.CATEGORY_APP_CALENDAR,
+                Intent.CATEGORY_APP_MAPS,
+                Intent.CATEGORY_APP_MUSIC,
+                Intent.CATEGORY_APP_MESSAGING,
+                Intent.CATEGORY_APP_CALCULATOR,
+
+        };
+        String[] shortcutLabels = {
+                mContext.getString(R.string.keyboard_shortcut_group_applications_browser),
+                mContext.getString(R.string.keyboard_shortcut_group_applications_contacts),
+                mContext.getString(R.string.keyboard_shortcut_group_applications_email),
+                mContext.getString(R.string.keyboard_shortcut_group_applications_calendar),
+                mContext.getString(R.string.keyboard_shortcut_group_applications_maps),
+                mContext.getString(R.string.keyboard_shortcut_group_applications_music),
+                mContext.getString(R.string.keyboard_shortcut_group_applications_sms),
+                mContext.getString(R.string.keyboard_shortcut_group_applications_calculator)
+        };
+        int[] keyCodes = {
+                KeyEvent.KEYCODE_B,
+                KeyEvent.KEYCODE_C,
+                KeyEvent.KEYCODE_E,
+                KeyEvent.KEYCODE_K,
+                KeyEvent.KEYCODE_M,
+                KeyEvent.KEYCODE_P,
+                KeyEvent.KEYCODE_S,
+                KeyEvent.KEYCODE_U,
+        };
+
+        // Assist.
+        if (assistPackageInfo != null) {
+            if (assistPackageInfo != null) {
+                final Icon assistIcon = Icon.createWithResource(
+                        assistPackageInfo.applicationInfo.packageName,
+                        assistPackageInfo.applicationInfo.icon);
+                CharSequence assistLabel =
+                        mContext.getString(R.string.keyboard_shortcut_group_applications_assist);
+                KeyboardShortcutInfo assistShortcutInfo = new KeyboardShortcutInfo(
+                        assistLabel,
+                        assistIcon,
+                        KeyEvent.KEYCODE_A,
+                        KeyEvent.META_META_ON);
+                shortcutMultiMappingInfos.add(
+                        new ShortcutMultiMappingInfo(
+                                assistLabel,
+                                assistIcon,
+                                Arrays.asList(new ShortcutKeyGroup(assistShortcutInfo, null))));
+            }
+        }
+
+        // Browser (Chrome as default): Meta + B
+        // Contacts: Meta + C
+        // Email (Gmail as default): Meta + E
+        // Gmail: Meta + G
+        // Calendar: Meta + K
+        // Maps: Meta + M
+        // Music: Meta + P
+        // SMS: Meta + S
+        // Calculator: Meta + U
+        for (int i = 0; i < shortcutLabels.length; i++) {
+            final Icon icon = getIconForIntentCategory(intentCategories[i], userId);
+            if (icon != null) {
+                CharSequence label =
+                        shortcutLabels[i];
+                KeyboardShortcutInfo keyboardShortcutInfo = new KeyboardShortcutInfo(
+                        label,
+                        icon,
+                        keyCodes[i],
+                        KeyEvent.META_META_ON);
+                List<ShortcutKeyGroup> shortcutKeyGroups =
+                        Arrays.asList(new ShortcutKeyGroup(keyboardShortcutInfo, null));
+                shortcutMultiMappingInfos.add(
+                        new ShortcutMultiMappingInfo(label, icon, shortcutKeyGroups));
+            }
+        }
+
+        Comparator<ShortcutMultiMappingInfo> applicationItemsComparator =
+                new Comparator<ShortcutMultiMappingInfo>() {
+                    @Override
+                    public int compare(
+                            ShortcutMultiMappingInfo ksh1, ShortcutMultiMappingInfo ksh2) {
+                        boolean ksh1ShouldBeLast = ksh1.getLabel() == null
+                                || ksh1.getLabel().toString().isEmpty();
+                        boolean ksh2ShouldBeLast = ksh2.getLabel() == null
+                                || ksh2.getLabel().toString().isEmpty();
+                        if (ksh1ShouldBeLast && ksh2ShouldBeLast) {
+                            return 0;
+                        }
+                        if (ksh1ShouldBeLast) {
+                            return 1;
+                        }
+                        if (ksh2ShouldBeLast) {
+                            return -1;
+                        }
+                        return (ksh1.getLabel().toString()).compareToIgnoreCase(
+                                ksh2.getLabel().toString());
+                    }
+                };
+        // Sorts by label, case insensitive with nulls and/or empty labels last.
+        Collections.sort(shortcutMultiMappingInfos, applicationItemsComparator);
+        return new KeyboardShortcutMultiMappingGroup(categoryTitle, shortcutMultiMappingInfos);
+    }
+
+    private Icon getIconForIntentCategory(String intentCategory, int userId) {
+        final Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(intentCategory);
+
+        final PackageInfo packageInfo = getPackageInfoForIntent(intent, userId);
+        if (packageInfo != null && packageInfo.applicationInfo.icon != 0) {
+            return Icon.createWithResource(
+                    packageInfo.applicationInfo.packageName,
+                    packageInfo.applicationInfo.icon);
+        }
+        return null;
+    }
+
+    private PackageInfo getPackageInfoForIntent(Intent intent, int userId) {
+        try {
+            ResolveInfo handler;
+            handler = mPackageManager.resolveIntent(
+                    intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), 0, userId);
+            if (handler == null || handler.activityInfo == null) {
+                return null;
+            }
+            return mPackageManager.getPackageInfo(handler.activityInfo.packageName, 0, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "PackageManagerService is dead", e);
+            return null;
+        }
+    }
+
+    private void showKeyboardShortcutSearchList(
+            List<List<KeyboardShortcutMultiMappingGroup>> keyboardShortcutMultiMappingGroupList) {
+        // Need to post on the main thread.
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                handleShowKeyboardShortcutSearchList(keyboardShortcutMultiMappingGroupList);
+            }
+        });
+    }
+
+    private void handleShowKeyboardShortcutSearchList(
+            List<List<KeyboardShortcutMultiMappingGroup>> keyboardShortcutMultiMappingGroupList) {
+        mQueryString = null;
+        LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
+        mKeyboardShortcutsBottomSheetDialog =
+                new BottomSheetDialog(mContext);
+        final View keyboardShortcutsView = inflater.inflate(
+                R.layout.keyboard_shortcuts_search_view, null);
+        mNoSearchResults = keyboardShortcutsView.findViewById(R.id.shortcut_search_no_result);
+        mKeyboardShortcutsBottomSheetDialog.setContentView(keyboardShortcutsView);
+        setButtonsDefaultStatus(keyboardShortcutsView);
+        populateKeyboardShortcutSearchList(
+                keyboardShortcutsView.findViewById(R.id.keyboard_shortcuts_container));
+
+        // Workaround for solve issue about dialog not full expanded when landscape.
+        FrameLayout bottomSheet = (FrameLayout)
+                mKeyboardShortcutsBottomSheetDialog.findViewById(
+                        com.google.android.material.R.id.design_bottom_sheet);
+        if (bottomSheet != null) {
+            bottomSheet.setBackgroundResource(android.R.color.transparent);
+        }
+
+        BottomSheetBehavior<FrameLayout> behavior = BottomSheetBehavior.from(bottomSheet);
+        behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+        behavior.setSkipCollapsed(true);
+
+        mKeyboardShortcutsBottomSheetDialog.setCanceledOnTouchOutside(true);
+        Window keyboardShortcutsWindow = mKeyboardShortcutsBottomSheetDialog.getWindow();
+        keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG);
+        synchronized (sLock) {
+            // show KeyboardShortcutsBottomSheetDialog only if it has not been dismissed already
+            if (sInstance != null) {
+                mKeyboardShortcutsBottomSheetDialog.show();
+                setDialogScreenSize();
+                keyboardShortcutsView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+                    @Override
+                    public void onLayoutChange(View v, int left, int top, int right,
+                            int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                        setDialogScreenSize();
+                    }
+                });
+            }
+        }
+        mSearchEditText = keyboardShortcutsView.findViewById(R.id.keyboard_shortcuts_search);
+        mSearchEditText.addTextChangedListener(
+                new TextWatcher() {
+                    @Override
+                    public void afterTextChanged(Editable s) {
+                        mQueryString = s.toString();
+                        populateKeyboardShortcutSearchList(
+                                keyboardShortcutsView.findViewById(
+                                        R.id.keyboard_shortcuts_container));
+                    }
+
+                    @Override
+                    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+                        // Do nothing.
+                    }
+
+                    @Override
+                    public void onTextChanged(CharSequence s, int start, int before, int count) {
+                        // Do nothing.
+                    }
+                });
+        mEditTextCancel = keyboardShortcutsView.findViewById(R.id.keyboard_shortcuts_search_cancel);
+        mEditTextCancel.setOnClickListener(v -> mSearchEditText.setText(null));
+    }
+
+    private void populateKeyboardShortcutSearchList(LinearLayout keyboardShortcutsLayout) {
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        TextView shortcutsKeyView = (TextView) inflater.inflate(
+                R.layout.keyboard_shortcuts_key_view, keyboardShortcutsLayout, false);
+        shortcutsKeyView.measure(
+                View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+        final int shortcutKeyTextItemMinWidth = shortcutsKeyView.getMeasuredHeight();
+        // Needed to be able to scale the image items to the same height as the text items.
+        final int shortcutKeyIconItemHeightWidth = shortcutsKeyView.getMeasuredHeight()
+                - shortcutsKeyView.getPaddingTop()
+                - shortcutsKeyView.getPaddingBottom();
+        keyboardShortcutsLayout.removeAllViews();
+
+        // Search if user's input is contained in any shortcut groups.
+        if (mQueryString != null) {
+            for (int i = 0; i < mFullShortsGroup.size(); i++) {
+                mKeySearchResultMap.put(i, false);
+                for (KeyboardShortcutMultiMappingGroup group : mFullShortsGroup.get(i)) {
+                    for (ShortcutMultiMappingInfo info : group.getItems()) {
+                        String itemLabel = info.getLabel().toString();
+                        if (itemLabel.toUpperCase(Locale.getDefault()).contains(
+                                mQueryString.toUpperCase(Locale.getDefault()))) {
+                            mKeySearchResultMap.put(i, true);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        // Set default color for the non-focus categories.
+        for (int i = 0; i < mKeySearchResultMap.size(); i++) {
+            if (mKeySearchResultMap.get(i)) {
+                mFullButtonList.get(i).setVisibility(View.VISIBLE);
+                setButtonFocusColor(i, false);
+            } else {
+                mFullButtonList.get(i).setVisibility(View.GONE);
+            }
+        }
+
+        // Move the focus to the suitable category.
+        if (mFullButtonList.get(mCurrentCategoryIndex).getVisibility() == View.GONE) {
+            for (int i = 0; i < mKeySearchResultMap.size(); i++) {
+                if (mKeySearchResultMap.get(i)) {
+                    setCurrentCategoryIndex(i);
+                    break;
+                }
+            }
+        }
+
+        // Set color for the current focus category
+        setButtonFocusColor(mCurrentCategoryIndex, true);
+
+        // Load shortcuts for current focus category.
+        List<KeyboardShortcutMultiMappingGroup> keyboardShortcutMultiMappingGroups =
+                mFullShortsGroup.get(mCurrentCategoryIndex);
+
+        int keyboardShortcutGroupsSize = keyboardShortcutMultiMappingGroups.size();
+        List<Boolean> groupSearchResult = new ArrayList<>();
+        for (int i = 0; i < keyboardShortcutGroupsSize; i++) {
+            View separator = inflater.inflate(
+                    R.layout.keyboard_shortcuts_category_short_separator,
+                    keyboardShortcutsLayout,
+                    false);
+
+            // If there are more than one category, add separators among categories.
+            if (i > 0) {
+                keyboardShortcutsLayout.addView(separator);
+            }
+
+            List<Boolean> itemSearchResult = new ArrayList<>();
+            KeyboardShortcutMultiMappingGroup categoryGroup =
+                    keyboardShortcutMultiMappingGroups.get(i);
+            TextView categoryTitle = (TextView) inflater.inflate(
+                    R.layout.keyboard_shortcuts_category_title, keyboardShortcutsLayout, false);
+            categoryTitle.setText(categoryGroup.getCategory());
+            keyboardShortcutsLayout.addView(categoryTitle);
+            LinearLayout shortcutContainer = (LinearLayout) inflater.inflate(
+                    R.layout.keyboard_shortcuts_container, keyboardShortcutsLayout, false);
+            final int itemsSize = categoryGroup.getItems().size();
+            for (int j = 0; j < itemsSize; j++) {
+                ShortcutMultiMappingInfo keyGroupInfo = categoryGroup.getItems().get(j);
+
+                if (mQueryString != null) {
+                    String shortcutLabel =
+                            keyGroupInfo.getLabel().toString().toUpperCase(Locale.getDefault());
+                    String queryString = mQueryString.toUpperCase(Locale.getDefault());
+                    if (!shortcutLabel.contains(queryString)) {
+                        itemSearchResult.add(j, false);
+                        continue;
+                    } else {
+                        itemSearchResult.add(j, true);
+                    }
+                }
+
+                View shortcutView = inflater.inflate(R.layout.keyboard_shortcut_app_item,
+                        shortcutContainer, false);
+                TextView shortcutKeyword =
+                        shortcutView.findViewById(R.id.keyboard_shortcuts_keyword);
+                shortcutKeyword.setText(keyGroupInfo.getLabel());
+
+                if (keyGroupInfo.getIcon() != null) {
+                    ImageView shortcutIcon =
+                            shortcutView.findViewById(R.id.keyboard_shortcuts_icon);
+                    shortcutIcon.setImageIcon(keyGroupInfo.getIcon());
+                    shortcutIcon.setVisibility(View.VISIBLE);
+                    RelativeLayout.LayoutParams lp =
+                            (RelativeLayout.LayoutParams) shortcutKeyword.getLayoutParams();
+                    lp.removeRule(RelativeLayout.ALIGN_PARENT_START);
+                    shortcutKeyword.setLayoutParams(lp);
+                }
+
+                ViewGroup shortcutItemsContainer =
+                        shortcutView.findViewById(R.id.keyboard_shortcuts_item_container);
+                final int keyGroupItemsSize = keyGroupInfo.getShortcutKeyGroups().size();
+                for (int p = 0; p < keyGroupItemsSize; p++) {
+                    KeyboardShortcutInfo keyboardShortcutInfo =
+                            keyGroupInfo.getShortcutKeyGroups().get(p).getKeyboardShortcutInfo();
+                    String complexCommand =
+                            keyGroupInfo.getShortcutKeyGroups().get(p).getComplexCommand();
+
+                    if (complexCommand == null) {
+                        List<StringDrawableContainer> shortcutKeys =
+                                getHumanReadableShortcutKeys(keyboardShortcutInfo);
+                        if (shortcutKeys == null) {
+                            // Ignore shortcuts we can't display keys for.
+                            Log.w(TAG, "Keyboard Shortcut contains unsupported keys, skipping.");
+                            continue;
+                        }
+                        final int shortcutKeysSize = shortcutKeys.size();
+                        for (int k = 0; k < shortcutKeysSize; k++) {
+                            StringDrawableContainer shortcutRepresentation = shortcutKeys.get(k);
+                            if (shortcutRepresentation.mDrawable != null) {
+                                ImageView shortcutKeyIconView = (ImageView) inflater.inflate(
+                                        R.layout.keyboard_shortcuts_key_new_icon_view,
+                                        shortcutItemsContainer,
+                                        false);
+                                Bitmap bitmap = Bitmap.createBitmap(shortcutKeyIconItemHeightWidth,
+                                        shortcutKeyIconItemHeightWidth, Bitmap.Config.ARGB_8888);
+                                Canvas canvas = new Canvas(bitmap);
+                                shortcutRepresentation.mDrawable.setBounds(0, 0, canvas.getWidth(),
+                                        canvas.getHeight());
+                                shortcutRepresentation.mDrawable.draw(canvas);
+                                shortcutKeyIconView.setImageBitmap(bitmap);
+                                shortcutKeyIconView.setImportantForAccessibility(
+                                        IMPORTANT_FOR_ACCESSIBILITY_YES);
+                                shortcutKeyIconView.setAccessibilityDelegate(
+                                        new ShortcutKeyAccessibilityDelegate(
+                                                shortcutRepresentation.mString));
+                                shortcutItemsContainer.addView(shortcutKeyIconView);
+                            } else if (shortcutRepresentation.mString != null) {
+                                TextView shortcutKeyTextView = (TextView) inflater.inflate(
+                                        R.layout.keyboard_shortcuts_key_new_view,
+                                        shortcutItemsContainer,
+                                        false);
+                                shortcutKeyTextView.setMinimumWidth(shortcutKeyTextItemMinWidth);
+                                shortcutKeyTextView.setText(shortcutRepresentation.mString);
+                                shortcutKeyTextView.setAccessibilityDelegate(
+                                        new ShortcutKeyAccessibilityDelegate(
+                                                shortcutRepresentation.mString));
+                                shortcutItemsContainer.addView(shortcutKeyTextView);
+                            }
+
+                            if (k < shortcutKeysSize - 1) {
+                                TextView shortcutKeyTextView = (TextView) inflater.inflate(
+                                        R.layout.keyboard_shortcuts_key_plus_view,
+                                        shortcutItemsContainer,
+                                        false);
+                                shortcutItemsContainer.addView(shortcutKeyTextView);
+                            }
+                        }
+                    } else {
+                        TextView shortcutKeyTextView = (TextView) inflater.inflate(
+                                R.layout.keyboard_shortcuts_key_new_view,
+                                shortcutItemsContainer,
+                                false);
+                        shortcutKeyTextView.setMinimumWidth(shortcutKeyTextItemMinWidth);
+                        shortcutKeyTextView.setText(complexCommand);
+                        shortcutKeyTextView.setAccessibilityDelegate(
+                                new ShortcutKeyAccessibilityDelegate(complexCommand));
+                        shortcutItemsContainer.addView(shortcutKeyTextView);
+                    }
+
+                    if (p < keyGroupItemsSize - 1) {
+                        TextView shortcutKeyTextView = (TextView) inflater.inflate(
+                                R.layout.keyboard_shortcuts_key_vertical_bar_view,
+                                shortcutItemsContainer,
+                                false);
+                        shortcutItemsContainer.addView(shortcutKeyTextView);
+                    }
+                }
+                shortcutContainer.addView(shortcutView);
+            }
+
+            if (!groupSearchResult.isEmpty() && !groupSearchResult.get(i - 1)) {
+                keyboardShortcutsLayout.removeView(separator);
+            }
+
+            if (!itemSearchResult.isEmpty() && !itemSearchResult.contains(true)) {
+                // No results, so remove the category title and separator
+                keyboardShortcutsLayout.removeView(categoryTitle);
+                keyboardShortcutsLayout.removeView(separator);
+                groupSearchResult.add(false);
+                if (i == keyboardShortcutGroupsSize - 1 && !groupSearchResult.contains(true)) {
+                    // show "No shortcut found"
+                    mNoSearchResults.setVisibility(View.VISIBLE);
+                }
+                continue;
+            }
+            groupSearchResult.add(true);
+            mNoSearchResults.setVisibility(View.GONE);
+            keyboardShortcutsLayout.addView(shortcutContainer);
+        }
+    }
+
+    private List<StringDrawableContainer> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
+        List<StringDrawableContainer> shortcutKeys = getHumanReadableModifiers(info);
+        if (shortcutKeys == null) {
+            return null;
+        }
+        String shortcutKeyString = null;
+        Drawable shortcutKeyDrawable = null;
+        if (info.getBaseCharacter() > Character.MIN_VALUE) {
+            shortcutKeyString = String.valueOf(info.getBaseCharacter());
+        } else if (mSpecialCharacterDrawables.get(info.getKeycode()) != null) {
+            shortcutKeyDrawable = mSpecialCharacterDrawables.get(info.getKeycode());
+            shortcutKeyString = mSpecialCharacterNames.get(info.getKeycode());
+        } else if (mSpecialCharacterNames.get(info.getKeycode()) != null) {
+            shortcutKeyString = mSpecialCharacterNames.get(info.getKeycode());
+        } else {
+            // Special case for shortcuts with no base key or keycode.
+            if (info.getKeycode() == KeyEvent.KEYCODE_UNKNOWN) {
+                return shortcutKeys;
+            }
+            char displayLabel = mKeyCharacterMap.getDisplayLabel(info.getKeycode());
+            if (displayLabel != 0) {
+                shortcutKeyString = String.valueOf(displayLabel);
+            } else {
+                displayLabel = mBackupKeyCharacterMap.getDisplayLabel(info.getKeycode());
+                if (displayLabel != 0) {
+                    shortcutKeyString = String.valueOf(displayLabel);
+                } else {
+                    return null;
+                }
+            }
+        }
+
+        if (shortcutKeyString != null) {
+            shortcutKeys.add(new StringDrawableContainer(shortcutKeyString, shortcutKeyDrawable));
+        } else {
+            Log.w(TAG, "Keyboard Shortcut does not have a text representation, skipping.");
+        }
+
+        return shortcutKeys;
+    }
+
+    private List<StringDrawableContainer> getHumanReadableModifiers(KeyboardShortcutInfo info) {
+        final List<StringDrawableContainer> shortcutKeys = new ArrayList<>();
+        int modifiers = info.getModifiers();
+        if (modifiers == 0) {
+            return shortcutKeys;
+        }
+        for (int supportedModifier : mModifierList) {
+            if ((modifiers & supportedModifier) != 0) {
+                shortcutKeys.add(new StringDrawableContainer(
+                        mModifierNames.get(supportedModifier),
+                        mModifierDrawables.get(supportedModifier)));
+                modifiers &= ~supportedModifier;
+            }
+        }
+        if (modifiers != 0) {
+            // Remaining unsupported modifiers, don't show anything.
+            return null;
+        }
+        return shortcutKeys;
+    }
+
+    private final class ShortcutKeyAccessibilityDelegate extends AccessibilityDelegate {
+        private String mContentDescription;
+
+        ShortcutKeyAccessibilityDelegate(String contentDescription) {
+            mContentDescription = contentDescription;
+        }
+
+        @Override
+        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(host, info);
+            if (mContentDescription != null) {
+                info.setContentDescription(mContentDescription.toLowerCase(Locale.getDefault()));
+            }
+        }
+    }
+
+    private static final class StringDrawableContainer {
+        @NonNull
+        public String mString;
+        @Nullable
+        public Drawable mDrawable;
+
+        StringDrawableContainer(String string, Drawable drawable) {
+            mString = string;
+            mDrawable = drawable;
+        }
+    }
+
+    private void setDialogScreenSize() {
+        Window window = mKeyboardShortcutsBottomSheetDialog.getWindow();
+        Display display = mWindowManager.getDefaultDisplay();
+        WindowManager.LayoutParams lp =
+                mKeyboardShortcutsBottomSheetDialog.getWindow().getAttributes();
+        if (mContext.getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_PORTRAIT) {
+            lp.width = (int) (display.getWidth() * 0.8);
+            lp.height = (int) (display.getHeight() * 0.7);
+        } else {
+            lp.width = (int) (display.getWidth() * 0.7);
+            lp.height = (int) (display.getHeight() * 0.8);
+        }
+        window.setGravity(Gravity.BOTTOM);
+        window.setAttributes(lp);
+    }
+
+    private void setCurrentCategoryIndex(int index) {
+        mCurrentCategoryIndex = index;
+    }
+
+    private void setButtonsDefaultStatus(View keyboardShortcutsView) {
+        mButtonSystem = keyboardShortcutsView.findViewById(R.id.shortcut_system);
+        mButtonInput = keyboardShortcutsView.findViewById(R.id.shortcut_input);
+        mButtonOpenApps = keyboardShortcutsView.findViewById(R.id.shortcut_open_apps);
+        mButtonSpecificApp = keyboardShortcutsView.findViewById(R.id.shortcut_specific_app);
+
+        mButtonSystem.setOnClickListener(v -> {
+            setCurrentCategoryIndex(SHORTCUT_SYSTEM_INDEX);
+            populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(
+                    R.id.keyboard_shortcuts_container));
+        });
+
+        mButtonInput.setOnClickListener(v -> {
+            setCurrentCategoryIndex(SHORTCUT_INPUT_INDEX);
+            populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(
+                    R.id.keyboard_shortcuts_container));
+        });
+
+        mButtonOpenApps.setOnClickListener(v -> {
+            setCurrentCategoryIndex(SHORTCUT_OPENAPPS_INDEX);
+            populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(
+                    R.id.keyboard_shortcuts_container));
+        });
+
+        mButtonSpecificApp.setOnClickListener(v -> {
+            setCurrentCategoryIndex(SHORTCUT_SPECIFICAPP_INDEX);
+            populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(
+                    R.id.keyboard_shortcuts_container));
+        });
+
+        mFullButtonList.add(mButtonSystem);
+        mFullButtonList.add(mButtonInput);
+        mFullButtonList.add(mButtonOpenApps);
+        mFullButtonList.add(mButtonSpecificApp);
+    }
+
+    private void setButtonFocusColor(int i, boolean isFocused) {
+        if (isFocused) {
+            mFullButtonList.get(i).setTextColor(getColorOfTextColorOnAccent());
+            mFullButtonList.get(i).setBackground(
+                    mContext.getDrawable(R.drawable.shortcut_button_focus_colored));
+        } else {
+            // Default color
+            mFullButtonList.get(i).setTextColor(getColorOfTextColorSecondary());
+            mFullButtonList.get(i).setBackground(
+                    mContext.getDrawable(R.drawable.shortcut_button_colored));
+        }
+    }
+
+    private int getColorOfTextColorOnAccent() {
+        return Utils.getColorAttrDefaultColor(
+                mContext, com.android.internal.R.attr.textColorOnAccent);
+    }
+
+    private int getColorOfTextColorSecondary() {
+        return Utils.getColorAttrDefaultColor(
+                mContext, com.android.internal.R.attr.textColorSecondary);
+    }
+
+    // Create the new data structure for handling the N-to-1 key mapping and other complex case.
+    private static class KeyboardShortcutMultiMappingGroup {
+        private final CharSequence mCategory;
+        private List<ShortcutMultiMappingInfo> mItems;
+
+        KeyboardShortcutMultiMappingGroup(
+                CharSequence category, List<ShortcutMultiMappingInfo> items) {
+            mCategory = category;
+            mItems = items;
+        }
+
+        void addItem(ShortcutMultiMappingInfo item) {
+            mItems.add(item);
+        }
+
+        CharSequence getCategory() {
+            return mCategory;
+        }
+
+        List<ShortcutMultiMappingInfo> getItems() {
+            return mItems;
+        }
+    }
+
+    private static class ShortcutMultiMappingInfo {
+        private final CharSequence mLabel;
+        private final Icon mIcon;
+        private List<ShortcutKeyGroup> mShortcutKeyGroups;
+
+        ShortcutMultiMappingInfo(
+                CharSequence label, Icon icon, List<ShortcutKeyGroup> shortcutKeyGroups) {
+            mLabel = label;
+            mIcon = icon;
+            mShortcutKeyGroups = shortcutKeyGroups;
+        }
+
+        ShortcutMultiMappingInfo(KeyboardShortcutInfo info) {
+            mLabel = info.getLabel();
+            mIcon = info.getIcon();
+            mShortcutKeyGroups = Arrays.asList(new ShortcutKeyGroup(info, null));
+        }
+
+        CharSequence getLabel() {
+            return mLabel;
+        }
+
+        Icon getIcon() {
+            return mIcon;
+        }
+
+        List<ShortcutKeyGroup> getShortcutKeyGroups() {
+            return mShortcutKeyGroups;
+        }
+    }
+
+    private static class ShortcutKeyGroup {
+        private final KeyboardShortcutInfo mKeyboardShortcutInfo;
+        private final String mComplexCommand;
+
+        ShortcutKeyGroup(KeyboardShortcutInfo keyboardShortcutInfo, String complexCommand) {
+            mKeyboardShortcutInfo = keyboardShortcutInfo;
+            mComplexCommand = complexCommand;
+        }
+
+        // To be compatible with the original functions, keep KeyboardShortcutInfo in here.
+        KeyboardShortcutInfo getKeyboardShortcutInfo() {
+            return mKeyboardShortcutInfo;
+        }
+
+        // In some case, the shortcut is a complex description not a N-to-1 key mapping.
+        String getComplexCommand() {
+            return mComplexCommand;
+        }
+    }
+
+    private static PackageInfo getAssistPackageInfo(
+            Context context, IPackageManager packageManager, int userId) {
+        AssistUtils assistUtils = new AssistUtils(context);
+        ComponentName assistComponent = assistUtils.getAssistComponentForUser(userId);
+        // Not all devices have an assist component.
+        PackageInfo assistPackageInfo = null;
+        if (assistComponent != null) {
+            try {
+                assistPackageInfo = packageManager.getPackageInfo(
+                        assistComponent.getPackageName(), 0, userId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "PackageManagerService is dead");
+            }
+        }
+        return assistPackageInfo;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 7e6ddcf..f20f929 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -63,6 +63,7 @@
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.AssistUtils;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
@@ -80,7 +81,8 @@
 public final class KeyboardShortcuts {
     private static final String TAG = KeyboardShortcuts.class.getSimpleName();
     private static final Object sLock = new Object();
-    private static KeyboardShortcuts sInstance;
+    @VisibleForTesting static KeyboardShortcuts sInstance;
+    private WindowManager mWindowManager;
 
     private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
     private final SparseArray<String> mModifierNames = new SparseArray<>();
@@ -94,7 +96,7 @@
     };
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
-    private final Context mContext;
+    @VisibleForTesting Context mContext;
     private final IPackageManager mPackageManager;
     private final OnClickListener mDialogCloseListener = new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int id) {
@@ -123,20 +125,26 @@
                 }
             };
 
-    private Dialog mKeyboardShortcutsDialog;
+    @VisibleForTesting Dialog mKeyboardShortcutsDialog;
     private KeyCharacterMap mKeyCharacterMap;
     private KeyCharacterMap mBackupKeyCharacterMap;
 
-    private KeyboardShortcuts(Context context) {
+    @VisibleForTesting
+    KeyboardShortcuts(Context context, WindowManager windowManager) {
         this.mContext = new ContextThemeWrapper(
                 context, android.R.style.Theme_DeviceDefault_Settings);
         this.mPackageManager = AppGlobals.getPackageManager();
+        if (windowManager != null) {
+            this.mWindowManager = windowManager;
+        } else {
+            this.mWindowManager = mContext.getSystemService(WindowManager.class);
+        }
         loadResources(context);
     }
 
     private static KeyboardShortcuts getInstance(Context context) {
         if (sInstance == null) {
-            sInstance = new KeyboardShortcuts(context);
+            sInstance = new KeyboardShortcuts(context, null);
         }
         return sInstance;
     }
@@ -371,10 +379,10 @@
         mKeyCharacterMap = mBackupKeyCharacterMap;
     }
 
-    private void showKeyboardShortcuts(int deviceId) {
+    @VisibleForTesting
+    void showKeyboardShortcuts(int deviceId) {
         retrieveKeyCharacterMap(deviceId);
-        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        wm.requestAppKeyboardShortcuts(new KeyboardShortcutsReceiver() {
+        mWindowManager.requestAppKeyboardShortcuts(new KeyboardShortcutsReceiver() {
             @Override
             public void onKeyboardShortcutsReceived(
                     final List<KeyboardShortcutGroup> result) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
index 8a5bece..e9fac28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
@@ -19,16 +19,38 @@
 import android.content.Context;
 import android.content.Intent;
 
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.shared.recents.utilities.Utilities;
+
+import javax.inject.Inject;
+
 /**
  * Receiver for the Keyboard Shortcuts Helper.
  */
 public class KeyboardShortcutsReceiver extends BroadcastReceiver {
+
+    private boolean mIsShortcutListSearchEnabled;
+
+    @Inject
+    public KeyboardShortcutsReceiver(FeatureFlags featureFlags) {
+        mIsShortcutListSearchEnabled = featureFlags.isEnabled(Flags.SHORTCUT_LIST_SEARCH_LAYOUT);
+    }
+
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
-            KeyboardShortcuts.show(context, -1 /* deviceId unknown */);
-        } else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
-            KeyboardShortcuts.dismiss();
+        if (mIsShortcutListSearchEnabled && Utilities.isTablet(context)) {
+            if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
+                KeyboardShortcutListSearch.show(context, -1 /* deviceId unknown */);
+            } else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
+                KeyboardShortcutListSearch.dismiss();
+            }
+        } else {
+            if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
+                KeyboardShortcuts.show(context, -1 /* deviceId unknown */);
+            } else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
+                KeyboardShortcuts.dismiss();
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 1966a66..6a52a33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -186,11 +186,13 @@
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CircleReveal;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.KeyboardShortcutListSearch;
 import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.LiftReveal;
@@ -299,6 +301,7 @@
     private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks;
     private float mTransitionToFullShadeProgress = 0f;
     private NotificationListContainer mNotifListContainer;
+    private boolean mIsShortcutListSearchEnabled;
 
     private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
             new KeyguardStateController.Callback() {
@@ -833,6 +836,7 @@
         mCameraLauncherLazy = cameraLauncherLazy;
         mAlternateBouncerInteractor = alternateBouncerInteractor;
         mUserTracker = userTracker;
+        mIsShortcutListSearchEnabled = featureFlags.isEnabled(Flags.SHORTCUT_LIST_SEARCH_LAYOUT);
 
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
         mStartingSurfaceOptional = startingSurfaceOptional;
@@ -2546,7 +2550,11 @@
             String action = intent.getAction();
             String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
-                KeyboardShortcuts.dismiss();
+                if (mIsShortcutListSearchEnabled && Utilities.isTablet(mContext)) {
+                    KeyboardShortcutListSearch.dismiss();
+                } else {
+                    KeyboardShortcuts.dismiss();
+                }
                 mRemoteInputManager.closeRemoteInputs();
                 if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
                     int flags = CommandQueue.FLAG_EXCLUDE_NONE;
@@ -3893,11 +3901,19 @@
     }
 
     protected void toggleKeyboardShortcuts(int deviceId) {
-        KeyboardShortcuts.toggle(mContext, deviceId);
+        if (mIsShortcutListSearchEnabled && Utilities.isTablet(mContext)) {
+            KeyboardShortcutListSearch.toggle(mContext, deviceId);
+        } else {
+            KeyboardShortcuts.toggle(mContext, deviceId);
+        }
     }
 
     protected void dismissKeyboardShortcuts() {
-        KeyboardShortcuts.dismiss();
+        if (mIsShortcutListSearchEnabled && Utilities.isTablet(mContext)) {
+            KeyboardShortcutListSearch.dismiss();
+        } else {
+            KeyboardShortcuts.dismiss();
+        }
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
new file mode 100644
index 0000000..109f185
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+
+import com.google.android.material.bottomsheet.BottomSheetDialog;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KeyboardShortcutListSearchTest extends SysuiTestCase {
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+
+    private static int DEVICE_ID = 1;
+    private KeyboardShortcutListSearch mKeyboardShortcutListSearch;
+
+    @Mock private BottomSheetDialog mBottomSheetDialog;
+    @Mock WindowManager mWindowManager;
+
+    @Before
+    public void setUp() {
+        mKeyboardShortcutListSearch = new KeyboardShortcutListSearch(mContext, mWindowManager);
+        mKeyboardShortcutListSearch.sInstance = mKeyboardShortcutListSearch;
+        mKeyboardShortcutListSearch.mKeyboardShortcutsBottomSheetDialog = mBottomSheetDialog;
+        mKeyboardShortcutListSearch.mContext = mContext;
+    }
+
+    @Test
+    public void toggle_isShowingTrue_instanceShouldBeNull() {
+        when(mBottomSheetDialog.isShowing()).thenReturn(true);
+
+        mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+
+        assertThat(mKeyboardShortcutListSearch.sInstance).isNull();
+    }
+
+    @Test
+    public void toggle_isShowingFalse_showKeyboardShortcuts() {
+        when(mBottomSheetDialog.isShowing()).thenReturn(false);
+
+        mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+
+        verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java
new file mode 100644
index 0000000..bea2cfb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.shared.recents.utilities.Utilities;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KeyboardShortcutsReceiverTest extends SysuiTestCase {
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+
+    private KeyboardShortcutsReceiver mKeyboardShortcutsReceiver;
+    private Intent mIntent;
+    private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+
+    @Mock private KeyboardShortcuts mKeyboardShortcuts;
+    @Mock private KeyboardShortcutListSearch mKeyboardShortcutListSearch;
+
+    @Before
+    public void setUp() {
+        mIntent = new Intent(Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS);
+        mKeyboardShortcuts.mContext = mContext;
+        mKeyboardShortcutListSearch.mContext = mContext;
+        KeyboardShortcuts.sInstance = mKeyboardShortcuts;
+        KeyboardShortcutListSearch.sInstance = mKeyboardShortcutListSearch;
+    }
+
+    @Test
+    public void onReceive_whenFlagOffDeviceIsTablet_showKeyboardShortcuts() {
+        MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+                .spyStatic(Utilities.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
+        mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
+        when(Utilities.isTablet(mContext)).thenReturn(true);
+
+        mKeyboardShortcutsReceiver.onReceive(mContext, mIntent);
+
+        verify(mKeyboardShortcuts).showKeyboardShortcuts(anyInt());
+        verify(mKeyboardShortcutListSearch, never()).showKeyboardShortcuts(anyInt());
+        mockitoSession.finishMocking();
+    }
+
+    @Test
+    public void onReceive_whenFlagOffDeviceIsNotTablet_showKeyboardShortcuts() {
+        MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+                .spyStatic(Utilities.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
+        mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
+        when(Utilities.isTablet(mContext)).thenReturn(false);
+
+        mKeyboardShortcutsReceiver.onReceive(mContext, mIntent);
+
+        verify(mKeyboardShortcuts).showKeyboardShortcuts(anyInt());
+        verify(mKeyboardShortcutListSearch, never()).showKeyboardShortcuts(anyInt());
+        mockitoSession.finishMocking();
+    }
+
+    @Test
+    public void onReceive_whenFlagOnDeviceIsTablet_showKeyboardShortcutListSearch() {
+        MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+                .spyStatic(Utilities.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, true);
+        mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
+        when(Utilities.isTablet(mContext)).thenReturn(true);
+
+        mKeyboardShortcutsReceiver.onReceive(mContext, mIntent);
+
+        verify(mKeyboardShortcuts, never()).showKeyboardShortcuts(anyInt());
+        verify(mKeyboardShortcutListSearch).showKeyboardShortcuts(anyInt());
+        mockitoSession.finishMocking();
+    }
+
+    @Test
+    public void onReceive_whenFlagOnDeviceIsNotTablet_showKeyboardShortcuts() {
+        MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+                .spyStatic(Utilities.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, true);
+        mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
+        when(Utilities.isTablet(mContext)).thenReturn(false);
+
+        mKeyboardShortcutsReceiver.onReceive(mContext, mIntent);
+
+        verify(mKeyboardShortcuts).showKeyboardShortcuts(anyInt());
+        verify(mKeyboardShortcutListSearch, never()).showKeyboardShortcuts(anyInt());
+        mockitoSession.finishMocking();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
new file mode 100644
index 0000000..ea822aa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Dialog;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KeyboardShortcutsTest extends SysuiTestCase {
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+
+    private static int DEVICE_ID = 1;
+    private KeyboardShortcuts mKeyboardShortcuts;
+
+    @Mock private Dialog mDialog;
+    @Mock WindowManager mWindowManager;
+
+    @Before
+    public void setUp() {
+        mKeyboardShortcuts = new KeyboardShortcuts(mContext, mWindowManager);
+        mKeyboardShortcuts.sInstance = mKeyboardShortcuts;
+        mKeyboardShortcuts.mKeyboardShortcutsDialog = mDialog;
+        mKeyboardShortcuts.mContext = mContext;
+    }
+
+    @Test
+    public void toggle_isShowingTrue_instanceShouldBeNull() {
+        when(mDialog.isShowing()).thenReturn(true);
+
+        mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
+
+        assertThat(mKeyboardShortcuts.sInstance).isNull();
+    }
+
+    @Test
+    public void toggle_isShowingFalse_showKeyboardShortcuts() {
+        when(mDialog.isShowing()).thenReturn(false);
+
+        mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
+
+        verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 0605398..8b0d4ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -327,6 +327,8 @@
         // CentralSurfacesImpl's runtime flag check fails if the flag is absent.
         // This value is unused, because test manifest is opted in.
         mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
+        // Set default value to avoid IllegalStateException.
+        mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
 
         IThermalService thermalService = mock(IThermalService.class);
         mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,