am 933e297f: am 15333d64: am b5d8d228: am 030f1bdf: Merge "New keyboard shortcuts" into ub-gmail-ur14-dev

* commit '933e297f00298b14cffa4d8b7807c29b45892213':
  New keyboard shortcuts
diff --git a/res/menu-sw600dp-land/conversation_actions.xml b/res/menu-sw600dp-land/conversation_actions.xml
index 3819f48..4937b39 100644
--- a/res/menu-sw600dp-land/conversation_actions.xml
+++ b/res/menu-sw600dp-land/conversation_actions.xml
@@ -24,22 +24,23 @@
         android:title="@string/archive"
         app:showAsAction="always"
         android:icon="@drawable/ic_archive_wht_24dp"
-        android:alphabeticShortcut="@string/trigger_y_char" />
+        android:alphabeticShortcut="@string/trigger_archive_char" />
 
     <item
         android:id="@+id/remove_folder"
         android:title="@string/remove_folder"
         app:showAsAction="always"
         android:icon="@drawable/ic_remove_label_wht_24dp"
-        android:alphabeticShortcut="@string/trigger_y_char"
-        android:visible="false" />
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_archive_char" />
 
     <!-- Depends on FolderCapabilities.DELETE -->
     <item
         android:id="@+id/delete"
         android:title="@string/delete"
         app:showAsAction="always"
-        android:icon="@drawable/ic_delete_wht_24dp" />
+        android:icon="@drawable/ic_delete_wht_24dp"
+        android:alphabeticShortcut="@string/trigger_delete_char" />
 
     <!-- Depends on the user viewing a draft label, and the above menu item not being shown -->
     <item
@@ -47,14 +48,16 @@
         android:title="@string/discard_drafts"
         app:showAsAction="always"
         android:icon="@drawable/ic_delete_wht_24dp"
-        android:visible="false" />
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_delete_char" />
     <!-- Depends on the user viewing a outbox label and the setting supports delete -->
     <item
         android:id="@+id/discard_outbox"
         android:title="@string/discard_failed"
         app:showAsAction="always"
         android:icon="@drawable/ic_delete_wht_24dp"
-        android:visible="false" />
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_delete_char" />
 
     <item
         android:id="@+id/inside_conversation_unread"
@@ -73,7 +76,8 @@
         android:id="@+id/change_folders"
         android:title="@string/menu_change_folders"
         app:showAsAction="never"
-        android:icon="@drawable/ic_change_labels_wht_24dp" />
+        android:icon="@drawable/ic_change_labels_wht_24dp"
+        android:alphabeticShortcut="@string/trigger_change_label_char" />
 
     <item
         android:id="@+id/move_to_inbox"
@@ -97,8 +101,9 @@
         app:showAsAction="never" />
 
     <item android:id="@+id/print_all"
-          android:title="@string/print_all"
-          android:visible="false" />
+        android:title="@string/print_all"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_print_char" />
 
     <item
         android:id="@+id/show_original"
@@ -136,4 +141,40 @@
         android:visible="false"
         android:alphabeticShortcut="@string/trigger_refresh_char" />
 
+    <item
+        android:id="@+id/reply"
+        android:title="@string/reply"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_reply_char" />
+
+    <item
+        android:id="@+id/reply_all"
+        android:title="@string/reply_all"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_reply_all_char" />
+
+    <item
+        android:id="@+id/toggle_read_unread"
+        android:title="@string/toggle_read_unread"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_toggle_read_unread_char" />
+
+    <item
+        android:id="@+id/toggle_drawer"
+        android:title="@string/menu_toggle_drawer"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_toggle_drawer_char" />
+
+    <item
+        android:id="@+id/settings"
+        android:title="@string/menu_settings"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_settings_char" />
+
+    <item
+        android:id="@+id/help_info_menu_item"
+        android:title="@string/help_and_feedback"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_help_char" />
+
 </menu>
diff --git a/res/menu-sw600dp-land/conversation_list_search_results_actions.xml b/res/menu-sw600dp-land/conversation_list_search_results_actions.xml
index 6f813a1..412de85 100644
--- a/res/menu-sw600dp-land/conversation_list_search_results_actions.xml
+++ b/res/menu-sw600dp-land/conversation_list_search_results_actions.xml
@@ -22,7 +22,8 @@
         android:id="@+id/delete"
         android:title="@string/delete"
         app:showAsAction="always"
-        android:icon="@drawable/ic_delete_wht_24dp" />
+        android:icon="@drawable/ic_delete_wht_24dp"
+        android:alphabeticShortcut="@string/trigger_delete_char" />
 
     <item
         android:id="@+id/inside_conversation_unread"
@@ -34,7 +35,8 @@
         android:id="@+id/change_folders"
         android:title="@string/menu_change_folders"
         app:showAsAction="never"
-        android:icon="@drawable/ic_change_labels_wht_24dp" />
+        android:icon="@drawable/ic_change_labels_wht_24dp"
+        android:alphabeticShortcut="@string/trigger_change_label_char" />
 
     <item
         android:id="@+id/mark_important"
@@ -58,9 +60,34 @@
         android:visible="false"
         android:alphabeticShortcut="@string/trigger_compose_char" />
 
-    <item android:id="@+id/refresh"
+    <item
+        android:id="@+id/refresh"
         android:title="@string/refresh"
         android:visible="false"
         android:alphabeticShortcut="@string/trigger_refresh_char" />
 
+    <item
+        android:id="@+id/toggle_read_unread"
+        android:title="@string/toggle_read_unread"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_toggle_read_unread_char" />
+
+    <item
+        android:id="@+id/toggle_drawer"
+        android:title="@string/menu_toggle_drawer"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_toggle_drawer_char" />
+
+    <item
+        android:id="@+id/settings"
+        android:title="@string/menu_settings"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_settings_char" />
+
+    <item
+        android:id="@+id/help_info_menu_item"
+        android:title="@string/help_and_feedback"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_help_char" />
+
 </menu>
diff --git a/res/menu/account_feedback_menu.xml b/res/menu/account_feedback_menu.xml
index 81e0ddf..4789bb3 100644
--- a/res/menu/account_feedback_menu.xml
+++ b/res/menu/account_feedback_menu.xml
@@ -24,7 +24,8 @@
         android:id="@+id/settings"
         android:title="@string/menu_settings"
         android:menuCategory="secondary"
-        app:showAsAction="never" />
+        app:showAsAction="never"
+        android:alphabeticShortcut="@string/trigger_settings_char" />
 
     <!-- Available if the account specifies a help url -->
     <item
@@ -32,5 +33,6 @@
         android:icon="@android:drawable/ic_menu_help"
         android:menuCategory="secondary"
         app:showAsAction="never"
-        android:title="@string/help_and_feedback" />
+        android:title="@string/help_and_feedback"
+        android:alphabeticShortcut="@string/trigger_help_char" />
 </menu>
\ No newline at end of file
diff --git a/res/menu/compose_menu.xml b/res/menu/compose_menu.xml
index 693afdc..31663e2 100644
--- a/res/menu/compose_menu.xml
+++ b/res/menu/compose_menu.xml
@@ -47,22 +47,26 @@
     <item android:id="@+id/send"
         android:icon="@drawable/ic_send_wht_24dp"
         android:title="@string/send"
-        app:showAsAction="always" />
+        app:showAsAction="always"
+        android:alphabeticShortcut="@string/trigger_send_char" />
 
     <item android:id="@+id/save"
         android:title="@string/save_draft"
-        app:showAsAction="never" />
+        app:showAsAction="never"
+        android:alphabeticShortcut="@string/trigger_save_char" />
 
     <item android:id="@+id/discard"
         android:title="@string/discard"
-        app:showAsAction="never" />
+        app:showAsAction="never"
+        android:alphabeticShortcut="@string/trigger_delete_char" />
 
     <item android:id="@+id/settings"
         android:title="@string/menu_settings"
-        app:showAsAction="never" />
+        app:showAsAction="never"
+        android:alphabeticShortcut="@string/trigger_settings_char" />
 
-    <item
-        android:id="@+id/help_info_menu_item"
-        android:title="@string/help_and_feedback" />
+    <item android:id="@+id/help_info_menu_item"
+        android:title="@string/help_and_feedback"
+        android:alphabeticShortcut="@string/trigger_help_char" />
 
 </menu>
diff --git a/res/menu/conversation_actions.xml b/res/menu/conversation_actions.xml
index 53efb5a..620169a 100644
--- a/res/menu/conversation_actions.xml
+++ b/res/menu/conversation_actions.xml
@@ -25,22 +25,23 @@
         android:title="@string/archive"
         app:showAsAction="always"
         android:icon="@drawable/ic_archive_wht_24dp"
-        android:alphabeticShortcut="@string/trigger_y_char" />
+        android:alphabeticShortcut="@string/trigger_archive_char" />
 
     <item
         android:id="@+id/remove_folder"
         android:title="@string/remove_folder"
         app:showAsAction="always"
         android:icon="@drawable/ic_remove_label_wht_24dp"
-        android:alphabeticShortcut="@string/trigger_y_char"
-        android:visible="false" />
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_archive_char" />
 
     <!-- Depends on FolderCapabilities.DELETE -->
     <item
         android:id="@+id/delete"
         android:title="@string/delete"
         app:showAsAction="always"
-        android:icon="@drawable/ic_delete_wht_24dp" />
+        android:icon="@drawable/ic_delete_wht_24dp"
+        android:alphabeticShortcut="@string/trigger_delete_char" />
 
     <!-- Depends on the user viewing a draft label, and the above menu item not being shown -->
     <item
@@ -48,14 +49,16 @@
         android:title="@string/discard_drafts"
         app:showAsAction="always"
         android:icon="@drawable/ic_delete_wht_24dp"
-        android:visible="false" />
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_delete_char" />
     <!-- Depends on the user viewing a outbox label and the setting supports delete -->
     <item
         android:id="@+id/discard_outbox"
         android:title="@string/discard_failed"
         app:showAsAction="always"
         android:icon="@drawable/ic_delete_wht_24dp"
-        android:visible="false" />
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_delete_char" />
 
     <!-- Always available -->
     <item
@@ -76,7 +79,8 @@
         android:id="@+id/change_folders"
         android:title="@string/menu_change_folders"
         app:showAsAction="never"
-        android:icon="@drawable/ic_change_labels_wht_24dp" />
+        android:icon="@drawable/ic_change_labels_wht_24dp"
+        android:alphabeticShortcut="@string/trigger_change_label_char" />
 
     <item
         android:id="@+id/move_to_inbox"
@@ -101,8 +105,9 @@
         android:title="@string/mute" />
 
     <item android:id="@+id/print_all"
-          android:title="@string/print_all"
-          android:visible="false" />
+        android:title="@string/print_all"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_print_char" />
 
     <item
         android:id="@+id/show_original"
@@ -123,4 +128,47 @@
         android:title="@string/report_phishing"
         android:visible="false" />
 
+    <!-- These invisible menu items are used to enable keyboard shortcuts -->
+    <item
+        android:id="@+id/compose"
+        android:title="@string/menu_compose"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_compose_char" />
+
+    <item
+        android:id="@+id/reply"
+        android:title="@string/reply"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_reply_char" />
+
+    <item
+        android:id="@+id/reply_all"
+        android:title="@string/reply_all"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_reply_all_char" />
+
+    <item
+        android:id="@+id/toggle_read_unread"
+        android:title="@string/toggle_read_unread"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_toggle_read_unread_char" />
+
+    <item
+        android:id="@+id/toggle_drawer"
+        android:title="@string/menu_toggle_drawer"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_toggle_drawer_char" />
+
+    <item
+        android:id="@+id/settings"
+        android:title="@string/menu_settings"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_settings_char" />
+
+    <item
+        android:id="@+id/help_info_menu_item"
+        android:title="@string/help_and_feedback"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_help_char" />
+
 </menu>
diff --git a/res/menu/conversation_list_menu.xml b/res/menu/conversation_list_menu.xml
index fc4896a..3d574d6 100644
--- a/res/menu/conversation_list_menu.xml
+++ b/res/menu/conversation_list_menu.xml
@@ -23,13 +23,15 @@
     <item
         android:id="@+id/empty_trash"
         android:title="@string/empty_trash"
-        app:showAsAction="never" />
+        app:showAsAction="never"
+        android:alphabeticShortcut="@string/trigger_delete_char" />
 
     <!-- Available only in the spam folder, when the account supports emptying it -->
     <item
         android:id="@+id/empty_spam"
         android:title="@string/empty_spam"
-        app:showAsAction="never" />
+        app:showAsAction="never"
+        android:alphabeticShortcut="@string/trigger_delete_char" />
 
     <!-- Available only for accounts with SERVER_SEARCH and in a folder
       that supports FOLDER_SERVER_SEARCH -->
@@ -45,9 +47,33 @@
         android:visible="false"
         android:alphabeticShortcut="@string/trigger_compose_char" />
 
-    <item android:id="@+id/refresh"
+    <item
+        android:id="@+id/refresh"
         android:title="@string/refresh"
         android:visible="false"
         android:alphabeticShortcut="@string/trigger_refresh_char" />
 
+    <item
+        android:id="@+id/toggle_read_unread"
+        android:title="@string/toggle_read_unread"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_toggle_read_unread_char" />
+
+    <item
+        android:id="@+id/toggle_drawer"
+        android:title="@string/menu_toggle_drawer"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_toggle_drawer_char" />
+
+    <item
+        android:id="@+id/settings"
+        android:title="@string/menu_settings"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_settings_char" />
+
+    <item
+        android:id="@+id/help_info_menu_item"
+        android:title="@string/help_and_feedback"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_help_char" />
 </menu>
diff --git a/res/menu/conversation_list_search_results_actions.xml b/res/menu/conversation_list_search_results_actions.xml
index 662c1e8..337f2c9 100644
--- a/res/menu/conversation_list_search_results_actions.xml
+++ b/res/menu/conversation_list_search_results_actions.xml
@@ -26,9 +26,28 @@
         android:visible="false"
         android:alphabeticShortcut="@string/trigger_compose_char" />
 
-    <item android:id="@+id/refresh"
+    <item
+        android:id="@+id/refresh"
         android:title="@string/refresh"
         android:visible="false"
         android:alphabeticShortcut="@string/trigger_refresh_char" />
 
+    <item
+        android:id="@+id/toggle_read_unread"
+        android:title="@string/toggle_read_unread"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_toggle_read_unread_char" />
+
+    <item
+        android:id="@+id/settings"
+        android:title="@string/menu_settings"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_settings_char" />
+
+    <item
+        android:id="@+id/help_info_menu_item"
+        android:title="@string/help_and_feedback"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_help_char" />
+
 </menu>
diff --git a/res/menu/conversation_list_selection_actions_menu.xml b/res/menu/conversation_list_selection_actions_menu.xml
index ac8f050..0aa811e 100644
--- a/res/menu/conversation_list_selection_actions_menu.xml
+++ b/res/menu/conversation_list_selection_actions_menu.xml
@@ -24,15 +24,13 @@
         android:id="@+id/archive"
         android:title="@string/archive"
         app:showAsAction="always"
-        android:icon="@drawable/ic_archive_wht_24dp"
-        android:alphabeticShortcut="@string/trigger_y_char"/>
+        android:icon="@drawable/ic_archive_wht_24dp" />
 
     <item
         android:id="@+id/remove_folder"
         android:title="@string/remove_folder"
         app:showAsAction="always"
         android:icon="@drawable/ic_remove_label_wht_24dp"
-        android:alphabeticShortcut="@string/trigger_y_char"
         android:visible="false" />
 
     <!-- Depends on FolderCapabilities.DELETE -->
@@ -40,7 +38,7 @@
         android:id="@+id/delete"
         android:title="@string/delete"
         app:showAsAction="always"
-        android:icon="@drawable/ic_delete_wht_24dp"/>
+        android:icon="@drawable/ic_delete_wht_24dp" />
 
     <!-- Depends on the user viewing a draft label, and the above menu item not being shown -->
     <item
diff --git a/res/menu/eml_fragment_menu.xml b/res/menu/eml_fragment_menu.xml
index 11624f0..3efcf07 100644
--- a/res/menu/eml_fragment_menu.xml
+++ b/res/menu/eml_fragment_menu.xml
@@ -18,7 +18,7 @@
 
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:id="@+id/print_message"
-          android:title="@string/print"
-          android:showAsAction="never" />
-
+        android:title="@string/print"
+        android:showAsAction="never"
+        android:alphabeticShortcut="@string/trigger_print_char" />
 </menu>
\ No newline at end of file
diff --git a/res/menu/general_prefs_fragment_menu.xml b/res/menu/general_prefs_fragment_menu.xml
index 030c765..5b2e9d9 100644
--- a/res/menu/general_prefs_fragment_menu.xml
+++ b/res/menu/general_prefs_fragment_menu.xml
@@ -30,7 +30,8 @@
     <item
         android:id="@+id/help_info_menu_item"
         android:icon="@android:drawable/ic_menu_help"
-        android:title="@string/help_and_feedback"/>
+        android:title="@string/help_and_feedback"
+        android:alphabeticShortcut="@string/trigger_help_char" />
 
     <!-- TODO add help menu item, once help support has been moved to UnifiedEmail -->
 </menu>
diff --git a/res/menu/help_menu.xml b/res/menu/help_menu.xml
index d7d1d29..9994e47 100644
--- a/res/menu/help_menu.xml
+++ b/res/menu/help_menu.xml
@@ -21,7 +21,8 @@
           android:showAsAction="never"/>
     <item android:id="@+id/print_dialog"
           android:title="@string/print_dialog"
-          android:showAsAction="never"/>
+          android:showAsAction="never"
+          android:alphabeticShortcut="@string/trigger_print_char" />
     <item android:id="@+id/copyright_information"
           android:title="@string/copyright_information"
           android:showAsAction="never"/>
diff --git a/res/menu/photo_view_menu.xml b/res/menu/photo_view_menu.xml
index 3969575..9d15daa 100644
--- a/res/menu/photo_view_menu.xml
+++ b/res/menu/photo_view_menu.xml
@@ -21,7 +21,8 @@
         <item
             android:id="@+id/menu_save"
             android:showAsAction="never"
-            android:title="@string/save_attachment"/>
+            android:title="@string/save_attachment"
+            android:alphabeticShortcut="@string/trigger_save_char" />
         <item
             android:id="@+id/menu_save_all"
             android:showAsAction="never"
@@ -41,7 +42,8 @@
         <item
             android:id="@+id/menu_print"
             android:showAsAction="never"
-            android:title="@string/menu_photo_print"/>
+            android:title="@string/menu_photo_print"
+            android:alphabeticShortcut="@string/trigger_print_char" />
         <item
             android:id="@+id/menu_download_again"
             android:showAsAction="never"
diff --git a/res/menu/vacation_responder_menu.xml b/res/menu/vacation_responder_menu.xml
index 184b24d..35b0c1f 100644
--- a/res/menu/vacation_responder_menu.xml
+++ b/res/menu/vacation_responder_menu.xml
@@ -17,12 +17,10 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:app="http://schemas.android.com/apk/res-auto">
     <item android:id="@+id/action_cancel"
-          android:alphabeticShortcut="c"
           android:title="@string/cancel"
           android:icon="@drawable/ic_cancel_holo_light"
           app:showAsAction="withText|always" />
     <item android:id="@+id/action_done"
-          android:alphabeticShortcut="d"
           android:title="@string/done"
           android:icon="@drawable/ic_menu_done_holo_light"
           app:showAsAction="withText|always" />
diff --git a/res/menu/wait_mode_actions.xml b/res/menu/wait_mode_actions.xml
index 65314ab..75a8ed1 100644
--- a/res/menu/wait_mode_actions.xml
+++ b/res/menu/wait_mode_actions.xml
@@ -23,4 +23,21 @@
         android:visible="false"
         android:alphabeticShortcut="@string/trigger_refresh_char" />
 
+    <item
+        android:id="@+id/toggle_drawer"
+        android:title="@string/menu_toggle_drawer"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_toggle_drawer_char" />
+
+    <item
+        android:id="@+id/settings"
+        android:title="@string/menu_settings"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_settings_char" />
+
+    <item
+        android:id="@+id/help_info_menu_item"
+        android:title="@string/help_and_feedback"
+        android:visible="false"
+        android:alphabeticShortcut="@string/trigger_help_char" />
 </menu>
diff --git a/res/values/shortcut_keys.xml b/res/values/shortcut_keys.xml
index c360f51..78be21d 100644
--- a/res/values/shortcut_keys.xml
+++ b/res/values/shortcut_keys.xml
@@ -15,15 +15,39 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
+    <!-- All shortcuts need to be combined with CTRL -->
 
-    <!-- Character, that when combined with CTRL, should cause a save action -->
+    <!-- open compose -->
+    <string name="trigger_compose_char" translatable="false">n</string>
+    <!-- archive, can also be remove folder when archive is not applicable -->
+    <string name="trigger_archive_char" translatable="false">d</string>
+    <!-- delete (\u0008 is BACKSPACE), can also be clear trash/spam -->
+    <!-- TODO: there is a separate ASCII for DEL, should we support that? -->
+    <string name="trigger_delete_char" translatable="false">\u0008</string>
+    <!-- send (TODO: looks like edittext is consuming this event) -->
+    <string name="trigger_send_char" translatable="false">\u000a</string>
+    <!-- save draft -->
     <string name="trigger_save_char" translatable="false">s</string>
-    <!-- Character, that when combined with CTRL, should open compose -->
-    <string name="trigger_compose_char" translatable="false">c</string>
-    <!-- Character, that when combined with CTRL, should trigger a refresh -->
+    <!-- manual refresh -->
     <string name="trigger_refresh_char" translatable="false">u</string>
-    <!-- Character, that when combined with CTRL, should trigger the Y button action (sometimes archive, sometimes delete, etc) -->
-    <string name="trigger_y_char" translatable="false">y</string>
+    <!-- open navigation drawer -->
+    <string name="trigger_toggle_drawer_char" translatable="false">m</string>
+    <!-- Settings -->
+    <string name="trigger_settings_char" translatable="false">,</string>
+    <!-- Help & Feedback -->
+    <string name="trigger_help_char" translatable="false">/</string>
+    <!-- reply TODO -->
+    <string name="trigger_reply_char" translatable="false">`</string>
+    <!-- reply all -->
+    <string name="trigger_reply_all_char" translatable="false">r</string>
+    <!-- mark as read/unread -->
+    <string name="trigger_toggle_read_unread_char" translatable="false">i</string>
+    <!-- change label -->
+    <string name="trigger_change_label_char" translatable="false">l</string>
+    <!-- print -->
+    <string name="trigger_print_char" translatable="false">p</string>
+    <!-- undo in snack bar TODO punted -->
+    <string name="trigger_undo_char" translatable="false">z</string>
 
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0cf62c3..29d8a91 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -108,6 +108,8 @@
     <string name="mark_read">Mark read</string>
     <!-- Menu item: mark this conversation as unread -->
     <string name="mark_unread">Mark unread</string>
+    <!-- Menu item: toggle read/unread for menus that contain both read and unread options -->
+    <string name="toggle_read_unread">Toggle read unread</string>
     <!--  Menu item: mute this conversation -->
     <string name="mute">Mute</string>
     <!-- Menu item: add a star to this conversation -->
@@ -171,6 +173,8 @@
     <string name="menu_settings">Settings</string>
     <!-- Menu item: search through the Gmail inbox -->
     <string name="menu_search">Search</string>
+    <!-- Menu item: toggle drawer -->
+    <string name="menu_toggle_drawer">Toggle drawer</string>
     <!-- Title of the drawer, indicating what it is used for, which is navigation of the app -->
     <string name="drawer_title">Navigation</string>
 
diff --git a/src/com/android/mail/analytics/AnalyticsUtils.java b/src/com/android/mail/analytics/AnalyticsUtils.java
index b968ad7..4d16e30 100644
--- a/src/com/android/mail/analytics/AnalyticsUtils.java
+++ b/src/com/android/mail/analytics/AnalyticsUtils.java
@@ -46,12 +46,12 @@
             s = "report_spam";
         } else if (id == R.id.mark_not_spam) {
             s = "mark_not_spam";
-        } else if (id == R.id.report_phishing) {
-            s = "report_phishing";
         } else if (id == R.id.compose) {
             s = "compose";
         } else if (id == R.id.refresh) {
             s = "refresh";
+        } else if (id == R.id.toggle_drawer) {
+            s = "toggle_drawer";
         } else if (id == R.id.settings) {
             s = "settings";
         } else if (id == R.id.help_info_menu_item) {
@@ -76,6 +76,8 @@
             s = "mark_read";
         } else if (id == R.id.unread) {
             s = "mark_unread";
+        } else if (id == R.id.toggle_read_unread) {
+            s = "toggle_read_unread";
         } else if (id == R.id.show_original) {
             s = "show_original";
         } else if (id == R.id.add_file_attachment) {
diff --git a/src/com/android/mail/browse/SelectedConversationsActionMenu.java b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
index 7293a36..5ebf7ce 100644
--- a/src/com/android/mail/browse/SelectedConversationsActionMenu.java
+++ b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
@@ -111,6 +111,10 @@
         mUpdater = activity.getConversationUpdater();
     }
 
+    public boolean onActionItemClicked(MenuItem item) {
+        return onActionItemClicked(mActionMode, item);
+    }
+
     @Override
     public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
         boolean handled = true;
@@ -160,6 +164,10 @@
             markConversationsRead(false);
         } else if (itemId == R.id.star) {
             starConversations(true);
+        } else if (itemId == R.id.toggle_read_unread) {
+            if (mActionMode != null) {
+                markConversationsRead(mActionMode.getMenu().findItem(R.id.read).isVisible());
+            }
         } else if (itemId == R.id.remove_star) {
             if (mFolder.isType(UIProvider.FolderType.STARRED)) {
                 LogUtils.d(LOG_TAG, "We are in a starred folder, removing the star");
@@ -396,6 +404,7 @@
         read.setVisible(!showMarkUnread);
         final MenuItem unread = menu.findItem(R.id.unread);
         unread.setVisible(showMarkUnread);
+
         // We only ever show one of:
         // 1) remove folder
         // 2) archive
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index 390afc8..9cf7e56 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -1521,7 +1521,6 @@
 
         final int id = item.getItemId();
         LogUtils.d(LOG_TAG, "AbstractController.onOptionsItemSelected(%d) called.", id);
-        boolean handled = true;
         /** This is NOT a batch action. */
         final boolean isBatch = false;
         final Collection<Conversation> target = Conversation.listOf(mCurrentConversation);
@@ -1532,95 +1531,121 @@
         final UndoCallback undoCallback = getUndoCallbackForDestructiveActionsWithAutoAdvance(
                 id, mCurrentConversation);
 
-        if (id == R.id.archive) {
-            final boolean showDialog = (settings != null && settings.confirmArchive);
-            confirmAndDelete(id, target, showDialog, R.plurals.confirm_archive_conversation, undoCallback);
-        } else if (id == R.id.remove_folder) {
-            delete(R.id.remove_folder, target,
-                    getDeferredRemoveFolder(target, mFolder, true, isBatch, true, undoCallback),
-                    isBatch);
-        } else if (id == R.id.delete) {
-            final boolean showDialog = (settings != null && settings.confirmDelete);
-            confirmAndDelete(id, target, showDialog, R.plurals.confirm_delete_conversation, undoCallback);
-        } else if (id == R.id.discard_drafts) {
-            // drafts are lost forever, so always confirm
-            confirmAndDelete(id, target, true /* showDialog */,
-                    R.plurals.confirm_discard_drafts_conversation, undoCallback);
-        } else if (id == R.id.discard_outbox) {
-            // discard in outbox means we discard the failed message and save them in drafts
-            delete(id, target, getDeferredAction(id, target, isBatch, undoCallback), isBatch);
-        } else if (id == R.id.mark_important) {
-            updateConversation(Conversation.listOf(mCurrentConversation),
-                    ConversationColumns.PRIORITY, UIProvider.ConversationPriority.HIGH);
-        } else if (id == R.id.mark_not_important) {
-            if (mFolder != null && mFolder.isImportantOnly()) {
-                delete(R.id.mark_not_important, target,
-                        getDeferredAction(R.id.mark_not_important, target, isBatch, undoCallback),
+        // Menu items that are targetted, only perform if there actually is a target.
+        boolean handled = false;
+        if (target.size() > 0) {
+            handled = true;
+            if (id == R.id.archive) {
+                final boolean showDialog = (settings != null && settings.confirmArchive);
+                confirmAndDelete(id, target, showDialog, R.plurals.confirm_archive_conversation,
+                        undoCallback);
+            } else if (id == R.id.remove_folder) {
+                delete(R.id.remove_folder, target,
+                        getDeferredRemoveFolder(target, mFolder, true, isBatch, true, undoCallback),
                         isBatch);
-            } else {
+            } else if (id == R.id.delete) {
+                final boolean showDialog = (settings != null && settings.confirmDelete);
+                confirmAndDelete(id, target, showDialog, R.plurals.confirm_delete_conversation,
+                        undoCallback);
+            } else if (id == R.id.discard_drafts) {
+                // drafts are lost forever, so always confirm
+                confirmAndDelete(id, target, true /* showDialog */,
+                        R.plurals.confirm_discard_drafts_conversation, undoCallback);
+            } else if (id == R.id.discard_outbox) {
+                // discard in outbox means we discard the failed message and save them in drafts
+                delete(id, target, getDeferredAction(id, target, isBatch, undoCallback), isBatch);
+            } else if (id == R.id.mark_important) {
                 updateConversation(Conversation.listOf(mCurrentConversation),
-                        ConversationColumns.PRIORITY, UIProvider.ConversationPriority.LOW);
-            }
-        } else if (id == R.id.mute) {
-            delete(R.id.mute, target, getDeferredAction(R.id.mute, target, isBatch, undoCallback),
-                    isBatch);
-        } else if (id == R.id.report_spam) {
-            delete(R.id.report_spam, target,
-                    getDeferredAction(R.id.report_spam, target, isBatch, undoCallback), isBatch);
-        } else if (id == R.id.mark_not_spam) {
-            // Currently, since spam messages are only shown in list with
-            // other spam messages,
-            // marking a message not as spam is a destructive action
-            delete(R.id.mark_not_spam, target,
-                    getDeferredAction(R.id.mark_not_spam, target, isBatch, undoCallback), isBatch);
-        } else if (id == R.id.report_phishing) {
-            delete(R.id.report_phishing, target,
-                    getDeferredAction(R.id.report_phishing, target, isBatch, undoCallback), isBatch);
-        } else if (id == android.R.id.home) {
-            handleUpPress();
-        } else if (id == R.id.compose) {
-            ComposeActivity.compose(mActivity.getActivityContext(), mAccount);
-        } else if (id == R.id.refresh) {
-            requestFolderRefresh();
-        } else if (id == R.id.settings) {
-            Utils.showSettings(mActivity.getActivityContext(), mAccount);
-        } else if (id == R.id.help_info_menu_item) {
-            mActivity.showHelp(mAccount, mViewMode.getMode());
-        } else if (id == R.id.move_to || id == R.id.change_folders) {
-            final FolderSelectionDialog dialog = FolderSelectionDialog.getInstance(mAccount,
-                    Conversation.listOf(mCurrentConversation), isBatch, mFolder,
-                    id == R.id.move_to);
-            if (dialog != null) {
-                dialog.show(mActivity.getFragmentManager(), null);
-            }
-        } else if (id == R.id.move_to_inbox) {
-            new AsyncTask<Void, Void, Folder>() {
-                @Override
-                protected Folder doInBackground(final Void... params) {
-                    // Get the "move to" inbox
-                    return Utils.getFolder(mContext, mAccount.settings.moveToInbox,
-                            true /* allowHidden */);
+                        ConversationColumns.PRIORITY, UIProvider.ConversationPriority.HIGH);
+            } else if (id == R.id.mark_not_important) {
+                if (mFolder != null && mFolder.isImportantOnly()) {
+                    delete(R.id.mark_not_important, target,
+                            getDeferredAction(R.id.mark_not_important, target, isBatch, undoCallback),
+                            isBatch);
+                } else {
+                    updateConversation(target, ConversationColumns.PRIORITY,
+                            UIProvider.ConversationPriority.LOW);
                 }
+            } else if (id == R.id.mute) {
+                delete(R.id.mute, target, getDeferredAction(R.id.mute, target, isBatch, undoCallback),
+                        isBatch);
+            } else if (id == R.id.report_spam) {
+                delete(R.id.report_spam, target,
+                        getDeferredAction(R.id.report_spam, target, isBatch, undoCallback),
+                        isBatch);
+            } else if (id == R.id.mark_not_spam) {
+                // Currently, since spam messages are only shown in list with
+                // other spam messages,
+                // marking a message not as spam is a destructive action
+                delete(R.id.mark_not_spam, target,
+                        getDeferredAction(R.id.mark_not_spam, target, isBatch, undoCallback),
+                        isBatch);
+            } else if (id == R.id.report_phishing) {
+                delete(R.id.report_phishing, target,
+                        getDeferredAction(R.id.report_phishing, target, isBatch, undoCallback),
+                        isBatch);
+            } else if (id == R.id.move_to || id == R.id.change_folders) {
+                final FolderSelectionDialog dialog = FolderSelectionDialog.getInstance(mAccount,
+                        target, isBatch, mFolder, id == R.id.move_to);
+                if (dialog != null) {
+                    dialog.show(mActivity.getFragmentManager(), null);
+                }
+            } else if (id == R.id.move_to_inbox) {
+                new AsyncTask<Void, Void, Folder>() {
+                    @Override
+                    protected Folder doInBackground(final Void... params) {
+                        // Get the "move to" inbox
+                        return Utils.getFolder(mContext, mAccount.settings.moveToInbox,
+                                true /* allowHidden */);
+                    }
 
-                @Override
-                protected void onPostExecute(final Folder moveToInbox) {
-                    final List<FolderOperation> ops = Lists.newArrayListWithCapacity(1);
-                    // Add inbox
-                    ops.add(new FolderOperation(moveToInbox, true));
-                    assignFolder(ops, Conversation.listOf(mCurrentConversation), true,
-                            true /* showUndo */, false /* isMoveTo */);
-                }
-            }.execute((Void[]) null);
-        } else if (id == R.id.empty_trash) {
-            showEmptyDialog();
-        } else if (id == R.id.empty_spam) {
-            showEmptyDialog();
-        } else if (id == R.id.search) {
-            mSearchViewController.showSearchActionBar(
-                    MaterialSearchViewController.SEARCH_VIEW_STATE_VISIBLE);
-        } else {
-            handled = false;
+                    @Override
+                    protected void onPostExecute(final Folder moveToInbox) {
+                        final List<FolderOperation> ops = Lists.newArrayListWithCapacity(1);
+                        // Add inbox
+                        ops.add(new FolderOperation(moveToInbox, true));
+                        assignFolder(ops, target, true, true /* showUndo */, false /* isMoveTo */);
+                    }
+                }.execute((Void[]) null);
+            } else {
+                handled = false;
+            }
         }
+
+        // Not handled by the targetted menu items, check the general ones.
+        if (!handled) {
+            handled = true;
+            if (id == android.R.id.home) {
+                handleUpPress();
+            } else if (id == R.id.compose) {
+                ComposeActivity.compose(mActivity.getActivityContext(), mAccount);
+            } else if (id == R.id.refresh) {
+                requestFolderRefresh();
+            } else if (id == R.id.toggle_drawer) {
+                toggleDrawerState();
+            } else if (id == R.id.settings) {
+                Utils.showSettings(mActivity.getActivityContext(), mAccount);
+            } else if (id == R.id.help_info_menu_item) {
+                mActivity.showHelp(mAccount, mViewMode.getMode());
+            } else if (id == R.id.empty_trash) {
+                showEmptyDialog();
+            } else if (id == R.id.empty_spam) {
+                showEmptyDialog();
+            } else if (id == R.id.search) {
+                mSearchViewController.showSearchActionBar(
+                        MaterialSearchViewController.SEARCH_VIEW_STATE_VISIBLE);
+            } else {
+                handled = false;
+            }
+        }
+
+        // If the controller didn't handle this event, check the CAB menu if it's active.
+        // This is necessary because keyboard shortcuts don't seem to check CAB menus.
+        if (!handled && mCabActionMenu != null && mCabActionMenu.isActivated() &&
+                    mCabActionMenu.onActionItemClicked(item)) {
+            handled = true;
+        }
+
         return handled;
     }
 
diff --git a/src/com/android/mail/ui/AbstractConversationViewFragment.java b/src/com/android/mail/ui/AbstractConversationViewFragment.java
index bf2e998..471f940 100644
--- a/src/com/android/mail/ui/AbstractConversationViewFragment.java
+++ b/src/com/android/mail/ui/AbstractConversationViewFragment.java
@@ -327,17 +327,20 @@
             return false;
         }
 
-        boolean handled = false;
+        boolean handled = true;
         final int itemId = item.getItemId();
-        if (itemId == R.id.inside_conversation_unread) {
+        if (itemId == R.id.inside_conversation_unread || itemId == R.id.toggle_read_unread) {
             markUnread();
-            handled = true;
         } else if (itemId == R.id.show_original) {
             showUntransformedConversation();
-            handled = true;
         } else if (itemId == R.id.print_all) {
             printConversation();
-            handled = true;
+        } else if (itemId == R.id.reply) {
+            handleReply();
+        } else if (itemId == R.id.reply_all) {
+            handleReplyAll();
+        } else {
+            handled = false;
         }
         return handled;
     }
@@ -707,6 +710,10 @@
      */
     protected abstract void printConversation();
 
+    // These methods should perform default reply/replyall action on the last message.
+    protected abstract void handleReply();
+    protected abstract void handleReplyAll();
+
     public boolean shouldAlwaysShowImages() {
         return (mAccount != null) && (mAccount.settings.showImages == Settings.ShowImages.ALWAYS);
     }
diff --git a/src/com/android/mail/ui/ActionBarController.java b/src/com/android/mail/ui/ActionBarController.java
index a9dc371..056de30 100644
--- a/src/com/android/mail/ui/ActionBarController.java
+++ b/src/com/android/mail/ui/ActionBarController.java
@@ -274,13 +274,10 @@
                 setConversationModeOptions(menu);
                 break;
             case ViewMode.CONVERSATION_LIST:
+            case ViewMode.SEARCH_RESULTS_LIST:
                 // The search menu item should only be visible for non-tablet devices
                 Utils.setMenuItemVisibility(menu, R.id.search,
                         mAccount.supportsSearch() && !mIsOnTablet);
-            case ViewMode.SEARCH_RESULTS_LIST:
-                // Hide compose and search
-                Utils.setMenuItemVisibility(menu, R.id.compose, false);
-                break;
         }
 
         return false;
diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java
index c53264b..25c1310 100644
--- a/src/com/android/mail/ui/ConversationViewFragment.java
+++ b/src/com/android/mail/ui/ConversationViewFragment.java
@@ -73,6 +73,7 @@
 import com.android.mail.browse.ScrollIndicatorsView;
 import com.android.mail.browse.SuperCollapsedBlock;
 import com.android.mail.browse.WebViewContextMenu;
+import com.android.mail.compose.ComposeActivity;
 import com.android.mail.content.ObjectCursor;
 import com.android.mail.print.PrintUtils;
 import com.android.mail.providers.Account;
@@ -841,12 +842,15 @@
     }
 
     private MessageHeaderItem getLastMessageHeaderItem() {
-        final int count = mAdapter.getCount();
-        if (count < 3) {
-            LogUtils.wtf(LOG_TAG, "not enough items in the adapter. count: %s", count);
-            return null;
+        int pos = mAdapter.getCount();
+        while (--pos >= 0) {
+            final ConversationOverlayItem item = mAdapter.getItem(pos);
+            if (item instanceof MessageHeaderItem) {
+                return (MessageHeaderItem) item;
+            }
         }
-        return (MessageHeaderItem) mAdapter.getItem(count - 2);
+        LogUtils.wtf(LOG_TAG, "No message header found");
+        return null;
     }
 
     private void renderSuperCollapsedBlock(int start, int end, boolean hasDraft) {
@@ -1806,4 +1810,26 @@
         PrintUtils.printConversation(mActivity.getActivityContext(), getMessageCursor(),
                 mAddressCache, mConversation.getBaseUri(mBaseUri), true /* useJavascript */);
     }
+
+    @Override
+    protected void handleReply() {
+        final MessageHeaderItem item = getLastMessageHeaderItem();
+        if (item != null) {
+            final ConversationMessage msg = item.getMessage();
+            if (msg != null) {
+                ComposeActivity.reply(getActivity(), mAccount, msg);
+            }
+        }
+    }
+
+    @Override
+    protected void handleReplyAll() {
+        final MessageHeaderItem item = getLastMessageHeaderItem();
+        if (item != null) {
+            final ConversationMessage msg = item.getMessage();
+            if (msg != null) {
+                ComposeActivity.replyAll(getActivity(), mAccount, msg);
+            }
+        }
+    }
 }
diff --git a/src/com/android/mail/ui/SecureConversationViewFragment.java b/src/com/android/mail/ui/SecureConversationViewFragment.java
index 7c4a9d9..7c80380 100644
--- a/src/com/android/mail/ui/SecureConversationViewFragment.java
+++ b/src/com/android/mail/ui/SecureConversationViewFragment.java
@@ -33,6 +33,7 @@
 import com.android.mail.browse.ConversationViewHeader;
 import com.android.mail.browse.MessageCursor;
 import com.android.mail.browse.MessageHeaderView;
+import com.android.mail.compose.ComposeActivity;
 import com.android.mail.content.ObjectCursor;
 import com.android.mail.providers.Account;
 import com.android.mail.providers.Conversation;
@@ -290,4 +291,20 @@
     protected void printConversation() {
         mViewController.printMessage();
     }
+
+    @Override
+    protected void handleReply() {
+        final ConversationMessage msg = mViewController.getMessage();
+        if (msg != null) {
+            ComposeActivity.reply(getActivity(), mAccount, msg);
+        }
+    }
+
+    @Override
+    protected void handleReplyAll() {
+        final ConversationMessage msg = mViewController.getMessage();
+        if (msg != null) {
+            ComposeActivity.replyAll(getActivity(), mAccount, msg);
+        }
+    }
 }
diff --git a/src/com/android/mail/utils/Utils.java b/src/com/android/mail/utils/Utils.java
index b3bdd7e..4d9b63f 100644
--- a/src/com/android/mail/utils/Utils.java
+++ b/src/com/android/mail/utils/Utils.java
@@ -789,6 +789,7 @@
             return;
         }
         item.setVisible(shouldShow);
+        item.setEnabled(shouldShow);
     }
 
     /**