Merge the 2019-10-05 SPL branch from AOSP-Partner

* security-aosp-nyc-mr2-release:
  AOSP/UnifiedEmail - bug fix to composing messages.

Change-Id: Ib70f4a0fb0795539941bbc09e2206b436999558b
diff --git a/res/drawable-hdpi/ic_load_more_holo_light.png b/res/drawable-hdpi/ic_load_more_holo_light.png
new file mode 100755
index 0000000..609208a
--- /dev/null
+++ b/res/drawable-hdpi/ic_load_more_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_load_more_holo_light.png b/res/drawable-mdpi/ic_load_more_holo_light.png
new file mode 100755
index 0000000..ab9ddc8
--- /dev/null
+++ b/res/drawable-mdpi/ic_load_more_holo_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_load_more_holo_light.png b/res/drawable-xhdpi/ic_load_more_holo_light.png
new file mode 100755
index 0000000..b6eeb18
--- /dev/null
+++ b/res/drawable-xhdpi/ic_load_more_holo_light.png
Binary files differ
diff --git a/res/drawable/save.xml b/res/drawable/save.xml
new file mode 100644
index 0000000..5a7f170
--- /dev/null
+++ b/res/drawable/save.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+     Redistribution and use in source and binary forms, with or without
+     modification, are permitted provided that the following conditions are
+     met:
+         * Redistributions of source code must retain the above copyright
+           notice, this list of conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above
+           copyright notice, this list of conditions and the following
+           disclaimer in the documentation and/or other materials provided
+           with the distribution.
+         * Neither the name of The Linux Foundation nor the names of its
+           contributors may be used to endorse or promote products derived
+           from this software without specific prior written permission.
+
+     THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+     WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+     ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+     BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+     WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+     IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<vector android:height="20dp" android:viewportHeight="60.0"
+    android:viewportWidth="60.0" android:width="20dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FF7F7F7F" android:pathData="M44.8,0H6C2.7,0 0,2.7 0,6v48c0,3.3 2.7,6 6,6h48c3.3,0 6,-2.7 6,-6V15.2c0,-1.6 -0.6,-3.1 -1.8,-4.2L49,1.8C47.9,0.6 46.4,0 44.8,0zM42,22H12V6h30V22zM37,9h-4v10h4V9z"/>
+</vector>
diff --git a/res/layout-sw600dp/mail_toolbar_view.xml b/res/layout-sw600dp/mail_toolbar_view.xml
index 8205676..2517a55 100644
--- a/res/layout-sw600dp/mail_toolbar_view.xml
+++ b/res/layout-sw600dp/mail_toolbar_view.xml
@@ -23,7 +23,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     app:contentInsetStart="@dimen/action_bar_content_inset_start"
-    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
     app:popupTheme="@style/ThemeOverlay.AppCompat.Light" >
 
     <!-- Custom view for search icon -->
@@ -51,4 +51,4 @@
 
     </LinearLayout>
 
-</com.android.mail.ui.CustomViewToolbar>
\ No newline at end of file
+</com.android.mail.ui.CustomViewToolbar>
diff --git a/res/layout/conversation_list_empty_view.xml b/res/layout/conversation_list_empty_view.xml
index 53dcf52..71cc1ac 100644
--- a/res/layout/conversation_list_empty_view.xml
+++ b/res/layout/conversation_list_empty_view.xml
@@ -27,7 +27,7 @@
     <ImageView
             android:id="@+id/empty_icon"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"/>
+            android:layout_height="wrap_content" />
 
     <TextView
             android:id="@+id/empty_text"
@@ -37,6 +37,8 @@
             android:layout_marginTop="@dimen/empty_view_space"
             android:fontFamily="sans-serif-light"
             android:gravity="center"
+            android:layout_marginRight="@dimen/empty_view_margin_right"
+            android:layout_marginLeft="@dimen/empty_view_margin_left"
             android:textColor="@color/text_color_grey"
             android:textSize="@dimen/empty_view_text_size" />
 
diff --git a/res/layout/conversation_message_attachment_bar.xml b/res/layout/conversation_message_attachment_bar.xml
index 4fe12c0..58a1b7d 100644
--- a/res/layout/conversation_message_attachment_bar.xml
+++ b/res/layout/conversation_message_attachment_bar.xml
@@ -68,15 +68,29 @@
             <include layout="@layout/conversation_message_attachment_bar_progress_bar"/>
         </RelativeLayout>
 
-        <ImageView
-            android:id="@+id/overflow"
-            style="@style/OverflowButtonStyle"
-            android:layout_width="48dip"
-            android:layout_height="match_parent" />
+        <TextView
+            android:id="@+id/attachment_bar_divider"
+            android:layout_width="1dip"
+            android:layout_height="40dip"
+            android:background="@color/attachment_bar_divider_line"
+            android:layout_gravity="center_vertical"
+            android:layout_marginLeft="18dp"/>
+
+        <RelativeLayout
+            android:layout_width="46dp"
+            android:layout_height="match_parent">
+            <ImageView
+                android:id="@+id/save_attachment"
+                android:src="@drawable/save"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_centerInParent="true"
+                android:background="?android:attr/selectableItemBackgroundBorderless"/>
+        </RelativeLayout>
 
         <ImageButton
             android:id="@+id/cancel_attachment"
-            android:layout_width="48dip"
+            android:layout_width="46dip"
             android:layout_height="match_parent"
             android:background="?android:attr/selectableItemBackground"
             android:clickable="true"
diff --git a/res/layout/conversation_message_footer.xml b/res/layout/conversation_message_footer.xml
old mode 100644
new mode 100755
index 7d3ac7c..cfa306d
--- a/res/layout/conversation_message_footer.xml
+++ b/res/layout/conversation_message_footer.xml
@@ -37,6 +37,14 @@
         android:visibility="gone"
         style="@style/AttachmentPaddingStyle" />
 
+    <LinearLayout
+        android:id="@+id/attachment_placeholder_load_more"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:orientation="vertical"
+        style="@style/AttachmentMarginStyle" />
+
     <com.android.mail.ui.AttachmentTileGrid
         android:id="@+id/attachment_tile_grid"
         android:layout_width="match_parent"
diff --git a/res/layout/mail_actionbar_searchview.xml b/res/layout/mail_actionbar_searchview.xml
index b65620c..be100e1 100644
--- a/res/layout/mail_actionbar_searchview.xml
+++ b/res/layout/mail_actionbar_searchview.xml
@@ -50,6 +50,7 @@
         android:singleLine="true"
         android:textAlignment="viewStart"
         android:textColor="@color/search_query_text"
+        android:maxLength="40"
         android:textColorHint="@color/search_query_hint_text"
         android:textSize="16sp" />
 
diff --git a/res/layout/mail_toolbar_view.xml b/res/layout/mail_toolbar_view.xml
index 78a5172..77e265a 100644
--- a/res/layout/mail_toolbar_view.xml
+++ b/res/layout/mail_toolbar_view.xml
@@ -22,5 +22,5 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     app:contentInsetStart="@dimen/action_bar_content_inset_start"
-    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
-    app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
\ No newline at end of file
+    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+    app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
diff --git a/res/layout/one_pane_activity.xml b/res/layout/one_pane_activity.xml
index 6705f55..00e26d9 100644
--- a/res/layout/one_pane_activity.xml
+++ b/res/layout/one_pane_activity.xml
@@ -29,7 +29,7 @@
 
         <!-- Custom toolbar/search overlay -->
         <include layout="@layout/toolbar_with_search" />
-
+        <include layout="@layout/search_factor_and_result"/>
         <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/res/layout/search_factor_and_result.xml b/res/layout/search_factor_and_result.xml
new file mode 100644
index 0000000..dc80b09
--- /dev/null
+++ b/res/layout/search_factor_and_result.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+     Redistribution and use in source and binary forms, with or without
+     modification, are permitted provided that the following conditions are
+     met:
+         * Redistributions of source code must retain the above copyright
+           notice, this list of conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above
+           copyright notice, this list of conditions and the following
+           disclaimer in the documentation and/or other materials provided
+           with the distribution.
+         * Neither the name of The Linux Foundation nor the names of its
+           contributors may be used to endorse or promote products derived
+           from this software without specific prior written permission.
+
+     THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+     WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+     ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+     BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+     WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+     IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<com.android.mail.ui.MaterialSearchFactorSelecteView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/mail_filter_and_result"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:visibility="gone" >
+
+    <RadioGroup
+        android:id="@+id/group_check_factor"
+        android:layout_width="match_parent"
+        android:layout_height="40dp"
+        android:orientation="horizontal" >
+
+        <RadioButton
+            android:id="@+id/check_factor_all"
+            style="@style/MailSearchFactorItemStyle"
+            android:checked="true"
+            android:textColor="@android:color/white"
+            android:background="@color/search_factor_selector_color"
+            android:text="@string/mail_search_factor_all" />
+
+        <RadioButton
+            android:id="@+id/check_factor_subject"
+            style="@style/MailSearchFactorItemStyle"
+            android:text="@string/mail_search_factor_subject" />
+
+        <RadioButton
+            android:id="@+id/check_factor_sender"
+            style="@style/MailSearchFactorItemStyle"
+            android:text="@string/mail_search_factor_sender" />
+
+        <RadioButton
+            android:id="@+id/check_factor_receiver"
+            style="@style/MailSearchFactorItemStyle"
+            android:text="@string/mail_search_factor_receiver" />
+    </RadioGroup>
+</com.android.mail.ui.MaterialSearchFactorSelecteView>
\ No newline at end of file
diff --git a/res/layout/search_results_view.xml b/res/layout/search_results_view.xml
index 2a02605..5c6875a 100644
--- a/res/layout/search_results_view.xml
+++ b/res/layout/search_results_view.xml
@@ -21,7 +21,7 @@
     android:id="@+id/search_status_view"
     android:layout_width="match_parent"
     android:layout_height="@dimen/notification_view_height"
-    android:background="@color/search_banner_bg"
+    android:background="@android:color/white"
     android:focusable="false"
     android:minHeight="@dimen/notification_view_height"
     android:paddingLeft="@dimen/search_results_padding"
@@ -35,7 +35,7 @@
         android:ellipsize="marquee"
         android:gravity="center_vertical"
         android:singleLine="true"
-        android:text="@string/search_results_header"
+        android:text="@string/mail_search_factor_result"
         android:textAllCaps="true"
         style="@style/SearchTextStyle" />
 
diff --git a/res/layout/two_pane_activity.xml b/res/layout/two_pane_activity.xml
index de1e21f..8910989 100644
--- a/res/layout/two_pane_activity.xml
+++ b/res/layout/two_pane_activity.xml
@@ -22,6 +22,7 @@
 
     <!-- Custom toolbar/search overlay -->
     <include layout="@layout/toolbar_with_search" />
+    <include layout="@layout/search_factor_and_result"/>
 
     <!-- Main content -->
     <FrameLayout
diff --git a/res/menu/message_header_overflow_menu.xml b/res/menu/message_header_overflow_menu.xml
old mode 100644
new mode 100755
index 788ac2a..62702c6
--- a/res/menu/message_header_overflow_menu.xml
+++ b/res/menu/message_header_overflow_menu.xml
@@ -23,6 +23,8 @@
           android:title="@string/reply_all" />
     <item android:id="@+id/forward"
           android:title="@string/forward" />
+    <item android:id="@+id/load_more"
+          android:title="@string/load_more"/>
     <item android:id="@+id/add_star"
           android:title="@string/add_star" />
     <item android:id="@+id/remove_star"
diff --git a/res/menu/message_header_overflow_menu_land_or_tablet.xml b/res/menu/message_header_overflow_menu_land_or_tablet.xml
old mode 100644
new mode 100755
index 8f7f1ba..12e0fc6
--- a/res/menu/message_header_overflow_menu_land_or_tablet.xml
+++ b/res/menu/message_header_overflow_menu_land_or_tablet.xml
@@ -21,6 +21,8 @@
           android:title="@string/reply" />
     <item android:id="@+id/reply_all"
           android:title="@string/reply_all" />
+    <item android:id="@+id/load_more"
+          android:title="@string/load_more"/>
     <item android:id="@+id/add_star"
           android:title="@string/add_star" />
     <item android:id="@+id/remove_star"
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 8937b6f..0e842c1 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -477,8 +477,25 @@
     <string name="manage_accounts_menu_item" msgid="4734660452395086786">"管理帐户"</string>
     <string name="general_preferences_title" msgid="4212138728220418162">"常规设置"</string>
     <string name="activity_preferences" msgid="8966077432517613292">"设置"</string>
+
+    <!-- Email search function filter text-->
+    <string name="mail_search_factor_all">全部</string>
+    <string name="mail_search_factor_subject">主题</string>
+    <string name="mail_search_factor_sender">发件人</string>
+    <string name="mail_search_factor_receiver">收件人</string>
+    <string name="mail_search_factor_result">搜索结果</string>
+
+    <!--  Settings screen, confirm on action option title [CHAR LIMIT=150] -->
+    <string name="preference_confirm_before_forward_title">转发前先确认</string>
+    <!-- Confirm forward message -->
+    <string name="confirm_forward_message">"若该邮件含有未下载的附件,如何转发该邮件?"</string>
+    <string name="confirm_forward_normal">"下载完成后发送"</string>
+    <string name="confirm_forward_drop_unloaded">"舍弃未完成的"</string>
+    <!-- Forward dialog message -->
+    <string name="forward_dialog_message">"您是否希望转发这封邮件?"</string>
     <string name="overflow_description" msgid="7886090597023240426">"更多选项"</string>
     <string name="security_hold_required_text" msgid="6619955920101834286">"<xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>需要进行安全更新"</string>
     <string name="update_security_text" msgid="3527335757205741077">"立即更新"</string>
     <string name="forms_are_disabled" msgid="2876312737118986789">"Gmail已停用表单"</string>
+    <string name="save_to">保存文件至:</string>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 6455cf8..e0fc4e6 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -154,6 +154,11 @@
     <color name="vacation_responder_header_text_color">@color/text_color_grey</color>
     <color name="vacation_responder_main_text_color">@color/text_color_black</color>
 
+    <!-- search filter button color -->
+    <color name="search_factor_normal_color">#EDEDED</color>
+    <color name="search_factor_selector_color">#B2B2B2</color>
+    <color name="search_factor_text_normal_color">#36000000</color>
+    
     <!-- Swipe refresh progress bar colors -->
     <color name="swipe_refresh_color1">#ff0f9d58</color>
     <color name="swipe_refresh_color2">#ffdb4437</color>
@@ -194,4 +199,6 @@
 
     <color name="text_color_undo_notification">#feb500</color>
     <color name="text_color_undo_notification_description">@android:color/white</color>
+
+    <color name="attachment_bar_divider_line">#1A000000</color>
 </resources>
diff --git a/res/values/dimen.xml b/res/values/dimen.xml
index 2c78bb1..fb2eb96 100644
--- a/res/values/dimen.xml
+++ b/res/values/dimen.xml
@@ -72,7 +72,7 @@
     <dimen name="super_collapsed_height">30sp</dimen>
     <dimen name="super_collapsed_text_size">16sp</dimen>
     <dimen name="super_collapsed_stroke_margin_start">0dp</dimen>
-    <dimen name="notification_view_height">36dp</dimen>
+    <dimen name="notification_view_height">32dp</dimen>
     <dimen name="contact_image_width">40dp</dimen>
     <dimen name="contact_image_height">40dp</dimen>
     <dimen name="message_header_icon_margin">16dp</dimen>
@@ -203,6 +203,8 @@
     <dimen name="empty_view_text_width">228dip</dimen>
     <dimen name="empty_view_text_size">20sp</dimen>
     <dimen name="empty_view_space">16dip</dimen>
+    <dimen name="empty_view_margin_left">60dp</dimen>
+    <dimen name="empty_view_margin_right">60dp</dimen>
 
     <dimen name="avatar_border_width">.5dp</dimen>
     <dimen name="progress_bar_height">12dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 608acdb..7d043ba 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -602,6 +602,7 @@
     <string name="search_title" translatable="false">Unified Email</string>
     <!-- Shown in light gray in the Search box when no text has been entered [CHAR LIMIT=20]-->
     <string name="search_hint">Search</string>
+    <string name="search_input_limit_number">Search word Input number limit of 40</string>
     <!-- Search Results: Text for status of the search when the results are completely loaded [CHAR LIMIT=10] -->
     <string name="search_results_loaded"><xliff:g id="searchCount">%1$d</xliff:g></string>
     <!-- Voice search is not supported on this device [CHAR LIMIT=100] -->
@@ -1157,6 +1158,24 @@
 
     <string name="user_agent_format" translatable="false">%1$s AndroidUnifiedEmail/%2$s</string>
 
+    <!--Email search function filter text  -->
+    <string name="mail_search_factor_all">ALL</string>
+    <string name="mail_search_factor_subject">SUBJECT</string>
+    <string name="mail_search_factor_sender">SENDER</string>
+    <string name="mail_search_factor_receiver">RECEIVER</string>
+    <string name="mail_search_factor_result">RESULTS</string>
+
+
+    <!--  Settings screen, confirm on action option title [CHAR LIMIT=150] -->
+    <string name="preference_confirm_before_forward_title">Confirm before forwarding</string>
+    <!-- Confirm forward dialog message -->
+    <string name="confirm_forward_message">If this message has unloaded attachment(s), what do you want?</string>
+    <string name="confirm_forward_normal">Send after loaded</string>
+    <string name="confirm_forward_drop_unloaded">Drop unloaded</string>
+    <!-- Forward dialog message -->
+    <string name="forward_dialog_message">Do you want forward this message?</string>
+    <!-- Feedback uri to be used when feedback is enabled -->
+    <string name="email_feedback_uri" translatable="false"></string>
     <!-- Text when there is a security hold on an account that needs to be accepted. The email address param takes the for "abc@xyz.com" -->
     <string name="security_hold_required_text">Security update required for <xliff:g id="email_address">%1$s</xliff:g></string>
 
@@ -1167,4 +1186,6 @@
 
     <!-- activity name of the photoviewer -->
     <string name="photo_view_activity" translatable="false">com.android.mail.photo.MailPhotoViewActivity</string>
+    <string name="download_first">Please Download first</string>
+    <string name="save_to">Save the file to:</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 5e53375..b83e37d 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -375,7 +375,7 @@
         <item name="android:scaleType">center</item>
     </style>
 
-    <style name="MessageHeaderOverflowButtonStyle" parent="@style/OverflowButtonStyle">
+    <style name="MessageHeaderOverflowButtonStyle" parent="@style/SaveAttachmentButtonStyle">
         <item name="android:layout_width">@dimen/message_header_overflow_button_width</item>
         <item name="android:layout_height">@dimen/message_header_action_button_height</item>
         <item name="android:layout_marginTop">@dimen/message_header_action_button_margin_top</item>
@@ -454,8 +454,8 @@
         <item name="android:layout_marginTop">16dip</item>
     </style>
 
-    <style name="OverflowButtonStyle" parent="@style/Widget.AppCompat.ActionButton.Overflow">
-        <item name="android:src">@drawable/ic_overflow_24dp</item>
+    <style name="SaveAttachmentButtonStyle" parent="@style/Widget.AppCompat.ActionButton.Overflow">
+        <item name="android:src">@drawable/save</item>
     </style>
 
     <style name="ActionBarOverflowButtonStyle" parent="@android:style/Widget.Holo.ActionButton.Overflow">
@@ -755,7 +755,7 @@
     </style>
 
     <style name="SearchTextStyle">
-        <item name="android:textColor">@color/search_banner_text</item>
+        <item name="android:textColor">@color/search_factor_text_normal_color</item>
         <item name="android:textSize">@dimen/search_banner_text_size</item>
     </style>
 
@@ -920,4 +920,14 @@
         <item name="android:paddingRight">@dimen/account_item_name_end_padding</item>
     </style>
 
+    <style name="MailSearchFactorItemStyle">
+        <item name="android:layout_width">0dp</item>
+        <item name="android:layout_height">match_parent</item>
+        <item name="android:layout_weight">1</item>
+        <item name="android:button">@null</item>
+        <item name="android:gravity">center</item>
+        <item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
+        <item name="android:textColor">@color/search_factor_text_normal_color</item>
+        <item name="android:textSize">14sp</item>
+    </style>
 </resources>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 2cee638..bef9473 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -25,6 +25,14 @@
         <item name="homeAsUpIndicator">@drawable/ic_arrow_back_wht_24dp_with_rtl</item>
     </style>
 
+    <style name="EmlViewerTheme.Appcompat" parent="@style/UnifiedEmailTheme.Appcompat">
+        <item name="android:actionBarStyle">@style/ActionBarStyle</item>
+        <item name="android:actionBarWidgetTheme">@style/ActionBarWidgetTheme</item>
+        <item name="android:actionOverflowButtonStyle">@style/ActionBarOverflowButtonStyle</item>
+        <item name="android:listViewWhiteStyle">@android:style/Widget.Holo.Light.ListView</item>
+    </style>
+
+
     <style name="UnifiedEmailTheme.Appcompat.Toolbar" parent="@style/Theme.AppCompat.Light.NoActionBar">
         <item name="actionBarPopupTheme">@style/ThemeOverlay.AppCompat.Light</item>
         <item name="actionModeBackground">@color/action_mode_background</item>
diff --git a/src/com/android/emailcommon/mail/FetchProfile.java b/src/com/android/emailcommon/mail/FetchProfile.java
old mode 100644
new mode 100755
index bfa48d3..b3dda40
--- a/src/com/android/emailcommon/mail/FetchProfile.java
+++ b/src/com/android/emailcommon/mail/FetchProfile.java
@@ -82,4 +82,25 @@
         }

         return null;

     }

+
+    /**
+     * To save the allow sync size for this fetch action. And the default value is Entire mail
+     * as the {@link Integer#MAX_VALUE} which is defined in Utility;
+     */
+    private int mAllowSyncSize = Integer.MAX_VALUE;
+
+    public void setAllowSyncSize(int size) {
+        mAllowSyncSize = size;
+    }
+
+    public int getAllowSyncSize() {
+        return mAllowSyncSize;
+    }
+
+    @Override
+    public void clear() {
+        super.clear();
+        // Reset the mAllowSyncSize as default value.
+        mAllowSyncSize = Integer.MAX_VALUE;
+    }
 }

diff --git a/src/com/android/emailcommon/service/SearchParams.java b/src/com/android/emailcommon/service/SearchParams.java
index c5d80af..8ed5fae 100644
--- a/src/com/android/emailcommon/service/SearchParams.java
+++ b/src/com/android/emailcommon/service/SearchParams.java
@@ -48,6 +48,15 @@
     // The id of the "search" mailbox being used
     public long mSearchMailboxId;
 
+    public static final String SEARCH_FACTOR_ALL = "ALL";
+    public static final String SEARCH_FACTOR_SUBJECT = "SUBJECT";
+    public static final String SEARCH_FACTOR_SENDER = "SENDER";
+    public static final String SEARCH_FACTOR_RECEIVER  = "RECEIVER";
+    public static final String BUNDLE_QUERY_FACTOR = "queryFactor";
+    public static final String BUNDLE_QUERY_FILTER = "queryFilter";
+
+    public String mFactor = SEARCH_FACTOR_ALL;
+
     /**
      * Error codes returned by the searchMessages API
      */
@@ -56,11 +65,12 @@
         public static final int CANT_SEARCH_CHILDREN = -2;
     }
 
-    public SearchParams(long mailboxId, String filter) {
+    public SearchParams(long mailboxId, String filter, String factor) {
         mMailboxId = mailboxId;
         mFilter = filter;
         mStartDate = null;
         mEndDate = null;
+        mFactor = factor;
     }
 
     public SearchParams(long mailboxId, String filter, long searchMailboxId) {
@@ -71,6 +81,15 @@
         mSearchMailboxId = searchMailboxId;
     }
 
+    public SearchParams(long mailboxId, String filter, String factor, long searchMailboxId) {
+        mMailboxId = mailboxId;
+        mFilter = filter;
+        mFactor = factor;
+        mStartDate = null;
+        mEndDate = null;
+        mSearchMailboxId = searchMailboxId;
+    }
+
     public SearchParams(long mailboxId, String filter, long searchMailboxId, Date startDate,
             Date endDate) {
         mMailboxId = mailboxId;
@@ -96,21 +115,26 @@
                 && Objects.equal(mStartDate, os.mStartDate)
                 && Objects.equal(mEndDate, os.mEndDate)
                 && mLimit == os.mLimit
-                && mOffset == os.mOffset;
+                && mOffset == os.mOffset
+
+                && mFactor.equals(os.mFactor);
+
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(mMailboxId, mFilter, mStartDate, mEndDate, mLimit, mOffset);
+        return Objects.hashCode(mMailboxId, mFilter, mStartDate, mEndDate, mLimit, mOffset,
+                mFactor);
     }
 
+
     @Override
     public String toString() {
-        return "[SearchParams "
-                + mMailboxId + ":" + mFilter
-                + " (" + mOffset + ", " + mLimit + ")"
-                + " {" + mStartDate + ", " + mEndDate + "}"
-                + "]";
+        return "SearchParams [mMailboxId=" + mMailboxId + ", mIncludeChildren="
+                + mIncludeChildren + ", mFilter=" + mFilter + ", mStartDate="
+                + mStartDate + ", mEndDate=" + mEndDate + ", mLimit=" + mLimit
+                + ", mOffset=" + mOffset + ", mSearchMailboxId="
+                + mSearchMailboxId + ", mFactor=" + mFactor + "]";
     }
 
     @Override
@@ -144,6 +168,7 @@
         dest.writeString(mFilter);
         dest.writeInt(mLimit);
         dest.writeInt(mOffset);
+        dest.writeString(mFactor);
         SparseArray<Object> dateWindow = new SparseArray<Object>(2);
         if (mStartDate != null) {
             dateWindow.put(0, mStartDate.getTime());
@@ -163,6 +188,7 @@
         mFilter = in.readString();
         mLimit = in.readInt();
         mOffset = in.readInt();
+        mFactor = in.readString();
         SparseArray dateWindow = in.readSparseArray(this.getClass().getClassLoader());
         if (dateWindow.get(0) != null) {
             mStartDate = new Date((Long)dateWindow.get(0));
diff --git a/src/com/android/mail/ConversationListContext.java b/src/com/android/mail/ConversationListContext.java
index f5885aa..7fdb6b1 100644
--- a/src/com/android/mail/ConversationListContext.java
+++ b/src/com/android/mail/ConversationListContext.java
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import android.text.TextUtils;
 
+import com.android.emailcommon.service.SearchParams;
 import com.android.mail.providers.Account;
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.UIProvider;
@@ -55,7 +56,14 @@
     /**
      * The search query whose results we are displaying, if any.
      */
-    public final String searchQuery;
+    public String searchQuery;
+
+    private boolean mIsLocalSearch = false;
+    private String mSearchFactor = "";
+    private SearchParams mSearchParams = new SearchParams(0, null, null);
+    public static final String EXTRA_SEARCH_LOCAL = "search_local";
+    public static final String EXTRA_SEARCH_FACTOR = "Search_factor";
+
 
     static {
         sUrlMatcher.addURI(UIProvider.AUTHORITY, "account/*/folder/*", 0);
@@ -69,7 +77,15 @@
         // We should probably be reading an account instance from our controller.
         Account account = bundle.getParcelable(Utils.EXTRA_ACCOUNT);
         Folder folder = bundle.getParcelable(Utils.EXTRA_FOLDER);
-        return new ConversationListContext(account, bundle.getString(EXTRA_SEARCH_QUERY), folder);
+        boolean isLocal = bundle.getBoolean(EXTRA_SEARCH_LOCAL, false);
+
+        String factor = bundle.getString(EXTRA_SEARCH_FACTOR);
+        ConversationListContext convListContext = new ConversationListContext(account,
+                bundle.getString(EXTRA_SEARCH_QUERY), folder);
+        convListContext.setLocalSearch(isLocal);
+        convListContext.setSearchFactor(factor);
+
+        return convListContext;
     }
 
     /**
@@ -102,15 +118,55 @@
         account = a;
         searchQuery = query;
         folder = f;
+        mSearchParams = new SearchParams(folder.id, searchQuery, mSearchFactor);
     }
 
+
+    public void setSearchFactor(String factor) {
+        mSearchFactor = factor;
+        mSearchParams = new SearchParams(folder.id, searchQuery, mSearchFactor);
+    }
+
+    public String getSearchQuery() {
+        return searchQuery;
+    }
+
+    public String getSearchFactor() {
+        return mSearchFactor;
+    }
+
+    public SearchParams getSearchParams() {
+        return mSearchParams;
+    }
+
+    public void setLocalSearch(boolean isLocalSearch) {
+        mIsLocalSearch = isLocalSearch;
+    }
+
+    public boolean isLocalSearch() {
+        return mIsLocalSearch;
+    }
+
+    public void setSearchQueryText(String query) {
+        searchQuery = query;
+        mSearchParams = new SearchParams(folder.id, searchQuery, mSearchFactor);
+    }
+
+
     /**
      * Returns true if the provided context represents search results.
      * @param in
      * @return true the context represents search results. False otherwise
      */
     public static final boolean isSearchResult(ConversationListContext in) {
-        return in != null && !TextUtils.isEmpty(in.searchQuery);
+        return in != null && !TextUtils.isEmpty(in.searchQuery) && !in.mIsLocalSearch;
+    }
+
+
+
+    public boolean isLocalSearchExecuted() {
+        return isLocalSearch() && !TextUtils.isEmpty(getSearchQuery())
+                && !TextUtils.isEmpty(getSearchQuery().trim());
     }
 
     /**
@@ -121,6 +177,8 @@
         result.putParcelable(Utils.EXTRA_ACCOUNT, account);
         result.putString(EXTRA_SEARCH_QUERY, searchQuery);
         result.putParcelable(Utils.EXTRA_FOLDER, folder);
+        result.putBoolean(EXTRA_SEARCH_LOCAL, mIsLocalSearch);
+        result.putString(EXTRA_SEARCH_FACTOR, mSearchFactor);
         return result;
     }
 }
diff --git a/src/com/android/mail/browse/AttachmentActionHandler.java b/src/com/android/mail/browse/AttachmentActionHandler.java
old mode 100644
new mode 100755
index ee569a6..838f76c
--- a/src/com/android/mail/browse/AttachmentActionHandler.java
+++ b/src/com/android/mail/browse/AttachmentActionHandler.java
@@ -19,6 +19,7 @@
 
 
 import android.app.DialogFragment;
+import android.app.DownloadManager;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
@@ -26,10 +27,13 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.media.MediaScannerConnection;
 import android.net.Uri;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.Parcelable;
 
+import android.widget.Toast;
 import com.android.mail.providers.Attachment;
 import com.android.mail.providers.Message;
 import com.android.mail.providers.UIProvider;
@@ -37,10 +41,19 @@
 import com.android.mail.providers.UIProvider.AttachmentContentValueKeys;
 import com.android.mail.providers.UIProvider.AttachmentDestination;
 import com.android.mail.providers.UIProvider.AttachmentState;
+import com.android.mail.R;
 import com.android.mail.utils.LogTag;
 import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.MimeType;
 import com.android.mail.utils.Utils;
 
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
 import java.util.ArrayList;
 
 public class AttachmentActionHandler {
@@ -101,7 +114,7 @@
                         mAttachment.destination == destination)) {
             mView.viewAttachment();
         } else {
-            showDownloadingDialog();
+
             startDownloadingAttachment(destination);
         }
     }
@@ -120,9 +133,32 @@
                 mAttachment, destination, rendition, additionalPriority, delayDownload);
     }
 
+    public void saveAttachment(int destination){
+
+        if (mAttachment.state == AttachmentState.SAVED
+                && destination == AttachmentDestination.EXTERNAL){
+            performAttachmentSave(mAttachment);
+        }else{
+            Toast.makeText(mContext, mContext.getResources().getString(R.string.download_first),
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
     private void startDownloadingAttachment(
             Attachment attachment, int destination, int rendition, int additionalPriority,
             boolean delayDownload) {
+        //do not auto install apk from stream . must save first so that can be install
+        if (attachment.state == AttachmentState.SAVED
+                && destination == AttachmentDestination.EXTERNAL
+                && !MimeType.isInstallable(attachment.getContentType())) {
+            File savedFile = performAttachmentSave(attachment);
+            if (savedFile != null && mView != null) {
+                // The attachment is saved successfully from cache.
+                mView.viewAttachment();
+                return;
+            }
+        }
+
         final ContentValues params = new ContentValues(5);
         params.put(AttachmentColumns.STATE, AttachmentState.DOWNLOADING);
         params.put(AttachmentColumns.DESTINATION, destination);
@@ -284,4 +320,62 @@
             // no-op
         }
     }
+
+    private File createUniqueFile(File directory, String filename) throws IOException {
+        File file = new File(directory, filename);
+        if (file.createNewFile()) {
+            return file;
+        }
+        // Get the extension of the file, if any.
+        int index = filename.lastIndexOf('.');
+        String format;
+        if (index != -1) {
+            String name = filename.substring(0, index);
+            String extension = filename.substring(index);
+            format = name + "-%d" + extension;
+        } else {
+            format = filename + "-%d";
+        }
+
+        for (int i = 2; i < Integer.MAX_VALUE; i++) {
+            file = new File(directory, String.format(format, i));
+            if (file.createNewFile()) {
+                return file;
+            }
+        }
+        return null;
+    }
+
+    private File performAttachmentSave(final Attachment attachment) {
+        try {
+            File downloads = Environment.getExternalStoragePublicDirectory(
+                    Environment.DIRECTORY_DOWNLOADS);
+            downloads.mkdirs();
+            File file = createUniqueFile(downloads, attachment.getName());
+            Uri contentUri = attachment.contentUri;
+            InputStream in = mContext.getContentResolver().openInputStream(contentUri);
+            OutputStream out = new FileOutputStream(file);
+            int size = IOUtils.copy(in, out);
+            out.flush();
+            out.close();
+            in.close();
+            String absolutePath = file.getAbsolutePath();
+            MediaScannerConnection.scanFile(mContext, new String[] {absolutePath},
+                    null, null);
+            DownloadManager dm =
+                    (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+            dm.addCompletedDownload(attachment.getName(), attachment.getName(),
+                    false /* do not use media scanner */,
+                    attachment.getContentType(), absolutePath, size,
+                    true /* show notification */);
+            Toast.makeText(mContext,
+                    mContext.getResources().getString(R.string.save_to) + absolutePath,
+                    Toast.LENGTH_SHORT).show();
+            return file;
+        } catch (IOException ioe) {
+            // Ignore. Callers will handle it from the return code.
+        }
+
+        return null;
+    }
 }
diff --git a/src/com/android/mail/browse/ConversationItemView.java b/src/com/android/mail/browse/ConversationItemView.java
index e2dcf47..af3126d 100644
--- a/src/com/android/mail/browse/ConversationItemView.java
+++ b/src/com/android/mail/browse/ConversationItemView.java
@@ -47,9 +47,11 @@
 import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
 import android.text.format.DateUtils;
+import android.text.style.AbsoluteSizeSpan;
 import android.text.style.BackgroundColorSpan;
 import android.text.style.CharacterStyle;
 import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
 import android.text.style.TextAppearanceSpan;
 import android.util.SparseArray;
 import android.util.TypedValue;
@@ -60,6 +62,8 @@
 import android.view.animation.DecelerateInterpolator;
 import android.widget.TextView;
 
+import com.android.emailcommon.service.SearchParams;
+import com.android.emailcommon.utility.TextUtilities;
 import com.android.mail.R;
 import com.android.mail.analytics.Analytics;
 import com.android.mail.bitmap.CheckableContactFlipDrawable;
@@ -72,6 +76,7 @@
 import com.android.mail.providers.UIProvider.ConversationColumns;
 import com.android.mail.providers.UIProvider.ConversationListIcon;
 import com.android.mail.providers.UIProvider.FolderType;
+import com.android.mail.ui.ActivityController;
 import com.android.mail.ui.AnimatedAdapter;
 import com.android.mail.ui.ControllableActivity;
 import com.android.mail.ui.ConversationCheckedSet;
@@ -87,8 +92,11 @@
 import com.android.mail.utils.ViewUtils;
 import com.google.common.annotations.VisibleForTesting;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class ConversationItemView extends View
         implements SwipeableItemView, ToggleableItem, ConversationSetObserver,
@@ -105,6 +113,7 @@
     private static final String PERF_TAG_CALCULATE_FOLDERS = "CCHV.folders";
     private static final String PERF_TAG_CALCULATE_COORDINATES = "CCHV.coordinates";
     private static final String LOG_TAG = LogTag.getLogTag();
+    private static final int HIGHLIGHT_COLOR_INT = 0xFFE7790D;
 
     private static final Typeface SANS_SERIF_BOLD = Typeface.create("sans-serif", Typeface.BOLD);
 
@@ -207,12 +216,15 @@
 
     private final CheckableContactFlipDrawable mSendersImageView;
 
+    private SearchParams mSearchParams;
+
     /** The resource id of the color to use to override the background. */
     private int mBackgroundOverrideResId = -1;
     /** The bitmap to use, or <code>null</code> for the default */
     private Bitmap mPhotoBitmap = null;
     private Rect mPhotoRect = new Rect();
 
+    private final static int OFFSET = 5;
     /**
      * A listener for clicks on the various areas of a conversation item.
      */
@@ -527,6 +539,12 @@
         mHeader = header;
         mActivity = activity;
         mCheckedConversationSet = set;
+
+        ActivityController activityController = (ActivityController) activity
+                .getAccountController();
+        mSearchParams = activityController != null ? activityController
+                .getCurrentConversationListContext().getSearchParams() : null;
+
         if (mCheckedConversationSet != null) {
             mCheckedConversationSet.addObserver(this);
         }
@@ -557,15 +575,17 @@
         Utils.traceEndSection();
 
         final int ignoreFolderType;
-        if (mDisplayedFolder.isInbox()) {
+        if (mDisplayedFolder != null && mDisplayedFolder.isInbox()) {
             ignoreFolderType = FolderType.INBOX;
         } else {
             ignoreFolderType = -1;
         }
 
         Utils.traceBeginSection("load folders");
-        mHeader.folderDisplayer.loadConversationFolders(mHeader.conversation,
-                mDisplayedFolder.folderUri, ignoreFolderType);
+        if(mDisplayedFolder != null) {
+            mHeader.folderDisplayer.loadConversationFolders(mHeader.conversation,
+                    mDisplayedFolder.folderUri, ignoreFolderType);
+        }
         Utils.traceEndSection();
 
         if (mHeader.showDateText) {
@@ -870,8 +890,9 @@
             mSendersTextView.setMaxLines(mCoordinates.sendersLineCount);
             mSendersTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mCoordinates.sendersFontSize);
             layoutViewExactly(mSendersTextView, w, h);
-
-            mSendersTextView.setText(participantText);
+            SpannableStringBuilder highLightSender = highlightTermsInText(
+                    participantText.toString(), participantText);
+            mSendersTextView.setText(highLightSender);
         }
     }
 
@@ -880,7 +901,8 @@
         String subject = filterTag(getContext(), mHeader.conversation.subject);
         subject = mAdapter.getBidiFormatter().unicodeWrap(subject);
         subject = Conversation.getSubjectForDisplay(mContext, badgeText, subject);
-        final Spannable displayedStringBuilder = new SpannableString(subject);
+
+        final SpannableString displayedStringBuilder = new SpannableString(subject);
 
         // since spans affect text metrics, add spans to the string before measure/layout or eliding
 
@@ -891,18 +913,23 @@
                     isUnread ? sSubjectTextUnreadSpan : sSubjectTextReadSpan),
                     badgeTextLength, subject.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
         }
+
         if (isActivated() && showActivatedText()) {
             displayedStringBuilder.setSpan(sActivatedTextSpan, badgeTextLength,
                     displayedStringBuilder.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
         }
 
+        SpannableStringBuilder spannableString = highlightTermsInText(
+                displayedStringBuilder.toString(), new SpannableStringBuilder(
+                        displayedStringBuilder));
+
         final int subjectWidth = mCoordinates.subjectWidth;
-        final int subjectHeight = mCoordinates.subjectHeight;
+        final int subjectHeight = isFactorMatch() ? mCoordinates.subjectHeight + OFFSET
+                : mCoordinates.subjectHeight;
         mSubjectTextView.setLayoutParams(new ViewGroup.LayoutParams(subjectWidth, subjectHeight));
         mSubjectTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mCoordinates.subjectFontSize);
         layoutViewExactly(mSubjectTextView, subjectWidth, subjectHeight);
-
-        mSubjectTextView.setText(displayedStringBuilder);
+        mSubjectTextView.setText(spannableString);
     }
 
     private void createSnippet() {
@@ -1779,4 +1806,34 @@
     public String getAccountEmailAddress() {
         return mAccount.getEmailAddress();
     }
+
+    // judge match the factor
+    private boolean isFactorMatch() {
+        return (mSearchParams != null
+                && !TextUtils.isEmpty(mSearchParams.mFilter)
+                && (SearchParams.SEARCH_FACTOR_SUBJECT.equals(mSearchParams.mFactor)
+                || SearchParams.SEARCH_FACTOR_SENDER.equals(mSearchParams.mFactor)
+                || SearchParams.SEARCH_FACTOR_ALL.equals(mSearchParams.mFactor)));
+    }
+
+    // highlight
+    private SpannableStringBuilder highlightTermsInText(String field,
+            SpannableStringBuilder highLightBuilder) {
+        if (isFactorMatch()) {
+            Pattern pattern = Pattern.compile(mSearchParams.mFilter.toLowerCase());
+            Matcher matcher = pattern.matcher(field.toLowerCase());
+            while (matcher.find()) {
+                highLightBuilder.setSpan(new ForegroundColorSpan(HIGHLIGHT_COLOR_INT),
+                        matcher.start(), matcher.end(),
+                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                highLightBuilder.setSpan(new AbsoluteSizeSpan(16, true), matcher.start(),
+                        matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                highLightBuilder.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
+                        matcher.start(),
+                        matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            }
+        }
+        return highLightBuilder;
+    }
+
 }
diff --git a/src/com/android/mail/browse/ConversationMessage.java b/src/com/android/mail/browse/ConversationMessage.java
old mode 100644
new mode 100755
index 36ab441..781dd45
--- a/src/com/android/mail/browse/ConversationMessage.java
+++ b/src/com/android/mail/browse/ConversationMessage.java
@@ -116,6 +116,13 @@
         }
     }
 
+    public void loadMore() {
+        final ConversationUpdater listController = mController.getListController();
+        if (listController != null) {
+            listController.loadMore(this);
+        }
+    }
+
     /**
      * Public object that knows how to construct Messages given Cursors.
      */
diff --git a/src/com/android/mail/browse/MessageAttachmentBar.java b/src/com/android/mail/browse/MessageAttachmentBar.java
old mode 100644
new mode 100755
index 0274d66..6596282
--- a/src/com/android/mail/browse/MessageAttachmentBar.java
+++ b/src/com/android/mail/browse/MessageAttachmentBar.java
@@ -20,8 +20,11 @@
 import android.app.AlertDialog;
 import android.app.FragmentManager;
 import android.content.ActivityNotFoundException;
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.net.Uri;
 import android.support.v4.text.BidiFormatter;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -61,6 +64,7 @@
         OnMenuItemClickListener, AttachmentViewInterface {
 
     private Attachment mAttachment;
+    private ImageView mIcon;
     private TextView mTitle;
     private TextView mSubTitle;
     private String mAttachmentSizeText;
@@ -68,11 +72,13 @@
     private ProgressBar mProgress;
     private ImageButton mCancelButton;
     private PopupMenu mPopup;
-    private ImageView mOverflowButton;
+    private ImageView mSaveAttachmentButton;
+    private TextView mAttachmentBarDivider;
 
     private final AttachmentActionHandler mActionHandler;
     private boolean mSaveClicked;
     private Account mAccount;
+    private final static String FORMAT_RAW = "RAW";
 
     private final Runnable mUpdateRunnable = new Runnable() {
             @Override
@@ -141,8 +147,11 @@
                 attachment.getContentType(), attachment.flags);
 
         final String attachmentName = attachment.getName();
-        if ((attachment.flags & Attachment.FLAG_DUMMY_ATTACHMENT) != 0) {
-            mTitle.setText(R.string.load_attachment);
+        if (attachment.isLoadMore()) {
+            mIcon.setImageResource(R.drawable.ic_load_more_holo_light);
+            mTitle.setText(R.string.load_more);
+            mSaveAttachmentButton.setVisibility(View.GONE);
+            mAttachmentBarDivider.setVisibility(View.GONE);
         } else if (prevAttachment == null
                 || !TextUtils.equals(attachmentName, prevAttachment.getName())) {
             mTitle.setText(attachmentName);
@@ -164,14 +173,16 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
+        mIcon = (ImageView) findViewById(R.id.attachment_icon);
         mTitle = (TextView) findViewById(R.id.attachment_title);
         mSubTitle = (TextView) findViewById(R.id.attachment_subtitle);
         mProgress = (ProgressBar) findViewById(R.id.attachment_progress);
-        mOverflowButton = (ImageView) findViewById(R.id.overflow);
+        mSaveAttachmentButton = (ImageView) findViewById(R.id.save_attachment);
         mCancelButton = (ImageButton) findViewById(R.id.cancel_attachment);
+        mAttachmentBarDivider = (TextView) findViewById(R.id.attachment_bar_divider);
 
         setOnClickListener(this);
-        mOverflowButton.setOnClickListener(this);
+        mSaveAttachmentButton.setOnClickListener(this);
         mCancelButton.setOnClickListener(this);
     }
 
@@ -187,55 +198,12 @@
     }
 
     private boolean onClick(final int res, final View v) {
-        if (res == R.id.preview_attachment) {
-            previewAttachment();
-        } else if (res == R.id.save_attachment) {
-            if (mAttachment.canSave()) {
-                mActionHandler.startDownloadingAttachment(AttachmentDestination.EXTERNAL);
-                mSaveClicked = true;
+        if (res == R.id.save_attachment) {
 
-                Analytics.getInstance().sendEvent(
+            mActionHandler.saveAttachment(AttachmentDestination.EXTERNAL);
+            Analytics.getInstance().sendEvent(
                         "save_attachment", Utils.normalizeMimeType(mAttachment.getContentType()),
                         "attachment_bar", mAttachment.size);
-            }
-        } else if (res == R.id.download_again) {
-            if (mAttachment.isPresentLocally()) {
-                mActionHandler.showDownloadingDialog();
-                mActionHandler.startRedownloadingAttachment(mAttachment);
-
-                Analytics.getInstance().sendEvent("redownload_attachment",
-                        Utils.normalizeMimeType(mAttachment.getContentType()), "attachment_bar",
-                        mAttachment.size);
-            }
-        } else if (res == R.id.cancel_attachment) {
-            mActionHandler.cancelAttachment();
-            mSaveClicked = false;
-
-            Analytics.getInstance().sendEvent(
-                    "cancel_attachment", Utils.normalizeMimeType(mAttachment.getContentType()),
-                    "attachment_bar", mAttachment.size);
-        } else if (res == R.id.attachment_extra_option1) {
-            mActionHandler.handleOption1();
-        } else if (res == R.id.overflow) {
-            // If no overflow items are visible, just bail out.
-            // We shouldn't be able to get here anyhow since the overflow
-            // button should be hidden.
-            if (shouldShowOverflow()) {
-                if (mPopup == null) {
-                    mPopup = new PopupMenu(getContext(), v);
-                    mPopup.getMenuInflater().inflate(R.menu.message_footer_overflow_menu,
-                            mPopup.getMenu());
-                    mPopup.setOnMenuItemClickListener(this);
-                }
-
-                final Menu menu = mPopup.getMenu();
-                menu.findItem(R.id.preview_attachment).setVisible(shouldShowPreview());
-                menu.findItem(R.id.save_attachment).setVisible(shouldShowSave());
-                menu.findItem(R.id.download_again).setVisible(shouldShowDownloadAgain());
-                menu.findItem(R.id.attachment_extra_option1).setVisible(shouldShowExtraOption1());
-
-                mPopup.show();
-            }
         } else {
             // Handles clicking the attachment
             // in any area that is not the overflow
@@ -244,24 +212,16 @@
             final String mime = Utils.normalizeMimeType(mAttachment.getContentType());
             final String action;
 
-            if ((mAttachment.flags & Attachment.FLAG_DUMMY_ATTACHMENT) != 0) {
-                // This is a dummy. We need to download it, but not attempt to open or preview.
-                mActionHandler.showDownloadingDialog();
-                mActionHandler.setViewOnFinish(false);
-                mActionHandler.startDownloadingAttachment(AttachmentDestination.CACHE);
+            if (mAttachment.isLoadMore()) {
+                // Changed to use the Message's load more uri to get the entire mail.
+                if (mAttachment.messageLoadMoreUri != null) {
+                    LoadMoreAction loadmore = new LoadMoreAction(getContext().getContentResolver(),
+                            mAttachment.messageLoadMoreUri);
+                    loadmore.sendCommand();
+                }
 
                 action = null;
             }
-            // If we can install, install.
-            else if (MimeType.isInstallable(mAttachment.getContentType())) {
-                // Save to external because the package manager only handles
-                // file:// uris not content:// uris. We do the same
-                // workaround in
-                // UiProvider#getUiAttachmentsCursorForUIAttachments()
-                mActionHandler.showAttachment(AttachmentDestination.EXTERNAL);
-
-                action = "attachment_bar_install";
-            }
             // If we can view or play with an on-device app,
             // view or play.
             else if (MimeType.isViewable(
@@ -281,8 +241,8 @@
                 AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
                 int dialogMessage = R.string.no_application_found;
                 builder.setTitle(R.string.more_info_attachment)
-                       .setMessage(dialogMessage)
-                       .show();
+                        .setMessage(dialogMessage)
+                        .show();
 
                 action = "attachment_bar_no_viewer";
             }
@@ -302,7 +262,7 @@
     }
 
     private boolean shouldShowSave() {
-        return mAttachment.canSave() && !mSaveClicked;
+        return mAttachment.canSave() && !mSaveClicked && !mAttachment.isLoadMore();
     }
 
     private boolean shouldShowDownloadAgain() {
@@ -311,15 +271,19 @@
         return mAttachment.supportsDownloadAgain() && mAttachment.isDownloadFinishedOrFailed();
     }
 
+    private boolean shouldCanInstall() {
+        return mAttachment.isInstallable();
+    }
+
     private boolean shouldShowExtraOption1() {
         return !mHideExtraOptionOne &&
                 mActionHandler.shouldShowExtraOption1(mAccount.getType(),
                         mAttachment.getContentType());
     }
 
-    private boolean shouldShowOverflow() {
-        return (shouldShowPreview() || shouldShowSave() || shouldShowDownloadAgain() ||
-                shouldShowExtraOption1()) && !shouldShowCancel();
+    private boolean shouldShowSaveAttachment() {
+        return (shouldShowPreview() || shouldShowSave() || shouldCanInstall())
+                && !shouldShowCancel();
     }
 
     private boolean shouldShowCancel() {
@@ -338,8 +302,16 @@
                 | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
 
         final String contentType = mAttachment.getContentType();
+
+        Uri contentUri = mAttachment.contentUri;
+        String contentUriString = mAttachment.contentUri.toString();
+        if (contentUriString.endsWith(FORMAT_RAW)) {
+            contentUri = Uri.parse(contentUriString.replace(FORMAT_RAW,
+                    mAttachment.getName()));
+        }
+
         Utils.setIntentDataAndTypeAndNormalize(
-                intent, mAttachment.contentUri, contentType);
+                intent, contentUri, contentType);
 
         // For EML files, we want to open our dedicated
         // viewer rather than let any activity open it.
@@ -391,7 +363,8 @@
         // To avoid visibility state transition bugs, every button's visibility should be touched
         // once by this routine.
         setButtonVisible(mCancelButton, shouldShowCancel());
-        setButtonVisible(mOverflowButton, shouldShowOverflow());
+        setButtonVisible(mSaveAttachmentButton, shouldShowSaveAttachment());
+        setButtonVisible(mAttachmentBarDivider, shouldShowSaveAttachment());
     }
 
     @Override
@@ -432,4 +405,17 @@
         }
         mSubTitle.setText(sb.toString());
     }
+
+    private class LoadMoreAction extends AsyncQueryHandler {
+        private final Uri mLoadMoreUri;
+
+        public LoadMoreAction(ContentResolver resolver, Uri loadMoreUri) {
+            super(resolver);
+            mLoadMoreUri = loadMoreUri;
+        }
+
+        public void sendCommand() {
+            startQuery(0, null, mLoadMoreUri, null, null, null, null);
+        }
+    }
 }
diff --git a/src/com/android/mail/browse/MessageFooterView.java b/src/com/android/mail/browse/MessageFooterView.java
old mode 100644
new mode 100755
index d5b65d7..3cd25fa
--- a/src/com/android/mail/browse/MessageFooterView.java
+++ b/src/com/android/mail/browse/MessageFooterView.java
@@ -40,6 +40,7 @@
 import com.android.mail.providers.Account;
 import com.android.mail.providers.Attachment;
 import com.android.mail.providers.Message;
+import com.android.mail.providers.UIProvider.MessageFlagLoaded;
 import com.android.mail.ui.AccountFeedbackActivity;
 import com.android.mail.ui.AttachmentTile;
 import com.android.mail.ui.AttachmentTileGrid;
@@ -59,6 +60,7 @@
     private FragmentManager mFragmentManager;
     private AttachmentCursor mAttachmentsCursor;
     private View mViewEntireMessagePrompt;
+    private LinearLayout mAttachmentLoadMore;
     private AttachmentTileGrid mAttachmentGrid;
     private LinearLayout mAttachmentBarList;
 
@@ -100,6 +102,7 @@
         super.onFinishInflate();
 
         mViewEntireMessagePrompt = findViewById(R.id.view_entire_message_prompt);
+        mAttachmentLoadMore = (LinearLayout) findViewById(R.id.attachment_placeholder_load_more);
         mAttachmentGrid = (AttachmentTileGrid) findViewById(R.id.attachment_tile_grid);
         mAttachmentBarList = (LinearLayout) findViewById(R.id.attachment_bar_list);
 
@@ -129,9 +132,11 @@
             // attachmentsListUri changes so that we don't
             // repeat the work of layout and measure when
             // we're only updating the attachments.
+            mAttachmentLoadMore.removeAllViewsInLayout();
             mAttachmentGrid.removeAllViewsInLayout();
             mAttachmentBarList.removeAllViewsInLayout();
             mViewEntireMessagePrompt.setVisibility(View.GONE);
+            mAttachmentLoadMore.setVisibility(View.GONE);
             mAttachmentGrid.setVisibility(View.GONE);
             mAttachmentBarList.setVisibility(View.GONE);
         }
@@ -146,14 +151,18 @@
         }
 
         // Do an initial render if initLoader didn't already do one
-        if (mAttachmentGrid.getChildCount() == 0 &&
-                mAttachmentBarList.getChildCount() == 0) {
+        if (mAttachmentLoadMore.getChildCount() == 0
+                && mAttachmentGrid.getChildCount() == 0
+                && mAttachmentBarList.getChildCount() == 0) {
             renderAttachments(false);
         }
 
         final ConversationMessage message = mMessageHeaderItem.getMessage();
         mViewEntireMessagePrompt.setVisibility(
                 message.clipped && !TextUtils.isEmpty(message.permalink) ? VISIBLE : GONE);
+        boolean showLoadMore =
+                message.messageFlagLoaded == MessageFlagLoaded.FLAG_LOADED_PARTIAL_COMPLETE;
+        mAttachmentLoadMore.setVisibility(showLoadMore ? View.VISIBLE : View.GONE);
         setVisibility(mMessageHeaderItem.isExpanded() ? VISIBLE : GONE);
     }
 
@@ -179,6 +188,7 @@
         }
 
         // filter the attachments into tiled and non-tiled
+        Attachment loadMore = null;
         final int maxSize = attachments.size();
         final List<Attachment> tiledAttachments = new ArrayList<Attachment>(maxSize);
         final List<Attachment> barAttachments = new ArrayList<Attachment>(maxSize);
@@ -187,20 +197,26 @@
             // attachments in secure views are displayed in the footer so the user may interact with
             // them; for normal views there is no need to show inline attachments in the footer
             // since users can interact with them in place
-            if (!attachment.isInlineAttachment() || mCallbacks.isSecure()) {
-                if (AttachmentTile.isTiledAttachment(attachment)) {
-                    tiledAttachments.add(attachment);
-                } else {
-                    barAttachments.add(attachment);
-                }
+            if (attachment.isInlineAttachment()) {
+                LogUtils.d(LOG_TAG, "attachment(" + attachment.contentUri
+                        + ") is inline attachment. Ignore and do not show it!");
+                continue;
+            }
+
+            if (attachment.isLoadMore()) {
+                loadMore = attachment;
+                loadMore.messageLoadMoreUri = mMessageHeaderItem.getMessage().loadMoreUri;
+            } else if (AttachmentTile.isTiledAttachment(attachment)) {
+                tiledAttachments.add(attachment);
+            } else {
+                barAttachments.add(attachment);
             }
         }
 
         mMessageHeaderItem.getMessage().attachmentsJson = Attachment.toJSONArray(attachments);
 
-        // All attachments are inline, don't display anything.
-        if (tiledAttachments.isEmpty() && barAttachments.isEmpty()) {
-            return;
+        if (loadMore != null) {
+            renderLoadMore(loadMore, loaderResult);
         }
 
         if (!tiledAttachments.isEmpty()) {
@@ -211,6 +227,13 @@
         }
     }
 
+    private void renderLoadMore(Attachment loadMore, boolean loaderResult) {
+        if (loadMore == null) return;
+
+        mAttachmentLoadMore.setVisibility(View.VISIBLE);
+        renderAttachment(mAttachmentLoadMore, getAccount(), loadMore, loaderResult);
+    }
+
     private void renderTiledAttachments(List<Attachment> tiledAttachments, boolean loaderResult) {
         mAttachmentGrid.setVisibility(View.VISIBLE);
 
@@ -224,22 +247,27 @@
 
         final Account account = getAccount();
         for (Attachment attachment : barAttachments) {
-            final Uri id = attachment.getIdentifierUri();
-            MessageAttachmentBar barAttachmentView =
-                    (MessageAttachmentBar) mAttachmentBarList.findViewWithTag(id);
-
-            if (barAttachmentView == null) {
-                barAttachmentView = MessageAttachmentBar.inflate(mInflater, this);
-                barAttachmentView.setTag(id);
-                barAttachmentView.initialize(mFragmentManager);
-                mAttachmentBarList.addView(barAttachmentView);
-            }
-
-            barAttachmentView.render(attachment, account, mMessageHeaderItem.getMessage(),
-                    loaderResult, getBidiFormatter());
+            renderAttachment(mAttachmentBarList, account, attachment, loaderResult);
         }
     }
 
+    private void renderAttachment(LinearLayout parentView, Account account, Attachment attachment,
+            boolean loaderResult) {
+        final Uri id = attachment.getIdentifierUri();
+        MessageAttachmentBar barAttachmentView =
+                (MessageAttachmentBar) parentView.findViewWithTag(id);
+
+        if (barAttachmentView == null) {
+            barAttachmentView = MessageAttachmentBar.inflate(mInflater, this);
+            barAttachmentView.setTag(id);
+            barAttachmentView.initialize(mFragmentManager);
+            parentView.addView(barAttachmentView);
+        }
+
+        barAttachmentView.render(attachment, account, mMessageHeaderItem.getMessage(),
+                loaderResult, getBidiFormatter());
+    }
+
     private Integer getAttachmentLoaderId() {
         Integer id = null;
         final Message msg = mMessageHeaderItem == null ? null : mMessageHeaderItem.getMessage();
diff --git a/src/com/android/mail/browse/MessageHeaderView.java b/src/com/android/mail/browse/MessageHeaderView.java
old mode 100644
new mode 100755
index 724125e..da620c0
--- a/src/com/android/mail/browse/MessageHeaderView.java
+++ b/src/com/android/mail/browse/MessageHeaderView.java
@@ -58,6 +58,8 @@
 import com.android.mail.providers.Message;
 import com.android.mail.providers.Settings;
 import com.android.mail.providers.UIProvider;
+import com.android.mail.providers.UIProvider.AccountCapabilities;
+import com.android.mail.providers.UIProvider.MessageFlagLoaded;
 import com.android.mail.text.EmailAddressSpan;
 import com.android.mail.ui.AbstractConversationViewFragment;
 import com.android.mail.ui.ImageCanvas;
@@ -923,6 +925,8 @@
                     text + "\n\n" + mCallbacks.getMessageTransforms(mMessage));
         } else if (id == R.id.edit_draft) {
             ComposeActivity.editDraft(getContext(), getAccount(), mMessage);
+        } else if (id == R.id.load_more) {
+            mMessage.loadMore();
         } else if (id == R.id.overflow) {
             if (mPopup == null) {
                 mPopup = new PopupMenu(getContext(), v);
@@ -937,6 +941,12 @@
             m.findItem(R.id.reply_all).setVisible(!defaultReplyAll);
             m.findItem(R.id.print_message).setVisible(Utils.isRunningKitkatOrLater());
 
+            // Update the load more menu visible value.
+            MenuItem fetch = m.findItem(R.id.load_more);
+            if (fetch != null) {
+                fetch.setVisible(mMessage.messageFlagLoaded
+                        == MessageFlagLoaded.FLAG_LOADED_PARTIAL_COMPLETE);
+            }
             final boolean isStarred = mMessage.starred;
             boolean showStar = true;
             final Conversation conversation = mMessage.getConversation();
diff --git a/src/com/android/mail/compose/ComposeActivity.java b/src/com/android/mail/compose/ComposeActivity.java
index 7d68344..ab60e55 100644
--- a/src/com/android/mail/compose/ComposeActivity.java
+++ b/src/com/android/mail/compose/ComposeActivity.java
@@ -156,7 +156,8 @@
     public static final int REPLY = 0;
     public static final int REPLY_ALL = 1;
     public static final int FORWARD = 2;
-    public static final int EDIT_DRAFT = 3;
+    public static final int FORWARD_DROP_UNLOADED_ATTS = 3;
+    public static final int EDIT_DRAFT = 4;
 
     // Integer extra holding one of the above compose action
     protected static final String EXTRA_ACTION = "action";
@@ -346,6 +347,7 @@
     private boolean mRespondedInline;
     private boolean mPerformedSendOrDiscard = false;
 
+    private ArrayList<Attachment> mInsertAttachments;
     // OnKeyListener solely used for intercepting CTRL+ENTER event for SEND.
     private final View.OnKeyListener mKeyListenerForSendShortcut = new View.OnKeyListener() {
         @Override
@@ -528,7 +530,7 @@
             actionBar.setIcon(null);
             actionBar.setDisplayUseLogoEnabled(false);
         }
-
+        mInsertAttachments = new ArrayList<Attachment>();
         mInnerSavedState = (savedInstanceState != null) ?
                 savedInstanceState.getBundle(KEY_INNER_SAVED_STATE) : null;
         checkValidAccounts();
@@ -1568,8 +1570,19 @@
         }
         initRecipientsFromRefMessage(mRefMessage, action);
         initQuotedTextFromRefMessage(mRefMessage, action);
-        if (action == ComposeActivity.FORWARD || mAttachmentsChanged) {
-            initAttachments(mRefMessage);
+        if (action == ComposeActivity.FORWARD
+                || action == ComposeActivity.FORWARD_DROP_UNLOADED_ATTS
+                || mAttachmentsChanged) {
+            if ((action == REPLY || action == REPLY_ALL)) {
+                if (mInsertAttachments != null && mInsertAttachments.size() != 0) {
+                    addAttachments(mInsertAttachments,
+                            action == ComposeActivity.FORWARD_DROP_UNLOADED_ATTS);
+                }
+            } else {
+                initAttachments(mRefMessage,
+                        action == ComposeActivity.FORWARD_DROP_UNLOADED_ATTS);
+            }
+
         }
     }
 
@@ -1856,10 +1869,21 @@
 
     @VisibleForTesting
     protected void initAttachments(Message refMessage) {
-        addAttachments(refMessage.getAttachments());
+        initAttachments(refMessage, false);
     }
 
-    public long addAttachments(List<Attachment> attachments) {
+    @VisibleForTesting
+    protected void initAttachments(Message refMessage, boolean dropUnloaded) {
+        if (mInsertAttachments != null && mInsertAttachments.size() != 0) {
+            mInsertAttachments.addAll(refMessage.getAttachments());
+            addAttachments(mInsertAttachments, dropUnloaded);
+        } else {
+            addAttachments(refMessage.getAttachments(), dropUnloaded);
+        }
+
+    }
+
+    public long addAttachments(List<Attachment> attachments, boolean dropUnloaded) {
         long size = 0;
         AttachmentFailureException error = null;
         for (Attachment a : attachments) {
@@ -2002,7 +2026,7 @@
                 showErrorToast(getString(R.string.attachment_permission_denied));
             }
         }
-        return addAttachments(attachments);
+        return addAttachments(attachments, false);
     }
 
     protected void initQuotedText(CharSequence quotedText, boolean shouldQuoteText) {
@@ -2505,6 +2529,7 @@
         sendOrSaveWithSanityChecks(false, true, false, false);
         logSendOrSave(false /* save */);
         mPerformedSendOrDiscard = true;
+        mInsertAttachments.clear();
     }
 
     private void doSave(boolean showToast) {
@@ -3575,9 +3600,16 @@
         // 1) Body
         // 2) Attachments
         // If the user made changes to attachments, keep their changes.
-        if (!mAttachmentsChanged) {
-            mAttachmentsView.deleteAllAttachments();
+        if (mAttachmentsChanged && mRefMessage != null) {
+            mInsertAttachments.clear();
+            for (Attachment attachment : mAttachmentsView.getAttachments()) {
+                if (!((ArrayList<Attachment>) mRefMessage.getAttachments()).contains(attachment)) {
+                    mInsertAttachments.add(attachment);
+                }
+            }
         }
+        mAttachmentsView.deleteAllAttachments();
+
     }
 
     private class ComposeModeAdapter extends ArrayAdapter<String> {
@@ -3751,7 +3783,9 @@
                 if (!mAccount.expungeMessageUri.equals(Uri.EMPTY)) {
                     getContentResolver().update(mAccount.expungeMessageUri, values, null, null);
                 } else {
-                    getContentResolver().delete(mDraft.uri, null, null);
+                    if (mDraft.uri != null) {
+                        getContentResolver().delete(mDraft.uri, null, null);
+                    }
                 }
                 // This is not strictly necessary (since we should not try to
                 // save the draft after calling this) but it ensures that if we
diff --git a/src/com/android/mail/photo/MailPhotoViewActivity.java b/src/com/android/mail/photo/MailPhotoViewActivity.java
index 2926d3f..7f379cf 100644
--- a/src/com/android/mail/photo/MailPhotoViewActivity.java
+++ b/src/com/android/mail/photo/MailPhotoViewActivity.java
@@ -51,6 +51,9 @@
      */
     public static void startMailPhotoViewActivity(final Context context, final String account,
             final String accountType, final ConversationMessage msg, final int photoIndex) {
+        if (msg.attachmentListUri == null) {
+            return;
+        }
         final Intents.PhotoViewIntentBuilder builder =
                 Intents.newPhotoViewIntentBuilder(context,
                         context.getString(R.string.photo_view_activity));
diff --git a/src/com/android/mail/providers/Attachment.java b/src/com/android/mail/providers/Attachment.java
old mode 100644
new mode 100755
index af4b3e3..33f01df
--- a/src/com/android/mail/providers/Attachment.java
+++ b/src/com/android/mail/providers/Attachment.java
@@ -168,6 +168,13 @@
     private boolean supportsDownloadAgain;
 
 
+    /**
+     * Might be null.
+     *
+     * @see MessageColumns#MESSAGE_LOAD_MORE_URI
+     */
+    public Uri messageLoadMoreUri = null;
+
     public Attachment() {
     }
 
@@ -186,6 +193,8 @@
         supportsDownloadAgain = in.readInt() == 1;
         type = in.readInt();
         flags = in.readInt();
+        partId = in.readString();
+        messageLoadMoreUri = in.readParcelable(null);
     }
 
     public Attachment(Cursor cursor) {
@@ -211,6 +220,7 @@
                 cursor.getColumnIndex(AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN)) == 1;
         type = cursor.getInt(cursor.getColumnIndex(AttachmentColumns.TYPE));
         flags = cursor.getInt(cursor.getColumnIndex(AttachmentColumns.FLAGS));
+        partId = cursor.getString(cursor.getColumnIndex(AttachmentColumns.CONTENT_ID));
     }
 
     public Attachment(JSONObject srcJson) {
@@ -228,6 +238,7 @@
         supportsDownloadAgain = srcJson.optBoolean(AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, true);
         type = srcJson.optInt(AttachmentColumns.TYPE);
         flags = srcJson.optInt(AttachmentColumns.FLAGS);
+        partId = srcJson.optString(AttachmentColumns.CONTENT_ID, null);
     }
 
     /**
@@ -262,6 +273,7 @@
             type = inline ? AttachmentType.INLINE_CURRENT_MESSAGE : AttachmentType.STANDARD;
             partId = cid;
             flags = 0;
+            messageLoadMoreUri = null;
 
             // insert attachment into content provider so that we can open the file
             final ContentResolver resolver = context.getContentResolver();
@@ -353,6 +365,8 @@
         dest.writeInt(supportsDownloadAgain ? 1 : 0);
         dest.writeInt(type);
         dest.writeInt(flags);
+        dest.writeString(partId);
+        dest.writeParcelable(messageLoadMoreUri, flags);
     }
 
     public JSONObject toJSON() throws JSONException {
@@ -372,6 +386,7 @@
         result.put(AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, supportsDownloadAgain);
         result.put(AttachmentColumns.TYPE, type);
         result.put(AttachmentColumns.FLAGS, flags);
+        result.put(AttachmentColumns.CONTENT_ID, partId);
 
         return result;
     }
@@ -420,7 +435,7 @@
     }
 
     public boolean canSave() {
-        return !isSavedToExternal() && !isInstallable();
+        return !isInstallable();
     }
 
     public boolean canShare() {
@@ -452,6 +467,10 @@
         return state == AttachmentState.FAILED || state == AttachmentState.SAVED;
     }
 
+    public boolean isLoadMore() {
+        return (flags & Attachment.FLAG_DUMMY_ATTACHMENT) != 0;
+    }
+
     public boolean supportsDownloadAgain() {
         return supportsDownloadAgain;
     }
@@ -531,7 +550,7 @@
      * quoted text).
      */
     public boolean isInlineAttachment() {
-        return type != UIProvider.AttachmentType.STANDARD;
+        return type != UIProvider.AttachmentType.STANDARD && !TextUtils.isEmpty(partId);
     }
 
     @Override
diff --git a/src/com/android/mail/providers/Message.java b/src/com/android/mail/providers/Message.java
old mode 100644
new mode 100755
index b90f76b..efe78b1
--- a/src/com/android/mail/providers/Message.java
+++ b/src/com/android/mail/providers/Message.java
@@ -217,6 +217,15 @@
      */
     public String permalink;
 
+    /**
+     * @see UIProvider.MessageColumns#MESSAGE_FLAG_LOADED
+     */
+    public int messageFlagLoaded;
+    /**
+     * @see UIProvider.MessageColumns#MESSAGE_LOAD_MORE_URI
+     */
+    public Uri loadMoreUri;
+
     private transient String[] mFromAddresses = null;
     private transient String[] mToAddresses = null;
     private transient String[] mCcAddresses = null;
@@ -298,6 +307,8 @@
         dest.writeInt(sendingState);
         dest.writeInt(clipped ? 1 : 0);
         dest.writeString(permalink);
+        dest.writeInt(messageFlagLoaded);
+        dest.writeParcelable(loadMoreUri, 0);
     }
 
     private Message(Parcel in) {
@@ -334,6 +345,8 @@
         sendingState = in.readInt();
         clipped = in.readInt() != 0;
         permalink = in.readString();
+        messageFlagLoaded = in.readInt();
+        loadMoreUri = in.readParcelable(null);
     }
 
     public Message() {
@@ -414,6 +427,9 @@
             sendingState = cursor.getInt(UIProvider.MESSAGE_SENDING_STATE_COLUMN);
             clipped = cursor.getInt(UIProvider.MESSAGE_CLIPPED_COLUMN) != 0;
             permalink = cursor.getString(UIProvider.MESSAGE_PERMALINK_COLUMN);
+            messageFlagLoaded = cursor.getInt(UIProvider.MESSAGE_FLAG_LOADED_COLUMN);
+            loadMoreUri = Utils.getValidUri(
+                    cursor.getString(UIProvider.MESSAGE_LOAD_MORE_URI_COLUMN));
         }
     }
 
diff --git a/src/com/android/mail/providers/UIProvider.java b/src/com/android/mail/providers/UIProvider.java
old mode 100644
new mode 100755
index 783e4f3..63c55a1
--- a/src/com/android/mail/providers/UIProvider.java
+++ b/src/com/android/mail/providers/UIProvider.java
@@ -1625,7 +1625,9 @@
         MessageColumns.VIA_DOMAIN,
         MessageColumns.SENDING_STATE,
         MessageColumns.CLIPPED,
-        MessageColumns.PERMALINK
+        MessageColumns.PERMALINK,
+        MessageColumns.MESSAGE_FLAG_LOADED,
+        MessageColumns.MESSAGE_LOAD_MORE_URI
     };
 
     /** Separates attachment info parts in strings in a message. */
@@ -1674,6 +1676,8 @@
     public static final int MESSAGE_SENDING_STATE_COLUMN = 35;
     public static final int MESSAGE_CLIPPED_COLUMN = 36;
     public static final int MESSAGE_PERMALINK_COLUMN = 37;
+    public static final int MESSAGE_FLAG_LOADED_COLUMN = 38;
+    public static final int MESSAGE_LOAD_MORE_URI_COLUMN = 39;
 
     public static final class CursorStatus {
         // The cursor is actively loading more data
@@ -1730,6 +1734,19 @@
         public static final int CALENDAR_INVITE =   1 << 4;
     }
 
+    /**
+     * These values are also defined in the EmailContent.
+     */
+    public static final class MessageFlagLoaded {
+        public static final int FLAG_LOADED_UNLOADED = 0;
+        public static final int FLAG_LOADED_COMPLETE = 1;
+        public static final int FLAG_LOADED_PARTIAL = 2;
+        public static final int FLAG_LOADED_PARTIAL_COMPLETE = 3;
+        public static final int FLAG_LOADED_PARTIAL_FETCHING = 4;
+        public static final int FLAG_LOADED_DELETED = 5;
+        public static final int FLAG_LOADED_UNKNOWN = 6;
+    }
+
     public static final class MessageColumns {
         /**
          * This string column contains a content provider URI that points to this single message.
@@ -1904,6 +1921,17 @@
          */
         public static final String PERMALINK = "permalink";
 
+        /**
+         * This integer column indicates the state of the message loaded
+         * and it defined in {@link MessageFlagLoaded}
+         */
+        public static final String MESSAGE_FLAG_LOADED = "messageFlagLoaded";
+
+        /**
+         * String with the content provider Uri used to request fetch entire content.
+         */
+        public static final String MESSAGE_LOAD_MORE_URI = "messageLoadMoreUri";
+
         private MessageColumns() {}
     }
 
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
old mode 100644
new mode 100755
index a7de33b..569b0e9
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -26,6 +26,7 @@
 import android.app.FragmentManager;
 import android.app.LoaderManager;
 import android.app.SearchManager;
+import android.content.AsyncQueryHandler;
 import android.content.ContentProviderOperation;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -50,6 +51,7 @@
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.app.ActionBar;
 import android.support.v7.app.ActionBarDrawerToggle;
+import android.text.TextUtils;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.Menu;
@@ -60,6 +62,7 @@
 import android.widget.ListView;
 import android.widget.Toast;
 
+import com.android.emailcommon.service.SearchParams;
 import com.android.mail.ConversationListContext;
 import com.android.mail.MailLogService;
 import com.android.mail.R;
@@ -442,6 +445,8 @@
      */
     public static final int LAST_FRAGMENT_LOADER_ID = 1000;
 
+    private static final int LOADER_LOCALSEARCH_CONVERSATION_LIST = 1100;
+
     /** Code returned after an account has been added. */
     private static final int ADD_ACCOUNT_REQUEST_CODE = 1;
     /** Code returned when the user has to enter the new password on an existing account. */
@@ -502,6 +507,10 @@
     private final MailDrawerListener mDrawerListener = new MailDrawerListener();
     private boolean mHideMenuItems;
 
+    private int mFolderType;
+    private final static String UI_MESSAGES = "uimessages";
+    private final static String UI_LOCAL_SEARCH = "uilocalsearch";
+
     private final DrawIdler mDrawIdler = new DrawIdler();
 
     public static final String SYNC_ERROR_DIALOG_FRAGMENT_TAG = "SyncErrorDialogFragment";
@@ -555,7 +564,7 @@
         return mAccount;
     }
 
-    public ConversationListContext getCurrentListContext() {
+    public ConversationListContext getCurrentConversationListContext() {
         return mConvListContext;
     }
 
@@ -939,6 +948,7 @@
                 || (mViewMode.getMode() != ViewMode.CONVERSATION_LIST)) {
             setListContext(folder, query);
             showConversationList(mConvListContext);
+            mSearchViewController.restoreLocalSearch();
             // Touch the current folder: it is different, and it has been accessed.
             if (mFolder != null) {
                 mRecentFolderList.touchFolder(mFolder, mAccount);
@@ -1057,24 +1067,11 @@
         // The first time this runs when the activity is [re-]initialized, we want to re-use the
         // previous loader's instance and data upon configuration change (e.g. rotation).
         // If there was not already an instance of the loader, init it.
-        if (lm.getLoader(LOADER_FOLDER_CURSOR) == null) {
-            lm.initLoader(LOADER_FOLDER_CURSOR, Bundle.EMPTY, mFolderCallbacks);
-        } else {
-            lm.restartLoader(LOADER_FOLDER_CURSOR, Bundle.EMPTY, mFolderCallbacks);
+        final ConversationListFragment conversationList = getConversationListFragment();
+        if (conversationList != null) {
+            conversationList.getListView().setEmptyView(null);
         }
-        if (!wasNull && lm.getLoader(LOADER_CONVERSATION_LIST) != null) {
-            // If there was an existing folder AND we have changed
-            // folders, we want to restart the loader to get the information
-            // for the newly selected folder
-            lm.destroyLoader(LOADER_CONVERSATION_LIST);
-        }
-        final Bundle args = new Bundle(2);
-        args.putParcelable(BUNDLE_ACCOUNT_KEY, mAccount);
-        args.putParcelable(BUNDLE_FOLDER_KEY, mFolder);
-        args.putBoolean(BUNDLE_IGNORE_INITIAL_CONVERSATION_LIMIT_KEY,
-                mIgnoreInitialConversationLimit);
-        mIgnoreInitialConversationLimit = false;
-        lm.initLoader(LOADER_CONVERSATION_LIST, args, mListCursorCallbacks);
+        loadConversationListData(true);
     }
 
     @Override
@@ -1177,7 +1174,7 @@
     @Override
     public void onConversationListVisibilityChanged(boolean visible) {
         mFloatingComposeButton.setVisibility(
-                !ViewMode.isSearchMode(mViewMode.getMode()) && visible ? View.VISIBLE : View.GONE);
+                !mSearchViewController.isOnlyActionbar() && visible ? View.VISIBLE : View.GONE);
 
         informCursorVisiblity(visible);
         commitAutoAdvanceOperation();
@@ -1367,6 +1364,8 @@
         // Create the accounts loader; this loads the account switch spinner.
         mActivity.getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, Bundle.EMPTY,
                 mAccountCallbacks);
+
+        mSearchViewController.restoreLocalSearch();
     }
 
     /**
@@ -2063,6 +2062,27 @@
     }
 
     @Override
+    public void loadMore(ConversationMessage msg) {
+        if (msg != null && msg.loadMoreUri != null) {
+            LoadMoreAction action = new LoadMoreAction(mResolver, msg.loadMoreUri);
+            action.sendCommand();
+        }
+    }
+
+    private class LoadMoreAction extends AsyncQueryHandler {
+        private final Uri mLoadMoreUri;
+
+        public LoadMoreAction(ContentResolver resolver, Uri loadMoreUri) {
+            super(resolver);
+            mLoadMoreUri = loadMoreUri;
+        }
+
+        public void sendCommand() {
+            startQuery(0, null, mLoadMoreUri, null, null, null, null);
+        }
+    }
+
+    @Override
     public void requestFolderRefresh() {
         if (mFolder == null) {
             return;
@@ -2241,18 +2261,16 @@
     }
 
     @Override
-    public void executeSearch(String query) {
+    public void executeSearch(String query ,String factor,boolean isUser) {
+        mSearchViewController.saveRecentQuery(query);
         AnalyticsTimer.getInstance().trackStart(AnalyticsTimer.SEARCH_TO_LIST);
-        Intent intent = new Intent();
-        intent.setAction(Intent.ACTION_SEARCH);
-        intent.putExtra(ConversationListContext.EXTRA_SEARCH_QUERY, query);
-        intent.putExtra(Utils.EXTRA_ACCOUNT, mAccount);
-        intent.setComponent(mActivity.getComponentName());
         mSearchViewController.showSearchActionBar(
-                MaterialSearchViewController.SEARCH_VIEW_STATE_GONE);
-        // Call startActivityForResult here so we can tell if we have navigated to a different folder
-        // or account from search results.
-        mActivity.startActivityForResult(intent, CHANGE_NAVIGATION_REQUEST_CODE);
+                MaterialSearchViewController.SEARCH_VIEW_STATE_ONLY_ACTIONBAR);
+        buildLocalSearch(query, factor,isUser);
+        if(!mConvListContext.isLocalSearch()){
+            return;
+        }
+        loadConversationListData(false);
     }
 
     @Override
@@ -2503,23 +2521,6 @@
             args.putParcelable(Utils.EXTRA_CONVERSATION,
                     intent.getParcelableExtra(Utils.EXTRA_CONVERSATION));
             restartOptionalLoader(LOADER_FIRST_FOLDER, mFolderCallbacks, args);
-        } else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
-            if (intent.hasExtra(Utils.EXTRA_ACCOUNT)) {
-                mHaveSearchResults = false;
-                // Save this search query for future suggestions
-                final String query = intent.getStringExtra(SearchManager.QUERY);
-                mSearchViewController.saveRecentQuery(query);
-                setAccount((Account) intent.getParcelableExtra(Utils.EXTRA_ACCOUNT));
-                fetchSearchFolder(intent);
-                if (shouldEnterSearchConvMode()) {
-                    mViewMode.enterSearchResultsConversationMode();
-                } else {
-                    mViewMode.enterSearchResultsListMode();
-                }
-            } else {
-                LogUtils.e(LOG_TAG, "Missing account extra from search intent.  Finishing");
-                mActivity.finish();
-            }
         }
         if (mAccount != null) {
             restartOptionalLoader(LOADER_ACCOUNT_UPDATE_CURSOR, mAccountCallbacks, Bundle.EMPTY);
@@ -2663,6 +2664,8 @@
         final ConversationListFragment convListFragment = getConversationListFragment();
         if (convListFragment != null && convListFragment.getAnimatedAdapter() != null) {
             convListFragment.getAnimatedAdapter().onConversationSelected();
+            mSearchViewController
+                    .showSearchActionBar(MaterialSearchViewController.SEARCH_VIEW_STATE_GONE);
         }
         // Only animate destructive actions if we are going to be showing the
         // conversation list when we show the next conversation.
@@ -3411,8 +3414,9 @@
             if (account == null || folder == null) {
                 return null;
             }
-            return new ConversationCursorLoader(mActivity, account,
-                    folder.conversationListUri, folder.getTypeDescription(),
+
+            return new ConversationCursorLoader((Activity) mActivity,
+                    account, getCurrentFolderConversationListUri(id, args, folder), folder.name,
                     ignoreInitialConversationLimit);
         }
 
@@ -3450,6 +3454,9 @@
                 informCursorVisiblity(true);
             }
             perhapsShowFirstConversation();
+
+            showLocalSearchResult(loader, data);
+
         }
 
         @Override
@@ -3471,6 +3478,18 @@
         }
     }
 
+    public void updateSearchResultCount(int count) {
+
+        if (mSearchViewController != null) {
+            if (count == 0
+                    && SearchParams.SEARCH_FACTOR_ALL.equals(mConvListContext.getSearchFactor())) {
+                mSearchViewController.updateSearchResultCount(count, true);
+            } else {
+                mSearchViewController.updateSearchResultCount(count, false);
+            }
+        }
+    }
+
     /**
      * Class to perform {@link LoaderManager.LoaderCallbacks} for creating {@link Folder} objects.
      */
@@ -4462,4 +4481,148 @@
             handleUpPress();
         }
     }
+
+
+    private void loadConversationListData(boolean isFolderUpdated) {
+        if (mFolder == null || !mFolder.isInitialized()) {
+            return;
+        }
+        final LoaderManager lm = mActivity.getLoaderManager();
+
+        if (mConvListContext != null && mConvListContext.isLocalSearchExecuted()) {
+            executedLocalSearchLoader(lm);
+        } else {
+            executedChangeFolderLoader(lm, isFolderUpdated);
+        }
+    }
+
+
+    private void executedLocalSearchLoader(LoaderManager lm) {
+        final Bundle args = new Bundle(4);
+        args.putParcelable(BUNDLE_ACCOUNT_KEY, mAccount);
+        args.putParcelable(BUNDLE_FOLDER_KEY, mFolder);
+        args.putString(SearchParams.BUNDLE_QUERY_FACTOR, mConvListContext.getSearchFactor());
+        args.putString(SearchParams.BUNDLE_QUERY_FILTER, mConvListContext.getSearchQuery());
+
+        if (lm.getLoader(LOADER_CONVERSATION_LIST) != null) {
+            lm.destroyLoader(LOADER_CONVERSATION_LIST);
+        }
+
+        lm.restartLoader(LOADER_LOCALSEARCH_CONVERSATION_LIST, args, mListCursorCallbacks);
+    }
+
+    private void executedChangeFolderLoader(LoaderManager lm, boolean isFolderUpdated) {
+        if (lm.getLoader(LOADER_LOCALSEARCH_CONVERSATION_LIST) != null) {
+            lm.destroyLoader(LOADER_LOCALSEARCH_CONVERSATION_LIST);
+        }
+
+        final ConversationCursorLoader ccl = (ConversationCursorLoader) ((Object) lm
+                .getLoader(LOADER_CONVERSATION_LIST));
+        if (ccl != null && !ccl.getUri().equals(mFolder.conversationListUri)
+                && isFolderUpdated) {
+            lm.destroyLoader(LOADER_CONVERSATION_LIST);
+        }
+        final Bundle args = new Bundle(2);
+        args.putParcelable(BUNDLE_ACCOUNT_KEY, mAccount);
+        args.putParcelable(BUNDLE_FOLDER_KEY, mFolder);
+        args.putBoolean(BUNDLE_IGNORE_INITIAL_CONVERSATION_LIMIT_KEY,
+                mIgnoreInitialConversationLimit);
+        mIgnoreInitialConversationLimit = false;
+        lm.initLoader(LOADER_CONVERSATION_LIST, args, mListCursorCallbacks);
+    }
+
+    @Override
+    public void buildLocalSearch(String query, String factor, boolean isUser) {
+        mFloatingComposeButton.setVisibility(View.GONE);
+        if (mConvListContext == null || TextUtils.isEmpty(query)) {
+            return;
+        }
+        if (!mConvListContext.isLocalSearch()) {
+            mActivity.invalidateOptionsMenu();
+        }
+        mConvListContext.setSearchQueryText(query);
+        if (factor == null) {
+            mConvListContext.setSearchFactor(SearchParams.SEARCH_FACTOR_ALL);
+        } else {
+            if (isUser) {
+                mCheckedSet.clear();
+            }
+            mConvListContext.setSearchFactor(factor);
+        }
+        mConvListContext.setLocalSearch(true);
+    }
+
+    @Override
+    public void exitLocalSearch() {
+        if (mFolder != null) {
+            mFolder.type = mFolderType;
+        }
+        if (getConversationListFragment() != null) {
+            getConversationListFragment().exitSeachView();
+        }
+        if (mConvListContext == null) {
+            return;
+        }
+        mConvListContext.setLocalSearch(false);
+        mConvListContext.setSearchQueryText(null);
+        mConvListContext.setSearchFactor(null);
+        // refresh conversation list.
+        mFloatingComposeButton.setVisibility(View.VISIBLE);
+        mActivity.invalidateOptionsMenu();
+        loadConversationListData(false);
+    }
+
+    private Uri getCurrentFolderConversationListUri(int id, Bundle args, Folder folder) {
+        Uri uri = folder.conversationListUri;
+        if (id == LOADER_LOCALSEARCH_CONVERSATION_LIST) {
+            String filter = args.getString(SearchParams.BUNDLE_QUERY_FILTER);
+            String factor = args.getString(SearchParams.BUNDLE_QUERY_FACTOR);
+            mFolderType = folder.type;
+            folder.type = FolderType.SEARCH;
+            if (!TextUtils.isEmpty(filter) && !TextUtils.isEmpty(factor)) {
+                uri = buildLocalSearchUri(folder, filter, factor);
+            }
+        }
+        return uri;
+    }
+
+    private Uri buildLocalSearchUri(Folder folder, String queryFilter, String queryFactor) {
+        Uri result = null;
+        if (TextUtils.isEmpty(queryFilter)
+                || TextUtils.isEmpty(queryFactor)) {
+            return folder.conversationListUri;
+        } else {
+            result = Uri.parse(folder.conversationListUri.toString().replace(UI_MESSAGES,
+                    UI_LOCAL_SEARCH));
+        }
+        return result
+                .buildUpon()
+                .appendQueryParameter(SearchParams.BUNDLE_QUERY_FILTER, queryFilter)
+                .appendQueryParameter(SearchParams.BUNDLE_QUERY_FACTOR, queryFactor)
+                .build();
+    }
+
+    private void showLocalSearchResult(Loader<ConversationCursor> loader, ConversationCursor data) {
+        if (loader.getId() == LOADER_LOCALSEARCH_CONVERSATION_LIST) {
+            updateSearchResultCount(data.getCount());
+            mCheckedSet.validateAgainstCursor(mConversationListCursor);
+        } else if (mConvListContext != null && mConvListContext.isLocalSearch()) {
+            updateSearchResultCount(0);
+        }
+
+        if (getConversationListFragment() == null) {
+            if (mActivity == null || mActivity.getFragmentManager().isDestroyed()) {
+                return;
+            }
+            int mode = mViewMode.getMode();
+            switch (mode) {
+                case ViewMode.CONVERSATION_LIST:
+                case ViewMode.SEARCH_RESULTS_LIST:
+                    showConversationList(mConvListContext);
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
 }
diff --git a/src/com/android/mail/ui/AbstractMailActivity.java b/src/com/android/mail/ui/AbstractMailActivity.java
index 00d2f5f..5a8c96c 100644
--- a/src/com/android/mail/ui/AbstractMailActivity.java
+++ b/src/com/android/mail/ui/AbstractMailActivity.java
@@ -56,6 +56,7 @@
             StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                     .detectLeakedSqlLiteObjects()
                     .detectLeakedClosableObjects()
+                    .detectActivityLeaks()
                     .penaltyLog()
                     .build());
         }
diff --git a/src/com/android/mail/ui/AccountFeedbackActivity.java b/src/com/android/mail/ui/AccountFeedbackActivity.java
index 89a736d..74e99e4 100644
--- a/src/com/android/mail/ui/AccountFeedbackActivity.java
+++ b/src/com/android/mail/ui/AccountFeedbackActivity.java
@@ -17,14 +17,6 @@
 
 package com.android.mail.ui;
 
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarActivity;
-import android.view.Menu;
-import android.view.MenuItem;
-
 import com.android.mail.R;
 import com.android.mail.browse.ConversationAccountController;
 import com.android.mail.content.ObjectCursor;
@@ -32,6 +24,14 @@
 import com.android.mail.providers.UIProvider;
 import com.android.mail.utils.Utils;
 
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarActivity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+
 /**
  * Activity that provides support for querying an {@link Account}
  * as well as showing settings/help/send feedback in the action
@@ -57,7 +57,8 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.account_feedback_activity);
 
-        final ActionBar actionBar = getSupportActionBar();
+
+        final ActionBar actionBar = getSupportActionBar();;
         actionBar.setDisplayHomeAsUpEnabled(true);
 
         final Intent intent = getIntent();
diff --git a/src/com/android/mail/ui/ActionBarController.java b/src/com/android/mail/ui/ActionBarController.java
index 217c521..b683ca2 100644
--- a/src/com/android/mail/ui/ActionBarController.java
+++ b/src/com/android/mail/ui/ActionBarController.java
@@ -272,8 +272,8 @@
             case ViewMode.CONVERSATION_LIST:
             case ViewMode.SEARCH_RESULTS_LIST:
                 // The search menu item should only be visible for non-tablet devices
-                Utils.setMenuItemPresent(menu, R.id.search,
-                        mAccount.supportsSearch() && !mIsOnTablet);
+                // enable show search menu for local searching except on tablet UI
+                Utils.setMenuItemPresent(menu, R.id.search, !mIsOnTablet);
         }
 
         return;
diff --git a/src/com/android/mail/ui/ActivityController.java b/src/com/android/mail/ui/ActivityController.java
index f64b275..4281b8d 100644
--- a/src/com/android/mail/ui/ActivityController.java
+++ b/src/com/android/mail/ui/ActivityController.java
@@ -28,6 +28,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 
+import com.android.mail.ConversationListContext;
 import com.android.mail.browse.ConversationCursor.ConversationListener;
 import com.android.mail.browse.ConversationListFooterView;
 import com.android.mail.providers.Folder;
@@ -195,7 +196,7 @@
     /**
      * Begin a search with the given query string.
      */
-    void executeSearch(String query);
+    void executeSearch(String query,String factor,boolean isUser);
 
     /**
      * Called by the Mail activity on Activity stop.
@@ -287,4 +288,19 @@
      * Attach layout listener so our custom toolbar can listen to thread list layout events.
      */
     void addConversationListLayoutListener(TwoPaneLayout.ConversationListLayoutListener listener);
+
+    /**
+     *  when user press search button or switch search filter tab .
+     */
+    void buildLocalSearch(String query,String factor,boolean isUser);
+
+    /**
+     *  Called when user press back key then exit local search .
+     */
+    void exitLocalSearch();
+
+    /**
+     * @return   get abstractActivityControll's current ConversationListContext;
+     */
+    ConversationListContext getCurrentConversationListContext();
 }
diff --git a/src/com/android/mail/ui/AnimatedAdapter.java b/src/com/android/mail/ui/AnimatedAdapter.java
index 2b38d27..7c649fd 100644
--- a/src/com/android/mail/ui/AnimatedAdapter.java
+++ b/src/com/android/mail/ui/AnimatedAdapter.java
@@ -901,6 +901,12 @@
         mHeaders.add(headerView);
     }
 
+    public void removeHeader(View headerView) {
+        if (mHeaders.contains(headerView)) {
+            mHeaders.remove(headerView);
+        }
+    }
+
     public void setFolder(Folder folder) {
         mFolder = folder;
     }
@@ -1195,4 +1201,17 @@
             specialView.saveInstanceState(outState);
         }
     }
+
+    public ArrayList<ConversationSpecialItemView> getmFleetingViews() {
+        return (ArrayList<ConversationSpecialItemView>) mFleetingViews;
+    }
+
+    public void resetmFleetingView(ArrayList<ConversationSpecialItemView> fleetingviews) {
+        if (mFleetingViews != null) {
+            mFleetingViews.clear();
+            if (fleetingviews != null) {
+                mFleetingViews.addAll(fleetingviews);
+            }
+        }
+    }
 }
diff --git a/src/com/android/mail/ui/ConversationCursorLoader.java b/src/com/android/mail/ui/ConversationCursorLoader.java
index f91d015..fe6d4eb 100644
--- a/src/com/android/mail/ui/ConversationCursorLoader.java
+++ b/src/com/android/mail/ui/ConversationCursorLoader.java
@@ -137,4 +137,9 @@
         cancelLoad();
         mConversationCursor.pause();
     }
+
+    public Uri getUri() {
+        return mUri;
+    }
+
 }
diff --git a/src/com/android/mail/ui/ConversationListFragment.java b/src/com/android/mail/ui/ConversationListFragment.java
index 439f6b5..ac65202 100644
--- a/src/com/android/mail/ui/ConversationListFragment.java
+++ b/src/com/android/mail/ui/ConversationListFragment.java
@@ -40,6 +40,7 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
+import com.android.emailcommon.service.SearchParams;
 import com.android.mail.ConversationListContext;
 import com.android.mail.R;
 import com.android.mail.analytics.Analytics;
@@ -59,6 +60,7 @@
 import com.android.mail.providers.UIProvider.AccountCapabilities;
 import com.android.mail.providers.UIProvider.ConversationListIcon;
 import com.android.mail.providers.UIProvider.FolderCapabilities;
+import com.android.mail.providers.UIProvider.FolderType;
 import com.android.mail.providers.UIProvider.Swipe;
 import com.android.mail.ui.SwipeableListView.ListItemSwipedListener;
 import com.android.mail.ui.SwipeableListView.ListItemsRemovedListener;
@@ -71,6 +73,7 @@
 import com.android.mail.utils.ViewUtils;
 import com.google.common.collect.ImmutableList;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
@@ -118,7 +121,6 @@
 
     private View mSearchHeaderView;
     private TextView mSearchResultCountTextView;
-
     /**
      * Current Account being viewed
      */
@@ -175,6 +177,7 @@
     /** Duration, in milliseconds, of the CAB mode (peek icon) animation. */
     private static long sSelectionModeAnimationDuration = -1;
 
+    private ArrayList<ConversationSpecialItemView> mFleetingViews;
     // Let's ensure that we are only showing one out of the three views at once
     private void showListView() {
         setupEmptyIcon(false);
@@ -196,8 +199,9 @@
     private void showEmptyView() {
         // If the callbacks didn't set up the empty icon, then we should show it in the empty view.
         final boolean shouldShowIcon = !setupEmptyIcon(true);
-        mEmptyView.setupEmptyText(mFolder, mViewContext.searchQuery,
-                mListAdapter.getBidiFormatter(), shouldShowIcon);
+        if (!isShowEmptyInSearch(shouldShowIcon)) {
+            return;
+        }
         mListView.setVisibility(View.INVISIBLE);
         mEmptyView.setVisibility(View.VISIBLE);
         mLoadingView.setVisibility(View.INVISIBLE);
@@ -294,15 +298,16 @@
     }
 
     /**
-     * Show the header if the current conversation list is showing search
-     * results.
+     * Show the header if the current conversation list is showing search results.
      */
     private void updateSearchResultHeader(int count) {
         if (mActivity == null || mSearchHeaderView == null) {
             return;
         }
-        mSearchResultCountTextView.setText(
-                getResources().getString(R.string.search_results_loaded, count));
+        if(mFolder != null && mFolder.type == FolderType.SEARCH){
+            mSearchResultCountTextView.setText(
+                    getResources().getString(R.string.search_results_loaded, count));
+        }
     }
 
     @Override
@@ -362,14 +367,6 @@
         mListAdapter = new AnimatedAdapter(mActivity.getApplicationContext(), conversationCursor,
                 mActivity.getCheckedSet(), mActivity, mListView, specialItemViews);
         mListAdapter.addFooter(mFooterView);
-        // Show search result header only if we are in search mode
-        final boolean showSearchHeader = ConversationListContext.isSearchResult(mViewContext);
-        if (showSearchHeader) {
-            mSearchHeaderView = inflater.inflate(R.layout.search_results_view, null);
-            mSearchResultCountTextView = (TextView)
-                    mSearchHeaderView.findViewById(R.id.search_result_count_view);
-            mListAdapter.addHeader(mSearchHeaderView);
-        }
 
         mListView.setAdapter(mListAdapter);
         mCheckedSet = mActivity.getCheckedSet();
@@ -770,6 +767,7 @@
         if (mListAdapter != null) {
             mListAdapter.saveSpecialItemInstanceState(outState);
         }
+
     }
 
     @Override
@@ -1030,8 +1028,8 @@
      */
     public void onConversationListStatusUpdated() {
         // Also change the cursor here.
+        setDisableInSearch();
         onCursorUpdated();
-
         if (isCursorReadyToShow() && mCanTakeDownLoadingView) {
             hideLoadingViewAndShowContents();
         }
@@ -1043,9 +1041,11 @@
         // Update the sync status bar with sync results if needed
         checkSyncStatus();
         mListAdapter.setFooterVisibility(showFooter);
+        if (showFooter && mFolder != null && mFolder.type == FolderType.SEARCH) {
+            mListAdapter.setFooterVisibility(false);
+        }
         mLoadingViewPending = false;
         mHandler.removeCallbacks(mLoadingViewRunnable);
-
         // Even though cursor might be empty, the list adapter might have teasers/footers.
         // So we check the list adapter count if the cursor is fully/partially loaded.
         if (mAccount.securityHold != 0) {
@@ -1376,4 +1376,70 @@
             }
         }
     }
+
+
+    private boolean isShowEmptyInSearch(boolean shouldShowIcon) {
+        ActivityController controller = (ActivityController) mActivity.getAccountController();
+        String queryText = controller != null ? controller.getCurrentConversationListContext()
+                .getSearchParams().mFilter
+                : mViewContext.getSearchQuery();
+        if (controller.getCurrentConversationListContext().isLocalSearch()
+                && !SearchParams.SEARCH_FACTOR_ALL
+                        .equals(controller.getCurrentConversationListContext()
+                                .getSearchFactor())) {
+            return false;
+        }
+        mEmptyView.setupEmptyText(mFolder, queryText,
+                mListAdapter.getBidiFormatter(), shouldShowIcon);
+        return true;
+    }
+
+    private void saveFleetingView() {
+        if (mFleetingViews == null) {
+            mFleetingViews = new ArrayList<>();
+        }
+        if (mListAdapter != null && mSwipeRefreshWidget != null) {
+            mFleetingViews.clear();
+            mFleetingViews.addAll(mListAdapter.getmFleetingViews());
+            if(mFleetingViews.size() != 0){
+                mListAdapter.resetmFleetingView(null);
+            }
+        }
+    }
+
+    public void exitSeachView() {
+        if (mSwipeRefreshWidget != null) {
+            mSwipeRefreshWidget.setEnabled(true);
+        }
+        if (mFleetingViews != null) {
+            mListAdapter.resetmFleetingView(mFleetingViews);
+            mFleetingViews = null;
+        }
+        if(mListAdapter != null){
+            mListAdapter.removeHeader(mSearchHeaderView);
+        }
+    }
+
+    private void setDisableInSearch() {
+        ActivityController controller = (ActivityController) mActivity.getAccountController();
+        boolean isLocalSearch = false;
+        if (controller != null && controller.getCurrentConversationListContext() != null) {
+            isLocalSearch = controller.getCurrentConversationListContext().isLocalSearchExecuted();
+        }
+        if (mFolder != null && mFolder.type == FolderType.SEARCH && isLocalSearch) {
+            saveFleetingView();
+            mSwipeRefreshWidget.setEnabled(false);
+            mListAdapter.setFooterVisibility(false);
+            addHeadView();
+        }
+    }
+
+    private void addHeadView() {
+        mListAdapter.removeHeader(mSearchHeaderView);
+        mSearchHeaderView = LayoutInflater.from(mActivity.getActivityContext()).inflate(
+                R.layout.search_results_view, null);
+        mSearchResultCountTextView = (TextView)
+                mSearchHeaderView.findViewById(R.id.search_result_count_view);
+        mListAdapter.addHeader(mSearchHeaderView);
+    }
 }
diff --git a/src/com/android/mail/ui/ConversationUpdater.java b/src/com/android/mail/ui/ConversationUpdater.java
old mode 100644
new mode 100755
index 745d7d2..9787236
--- a/src/com/android/mail/ui/ConversationUpdater.java
+++ b/src/com/android/mail/ui/ConversationUpdater.java
@@ -127,6 +127,12 @@
     void starMessage(ConversationMessage msg, boolean starred);
 
     /**
+     * Try to fetch the entire content of this message. This method requires a
+     * {@link ConversationMessage} to propagate the change to the owning {@link Conversation}.
+     */
+    void loadMore(ConversationMessage msg);
+
+    /**
      * Get a destructive action for selected conversations. The action corresponds to Menu item
      * identifiers, for example R.id.unread, or R.id.delete.
      * @param action
diff --git a/src/com/android/mail/ui/FolderSelectionDialog.java b/src/com/android/mail/ui/FolderSelectionDialog.java
index 0e1ea1c..ce3a9a7 100644
--- a/src/com/android/mail/ui/FolderSelectionDialog.java
+++ b/src/com/android/mail/ui/FolderSelectionDialog.java
@@ -22,6 +22,7 @@
 import android.app.DialogFragment;
 import android.content.DialogInterface.OnClickListener;
 import android.os.Bundle;
+import android.os.Parcelable;
 import android.view.View;
 import android.widget.AdapterView;
 
@@ -32,6 +33,7 @@
 import com.android.mail.providers.UIProvider;
 import com.android.mail.utils.LogTag;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 
@@ -86,7 +88,13 @@
         mCurrentFolder = args.getParcelable(ARG_FOLDER_TAG);
         mAccount = args.getParcelable(ARG_ACCOUNT_TAG);
         mBatch = args.getBoolean(ARG_BATCH_TAG);
-        mTarget = Arrays.asList((Conversation[])args.getParcelableArray(ARG_TARGET_TAG));
+        mTarget = new ArrayList<>();
+        Parcelable[] temps = args.getParcelableArray(ARG_TARGET_TAG);
+        if(temps != null){
+            for(Parcelable temp:temps){
+                mTarget.add((Conversation) temp);
+            }
+        }
     }
 
     @Override
diff --git a/src/com/android/mail/ui/MaterialSearchActionView.java b/src/com/android/mail/ui/MaterialSearchActionView.java
index 7b248ce..292ad58 100644
--- a/src/com/android/mail/ui/MaterialSearchActionView.java
+++ b/src/com/android/mail/ui/MaterialSearchActionView.java
@@ -37,6 +37,7 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import com.android.mail.R;
 import com.android.mail.utils.ViewUtils;
@@ -68,6 +69,8 @@
     private ImageView mBackButton;
     private EditText mQueryText;
     private ImageView mEndingButton;
+    private Context mContext;
+    public final static int SEARCH_INPUT_LIMIT_NUMBER = 40;
 
     public MaterialSearchActionView(Context context) {
         this(context, null);
@@ -87,6 +90,8 @@
         mDarkBgVoiceDrawable = R.drawable.ic_mic_wht_24dp;
         mLightBgTextColor = res.getColor(R.color.search_query_text);
         mDarkBgTextColor = res.getColor(android.R.color.white);
+
+        mContext = context;
     }
 
     // PUBLIC API
@@ -203,6 +208,14 @@
     public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
         mController.onQueryTextChanged(charSequence.toString());
         setupEndingButton(charSequence);
+        limitInputLength(charSequence);
+    }
+
+    private void limitInputLength(CharSequence charSequence) {
+        if(charSequence.length()>=SEARCH_INPUT_LIMIT_NUMBER){
+            Toast.makeText(mContext,R.string.search_input_limit_number,Toast.LENGTH_LONG).show();
+        }
+
     }
 
     @Override
@@ -231,6 +244,7 @@
     public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
         if (actionId == EditorInfo.IME_ACTION_SEARCH) {
             mController.onSearchPerformed(mQueryText.getText().toString());
+            return true;
         }
         return false;
     }
@@ -241,7 +255,16 @@
         // capture them manually here.
         if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_ENTER) {
             mController.onSearchPerformed(mQueryText.getText().toString());
+            return true;
         }
         return false;
     }
+
+    public String getQueryText(){
+        return mQueryText.getText().toString();
+    }
+
+    public void setQueryText(String query){
+        mQueryText.setText(query);
+    }
 }
diff --git a/src/com/android/mail/ui/MaterialSearchFactorSelecteView.java b/src/com/android/mail/ui/MaterialSearchFactorSelecteView.java
new file mode 100644
index 0000000..042b1d6
--- /dev/null
+++ b/src/com/android/mail/ui/MaterialSearchFactorSelecteView.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of The Linux Foundation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.mail.ui;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+
+import com.android.emailcommon.service.SearchParams;
+import com.android.mail.R;
+
+public class MaterialSearchFactorSelecteView extends LinearLayout implements
+        RadioGroup.OnCheckedChangeListener {
+
+    private RadioGroup mFactorGroup;
+    private Context mContext;
+    private int[] mFactorIds = {
+            R.id.check_factor_all, R.id.check_factor_subject, R.id.check_factor_sender,
+            R.id.check_factor_receiver
+    };
+
+    private MaterialSearchViewController mController;
+    public final static int FACTOR_ALL_ID = R.id.check_factor_all;
+
+    public MaterialSearchFactorSelecteView(Context context) {
+        this(context, null);
+    }
+
+    public MaterialSearchFactorSelecteView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+    }
+
+    // PUBLIC API
+    public void setController(MaterialSearchViewController controller) {
+        mController = controller;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mFactorGroup = (RadioGroup) this.findViewById(R.id.group_check_factor);
+        mFactorGroup.check(R.id.check_factor_all);
+        mFactorGroup.setOnCheckedChangeListener(this);
+    }
+
+    @Override
+    public void onCheckedChanged(RadioGroup group, int checkedId) {
+
+        changeCheckRadioButton(getFactor(checkedId), true);
+    }
+
+    public void changeCheckRadioButton(String factor, boolean isUser) {
+        for (int factorId : mFactorIds) {
+            RadioButton filterBtn = (RadioButton) this.findViewById(factorId);
+            if (factorId == mFactorGroup.getCheckedRadioButtonId()) {
+                filterBtn.setTextColor(Color.WHITE);
+                filterBtn.setBackgroundColor(mContext.getResources().getColor(
+                        R.color.search_factor_selector_color));
+            } else {
+                filterBtn.setTextColor(mContext.getResources().getColor(
+                        R.color.search_factor_text_normal_color));
+                filterBtn.setBackgroundColor(mContext.getResources().getColor(
+                        R.color.search_factor_normal_color));
+            }
+        }
+        changeFactorAction(factor, isUser);
+    }
+
+    private void changeFactorAction(String factor, boolean isUser) {
+        if (factor != null) {
+            mController.changeFactorAction(factor, isUser);
+        }
+    }
+
+    public void checkAllFactor() {
+        if (mFactorGroup.getCheckedRadioButtonId() != FACTOR_ALL_ID) {
+            mFactorGroup.check(FACTOR_ALL_ID);
+        } else {
+            changeFactorAction(SearchParams.SEARCH_FACTOR_ALL, false);
+        }
+    }
+
+    public int getCheckRadioButtonId() {
+        return mFactorGroup.getCheckedRadioButtonId();
+    }
+
+    public String getFactor(int checkedId) {
+        String factor = null;
+        switch (checkedId) {
+            case R.id.check_factor_all:
+                factor = SearchParams.SEARCH_FACTOR_ALL;
+                break;
+            case R.id.check_factor_subject:
+                factor = SearchParams.SEARCH_FACTOR_SUBJECT;
+                break;
+            case R.id.check_factor_sender:
+                factor = SearchParams.SEARCH_FACTOR_SENDER;
+                break;
+            case R.id.check_factor_receiver:
+                factor = SearchParams.SEARCH_FACTOR_RECEIVER;
+                break;
+            default:
+                break;
+        }
+        return factor;
+    }
+}
diff --git a/src/com/android/mail/ui/MaterialSearchSuggestionsList.java b/src/com/android/mail/ui/MaterialSearchSuggestionsList.java
index e40d42f..07c7bd1 100644
--- a/src/com/android/mail/ui/MaterialSearchSuggestionsList.java
+++ b/src/com/android/mail/ui/MaterialSearchSuggestionsList.java
@@ -107,8 +107,17 @@
 
     @Override
     public void onClick(View view) {
-        mController.showSearchActionBar(
-                MaterialSearchViewController.SEARCH_VIEW_STATE_ONLY_ACTIONBAR);
+        mController.setQueryText(mController.getKeyWord());
+        if (mController.isQueryTextNull()) {
+            mController.focusSearchBar(false);
+        } else if (mController.ismIsShowEmptyView()) {
+            mController.showSearchActionBar(
+                    MaterialSearchViewController.SEARCH_VIEW_STATE_VISIBLE);
+            mController.focusSearchBar(false);
+        } else {
+            mController.showSearchActionBar(
+                    MaterialSearchViewController.SEARCH_VIEW_STATE_ONLY_ACTIONBAR);
+        }
     }
 
     // Background task for querying the suggestions list
@@ -134,6 +143,8 @@
                         result.add(new SuggestionItem(suggestion, iconUri));
                     } while (c.moveToNext());
                 }
+            } catch (IllegalStateException e) {
+                // db could have been closed due to cleanup, simply don't do anything.
             } finally {
                 if (c != null) {
                     c.close();
diff --git a/src/com/android/mail/ui/MaterialSearchViewController.java b/src/com/android/mail/ui/MaterialSearchViewController.java
index 9e03a66..3f8c16b 100644
--- a/src/com/android/mail/ui/MaterialSearchViewController.java
+++ b/src/com/android/mail/ui/MaterialSearchViewController.java
@@ -29,6 +29,7 @@
 import android.view.View;
 import android.widget.Toast;
 
+import com.android.emailcommon.service.SearchParams;
 import com.android.mail.ConversationListContext;
 import com.android.mail.R;
 import com.android.mail.providers.SearchRecentSuggestionsProvider;
@@ -39,7 +40,7 @@
 /**
  * Controller for interactions between ActivityController and our custom search views.
  */
-public class MaterialSearchViewController implements ViewMode.ModeChangeListener,
+public class MaterialSearchViewController implements
         TwoPaneLayout.ConversationListLayoutListener {
     private static final long FADE_IN_OUT_DURATION_MS = 150;
 
@@ -54,6 +55,8 @@
     public static final int SEARCH_VIEW_STATE_ONLY_ACTIONBAR = 2;
 
     private static final String EXTRA_CONTROLLER_STATE = "extraSearchViewControllerViewState";
+    private static final String EXTRA_SEARCH_KEY_WORD = "extraSearchKeyWord";
+    private static final String EXTRA_SEARCH_FACTOR = "extraSearchFactor";
 
     private MailActivity mActivity;
     private ActivityController mController;
@@ -62,13 +65,17 @@
 
     private MaterialSearchActionView mSearchActionView;
     private MaterialSearchSuggestionsList mSearchSuggestionList;
-
+    private MaterialSearchFactorSelecteView mSearchFactorView;
     private int mViewMode;
     private int mControllerState;
     private int mEndXCoordForTabletLandscape;
 
     private boolean mSavePending;
     private boolean mDestroyProvider;
+    private String mKeyWord;
+    private final static int INIT_ID = -1000;
+    private int mFactorId = INIT_ID;
+    private boolean mIsShowEmptyView = true;
 
     public MaterialSearchViewController(MailActivity activity, ActivityController controller,
             Intent intent, Bundle savedInstanceState) {
@@ -87,12 +94,16 @@
                 R.id.search_actionbar_view);
         mSearchActionView.setController(this, intent.getStringExtra(
                 ConversationListContext.EXTRA_SEARCH_QUERY), supportVoice);
-
+        mSearchFactorView = (MaterialSearchFactorSelecteView) mActivity
+                .findViewById(R.id.mail_filter_and_result);
+        mSearchFactorView.setController(this);
         if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_CONTROLLER_STATE)) {
             mControllerState = savedInstanceState.getInt(EXTRA_CONTROLLER_STATE);
+            mKeyWord = savedInstanceState.getString(EXTRA_SEARCH_KEY_WORD);
+            mFactorId = savedInstanceState.getInt(EXTRA_SEARCH_FACTOR);
         }
 
-        mActivity.getViewMode().addListener(this);
+
     }
 
     /**
@@ -103,7 +114,6 @@
         if (!mSavePending) {
             mSuggestionsProvider.cleanup();
         }
-        mActivity.getViewMode().removeListener(this);
         mActivity = null;
         mController = null;
         mSearchActionView = null;
@@ -112,20 +122,8 @@
 
     public void saveState(Bundle outState) {
         outState.putInt(EXTRA_CONTROLLER_STATE, mControllerState);
-    }
-
-    @Override
-    public void onViewModeChanged(int newMode) {
-        final int oldMode = mViewMode;
-        mViewMode = newMode;
-        // Never animate visibility changes that are caused by view state changes.
-        if (mController.shouldShowSearchBarByDefault(mViewMode)) {
-            showSearchActionBar(SEARCH_VIEW_STATE_ONLY_ACTIONBAR, false /* animate */);
-        } else if (oldMode == ViewMode.UNKNOWN) {
-            showSearchActionBar(mControllerState, false /* animate */);
-        } else {
-            showSearchActionBar(SEARCH_VIEW_STATE_GONE, false /* animate */);
-        }
+        outState.putString(EXTRA_SEARCH_KEY_WORD, mKeyWord);
+        outState.putInt(EXTRA_SEARCH_FACTOR, mFactorId);
     }
 
     @Override
@@ -145,12 +143,8 @@
     }
 
     public boolean handleBackPress() {
-        final boolean shouldShowSearchBar = mController.shouldShowSearchBarByDefault(mViewMode);
-        if (shouldShowSearchBar && mSearchSuggestionList.isShown()) {
-            showSearchActionBar(SEARCH_VIEW_STATE_ONLY_ACTIONBAR);
-            return true;
-        } else if (!shouldShowSearchBar && mSearchActionView.isShown()) {
-            showSearchActionBar(SEARCH_VIEW_STATE_GONE);
+        if (mSearchActionView.isShown()) {
+            onSearchCanceled();
             return true;
         }
         return false;
@@ -175,20 +169,22 @@
         mControllerState = state;
 
         // ACTIONBAR is only applicable in search mode
-        final boolean onlyActionBar = state == SEARCH_VIEW_STATE_ONLY_ACTIONBAR &&
-                mController.shouldShowSearchBarByDefault(mViewMode);
+        final boolean onlyActionBar = state == SEARCH_VIEW_STATE_ONLY_ACTIONBAR;
         final boolean isStateVisible = state == SEARCH_VIEW_STATE_VISIBLE;
 
         final boolean isSearchBarVisible = isStateVisible || onlyActionBar;
 
         final int searchBarVisibility = isSearchBarVisible ? View.VISIBLE : View.GONE;
         final int suggestionListVisibility = isStateVisible ? View.VISIBLE : View.GONE;
+        final int filterVisibility = onlyActionBar ?View.VISIBLE:View.GONE;
         if (animate) {
             fadeInOutView(mSearchActionView, searchBarVisibility);
             fadeInOutView(mSearchSuggestionList, suggestionListVisibility);
+            fadeInOutView(mSearchFactorView, filterVisibility);
         } else {
             setViewVisibilityAndAlpha(mSearchActionView, searchBarVisibility);
             setViewVisibilityAndAlpha(mSearchSuggestionList, suggestionListVisibility);
+            setViewVisibilityAndAlpha(mSearchFactorView, filterVisibility);
         }
         mSearchActionView.focusSearchBar(isStateVisible);
 
@@ -266,21 +262,18 @@
     }
 
     public void onSearchCanceled() {
-        // Special case search mode
-        if (ViewMode.isSearchMode(mViewMode)) {
-            mActivity.setResult(Activity.RESULT_OK);
-            mActivity.finish();
-        } else {
-            mSearchActionView.clearSearchQuery();
-            showSearchActionBar(SEARCH_VIEW_STATE_GONE);
-        }
+        showSearchActionBar(SEARCH_VIEW_STATE_GONE);
+        mKeyWord = null;
+        mFactorId = INIT_ID;
+        mSearchActionView.clearSearchQuery();
+        mController.exitLocalSearch();
     }
 
     public void onSearchPerformed(String query) {
         query = query.trim();
         if (!TextUtils.isEmpty(query)) {
-            mSearchActionView.clearSearchQuery();
-            mController.executeSearch(query);
+            mSearchActionView.setQueryText(query);
+            mSearchFactorView.checkAllFactor();
         }
     }
 
@@ -302,7 +295,9 @@
     }
 
     public void saveRecentQuery(String query) {
-        new SaveRecentQueryTask().execute(query);
+        if(!TextUtils.isEmpty(query)){
+            new SaveRecentQueryTask().execute(query);
+        }
     }
 
     // static asynctask to save the query in the background.
@@ -328,4 +323,62 @@
             mSavePending = false;
         }
     }
+
+    public void changeFactorAction(String factor, boolean isUser) {
+        setKeywordAndFactorId();
+        mController.executeSearch(mKeyWord, factor, isUser);
+    }
+
+    public void updateSearchResultCount(int count, boolean isShowEmptyView) {
+        mIsShowEmptyView = isShowEmptyView;
+        if (isShowEmptyView) {
+            setViewVisibilityAndAlpha(mSearchFactorView, View.GONE);
+        }
+    }
+
+    private void setKeywordAndFactorId() {
+        mKeyWord = mSearchActionView.getQueryText();
+        mFactorId = mSearchFactorView.getCheckRadioButtonId();
+    }
+
+    public void restoreLocalSearch() {
+        if (mControllerState == SEARCH_VIEW_STATE_VISIBLE) {
+            showSearchActionBar(SEARCH_VIEW_STATE_VISIBLE);
+        } else if (mControllerState == SEARCH_VIEW_STATE_ONLY_ACTIONBAR) {
+            if (mKeyWord != null && mFactorId != INIT_ID) {
+                mSearchActionView.setQueryText(mKeyWord);
+                mSearchFactorView
+                        .changeCheckRadioButton(mSearchFactorView.getFactor(mFactorId), false);
+            }
+        }
+    }
+
+    public void setQueryText(String query) {
+        mSearchActionView.setQueryText(query);
+    }
+
+    public boolean isQueryTextNull() {
+        return TextUtils.isEmpty(mSearchActionView.getQueryText().trim());
+    }
+
+    public void focusSearchBar(boolean hasFocus) {
+        mSearchActionView.focusSearchBar(hasFocus);
+    }
+
+    public boolean isOnlyActionbar() {
+        return mControllerState == SEARCH_VIEW_STATE_ONLY_ACTIONBAR;
+    }
+
+    public String getKeyWord() {
+        return mKeyWord;
+    }
+
+    public boolean ismIsShowEmptyView() {
+        return mIsShowEmptyView;
+    }
+
+    public void setFloatingComposeButtonVisible(int visible) {
+        // mController.setFloatingComposeButtonVisible(visible);
+    }
+
 }
diff --git a/src/com/android/mail/ui/NestedFolderTeaserView.java b/src/com/android/mail/ui/NestedFolderTeaserView.java
index f3fe63e..39302ea 100644
--- a/src/com/android/mail/ui/NestedFolderTeaserView.java
+++ b/src/com/android/mail/ui/NestedFolderTeaserView.java
@@ -277,13 +277,14 @@
             // Clear out the folder views
             mNestedFolderContainer.removeAllViews();
 
+            if (mSortedFolderHolders != null) {
             // We either show all folders if it's not over the threshold, or we show none.
-            if (mSortedFolderHolders.size() <= sCollapsedFolderThreshold || !mCollapsed) {
-                for (final FolderHolder folderHolder : mSortedFolderHolders) {
-                    mNestedFolderContainer.addView(folderHolder.getItemView());
+                if (mSortedFolderHolders.size() <= sCollapsedFolderThreshold || !mCollapsed) {
+                    for (final FolderHolder folderHolder : mSortedFolderHolders) {
+                        mNestedFolderContainer.addView(folderHolder.getItemView());
+                    }
                 }
             }
-
             updateShowMoreView();
             mListUpdated = false;
         }
diff --git a/src/com/android/mail/ui/OnePaneController.java b/src/com/android/mail/ui/OnePaneController.java
index ef42ab9..1c9fc36 100644
--- a/src/com/android/mail/ui/OnePaneController.java
+++ b/src/com/android/mail/ui/OnePaneController.java
@@ -232,9 +232,11 @@
         enableCabMode();
         mConversationListVisible = true;
         if (ConversationListContext.isSearchResult(listContext)) {
-            mViewMode.enterSearchResultsListMode();
-        } else {
             mViewMode.enterConversationListMode();
+            mSearchViewController.setQueryText(listContext.getSearchQuery());
+            mSearchViewController
+                    .showSearchActionBar(
+                    MaterialSearchViewController.SEARCH_VIEW_STATE_ONLY_ACTIONBAR);
         }
         final int transition = mConversationListNeverShown
                 ? FragmentTransaction.TRANSIT_FRAGMENT_FADE
@@ -281,12 +283,7 @@
             return;
         }
         disableCabMode();
-        if (ConversationListContext.isSearchResult(mConvListContext)) {
-            mViewMode.enterSearchResultsConversationMode();
-        } else {
-            mViewMode.enterConversationMode();
-        }
-
+        mViewMode.enterConversationMode();
         mPagerController.show(mAccount, mFolder, conversation, true /* changeVisibility */,
                 shouldAnimate? mPagerAnimationListener : null);
         onConversationVisibilityChanged(true);
@@ -382,9 +379,7 @@
     public boolean handleBackPress() {
         final int mode = mViewMode.getMode();
 
-        if (mode == ViewMode.SEARCH_RESULTS_LIST) {
-            mActivity.finish();
-        } else if (mViewMode.isListMode() && !inInbox(mAccount, mConvListContext)) {
+        if (mViewMode.isListMode() && !inInbox(mAccount, mConvListContext)) {
             navigateUpFolderHierarchy();
         } else if (mViewMode.isConversationMode() || mViewMode.isAdMode()) {
             transitionBackToConversationListMode();
@@ -422,10 +417,7 @@
     @Override
     public boolean handleUpPress() {
         final int mode = mViewMode.getMode();
-        if (mode == ViewMode.SEARCH_RESULTS_LIST) {
-            mActivity.finish();
-            // Not needed, the activity is going away anyway.
-        } else if (mode == ViewMode.CONVERSATION_LIST
+            if (mode == ViewMode.CONVERSATION_LIST
                 || mode == ViewMode.WAITING_FOR_ACCOUNT_INITIALIZATION) {
             final boolean isTopLevel = Folder.isRoot(mFolder);
 
@@ -435,8 +427,7 @@
             } else {
                 navigateUpFolderHierarchy();
             }
-        } else if (mode == ViewMode.CONVERSATION || mode == ViewMode.SEARCH_RESULTS_CONVERSATION
-                || mode == ViewMode.AD) {
+        } else if (mode == ViewMode.CONVERSATION || mode == ViewMode.AD) {
             // Same as go back.
             handleBackPress();
         }
@@ -447,11 +438,7 @@
         final int mode = mViewMode.getMode();
         enableCabMode();
         mConversationListVisible = true;
-        if (mode == ViewMode.SEARCH_RESULTS_CONVERSATION) {
-            mViewMode.enterSearchResultsListMode();
-        } else {
-            mViewMode.enterConversationListMode();
-        }
+        mViewMode.enterConversationListMode();
 
         final Folder folder = mFolder != null ? mFolder : mInbox;
         onFolderChanged(folder, true /* force */);
diff --git a/src/com/android/mail/ui/SecureConversationViewController.java b/src/com/android/mail/ui/SecureConversationViewController.java
old mode 100644
new mode 100755
index 325ad56..10a7a10
--- a/src/com/android/mail/ui/SecureConversationViewController.java
+++ b/src/com/android/mail/ui/SecureConversationViewController.java
@@ -44,6 +44,7 @@
 import com.android.mail.print.PrintUtils;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Message;
+import com.android.mail.providers.UIProvider.MessageFlagLoaded;
 import com.android.mail.utils.ConversationViewUtils;
 
 /**
@@ -212,9 +213,14 @@
         mSnapHeaderView.unbind();
         mSnapHeaderView.bind(item, false);
 
-        if (mMessage.hasAttachments) {
+        if (mMessage.hasAttachments
+                || mMessage.messageFlagLoaded == MessageFlagLoaded.FLAG_LOADED_PARTIAL_COMPLETE) {
+            // Do not have the attachment, but the flag is partial complete, it must contains
+            // the load more placeholder, and we will show it.
             mMessageFooterView.setVisibility(View.VISIBLE);
             mMessageFooterView.bind(item, false);
+        } else {
+            mMessageFooterView.setVisibility(View.GONE);
         }
     }
 
diff --git a/src/com/android/mail/ui/SuppressNotificationReceiver.java b/src/com/android/mail/ui/SuppressNotificationReceiver.java
index 685edd0..a8dedfc 100644
--- a/src/com/android/mail/ui/SuppressNotificationReceiver.java
+++ b/src/com/android/mail/ui/SuppressNotificationReceiver.java
@@ -117,7 +117,7 @@
             return;
         }
 
-        final ConversationListContext listContext = mController.getCurrentListContext();
+        final ConversationListContext listContext = mController.getCurrentConversationListContext();
         if (listContext == null) {
             // A non-null list context was expected
             LogUtils.e(LOG_TAG, "unexpected null context");
diff --git a/src/com/android/mail/ui/TwoPaneController.java b/src/com/android/mail/ui/TwoPaneController.java
index 44600d3..3622787 100644
--- a/src/com/android/mail/ui/TwoPaneController.java
+++ b/src/com/android/mail/ui/TwoPaneController.java
@@ -160,7 +160,7 @@
     /**
      * Render the conversation list in the correct pane.
      */
-    private void renderConversationList() {
+     void renderConversationList() {
         if (mActivity == null) {
             return;
         }
diff --git a/src/com/android/mail/ui/settings/GeneralPrefsFragment.java b/src/com/android/mail/ui/settings/GeneralPrefsFragment.java
index a5e41c9..7ca69cc 100644
--- a/src/com/android/mail/ui/settings/GeneralPrefsFragment.java
+++ b/src/com/android/mail/ui/settings/GeneralPrefsFragment.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
+import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.preference.CheckBoxPreference;
@@ -37,6 +38,7 @@
 import com.android.mail.providers.SuggestionsProvider;
 import com.android.mail.providers.UIProvider.AutoAdvance;
 import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.Utils;
 import com.android.mail.R;
 import com.google.common.annotations.VisibleForTesting;
 
@@ -92,6 +94,13 @@
          */
         menu.clear();
         inflater.inflate(R.menu.general_prefs_fragment_menu, menu);
+
+        MenuItem feedbackMenuItem = menu.findItem(R.id.help_info_menu_item);
+        Uri feedbackUri = Utils.getValidUri(getString(R.string.email_feedback_uri));
+
+        if (feedbackMenuItem != null) {
+            feedbackMenuItem.setVisible(!Uri.EMPTY.equals(feedbackUri));
+        }
     }
 
     @Override
diff --git a/src/com/android/mail/utils/LogUtils.java b/src/com/android/mail/utils/LogUtils.java
index b2cfc5b..663990f 100644
--- a/src/com/android/mail/utils/LogUtils.java
+++ b/src/com/android/mail/utils/LogUtils.java
@@ -65,7 +65,7 @@
      * internal builds.
      */
     // STOPSHIP: ship with DEBUG set
-    private static final int MAX_ENABLED_LOG_LEVEL = VERBOSE;
+    private static final int MAX_ENABLED_LOG_LEVEL = DEBUG;
 
     private static Boolean sDebugLoggingEnabledForTests = null;
 
diff --git a/src/com/android/mail/utils/Utils.java b/src/com/android/mail/utils/Utils.java
old mode 100644
new mode 100755
index a9c93bf..d701bed
--- a/src/com/android/mail/utils/Utils.java
+++ b/src/com/android/mail/utils/Utils.java
@@ -96,6 +96,7 @@
     public static final String EXTRA_FROM_NOTIFICATION = "notification";
     public static final String EXTRA_IGNORE_INITIAL_CONVERSATION_LIMIT =
             "ignore-initial-conversation-limit";
+    public static final String EXTRA_CONVERSATIONLIST_URI = "uimessages";
 
     public static final String MAILTO_SCHEME = "mailto";
 
@@ -110,6 +111,8 @@
     // TODO: UI Provider has this notification URI?
     public static final String ACTION_NOTIFY_DATASET_CHANGED =
             "com.android.mail.ACTION_NOTIFY_DATASET_CHANGED";
+    public static final String ACTION_WIDGET_FOLDER_UPDATE =
+            "com.android.email.ACTION_WIDGET_FOLDER_UPDATE";
 
     /** Parameter keys for context-aware help. */
     private static final String SMART_HELP_LINK_PARAMETER_NAME = "p";
diff --git a/src/com/android/mail/widget/BaseWidgetProvider.java b/src/com/android/mail/widget/BaseWidgetProvider.java
index bbfe1cc..41880da 100644
--- a/src/com/android/mail/widget/BaseWidgetProvider.java
+++ b/src/com/android/mail/widget/BaseWidgetProvider.java
@@ -340,7 +340,6 @@
             final Intent configureIntent = new Intent(context, MailboxSelectionActivity.class);
             configureIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
             configureIntent.setData(Uri.parse(configureIntent.toUri(Intent.URI_INTENT_SCHEME)));
-            configureIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
             PendingIntent clickIntent = PendingIntent.getActivity(context, 0, configureIntent,
                     PendingIntent.FLAG_UPDATE_CURRENT);
             remoteViews.setOnClickPendingIntent(R.id.widget_configuration, clickIntent);
diff --git a/src/com/android/mail/widget/WidgetService.java b/src/com/android/mail/widget/WidgetService.java
old mode 100644
new mode 100755
index 22c74a7..d62af71
--- a/src/com/android/mail/widget/WidgetService.java
+++ b/src/com/android/mail/widget/WidgetService.java
@@ -17,14 +17,19 @@
 
 import android.app.PendingIntent;
 import android.appwidget.AppWidgetManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.CursorLoader;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.Loader;
+
 import android.content.Loader.OnLoadCompleteListener;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Looper;
 import android.support.v4.app.TaskStackBuilder;
 import android.text.SpannableString;
@@ -54,8 +59,11 @@
 import com.android.mail.utils.LogUtils;
 import com.android.mail.utils.Utils;
 
+import com.google.common.collect.Sets;
+import com.google.common.primitives.Ints;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 public class WidgetService extends RemoteViewsService {
     /**
@@ -205,6 +213,7 @@
         private static final int FOLDER_LOADER_ID = 0;
         private static final int CONVERSATION_CURSOR_LOADER_ID = 1;
         private static final int ACCOUNT_LOADER_ID = 2;
+        private static final int VIRTUALMAILBOX_LOADER_ID = 3;
 
         private final Context mContext;
         private final int mAppWidgetId;
@@ -226,6 +235,10 @@
         private final WidgetService mService;
         private String mSendersSplitToken;
         private String mElidedPaddingToken;
+        private UpdateFolderWidgetReceiver mUpdateFolderWidgetReceiver;
+        private CursorLoader mVirtualMailBoxLoader;
+        private int[] mWidgetIds;
+        private VirtualMailBoxLoaderCompleterListener mMailBoxLoaderCompleterListener;
 
         public MailFactory(Context context, Intent intent, WidgetService service) {
             mContext = context;
@@ -315,6 +328,11 @@
                     UIProvider.ACCOUNTS_PROJECTION_NO_CAPABILITIES, null, null, null);
             mAccountLoader.registerListener(ACCOUNT_LOADER_ID, this);
             mAccountLoader.startLoading();
+
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(Utils.ACTION_WIDGET_FOLDER_UPDATE);
+            mUpdateFolderWidgetReceiver = new UpdateFolderWidgetReceiver();
+            mContext.registerReceiver(mUpdateFolderWidgetReceiver, filter);
         }
 
         @Override
@@ -342,6 +360,21 @@
                 mAccountLoader.unregisterListener(this);
                 mAccountLoader = null;
             }
+
+
+            if (mVirtualMailBoxLoader != null) {
+                mVirtualMailBoxLoader.reset();
+                if (mMailBoxLoaderCompleterListener != null) {
+                    mVirtualMailBoxLoader.unregisterListener(mMailBoxLoaderCompleterListener);
+                }
+                mVirtualMailBoxLoader = null;
+                mMailBoxLoaderCompleterListener = null;
+                mWidgetIds = null;
+            }
+
+            if (mUpdateFolderWidgetReceiver != null) {
+                mContext.unregisterReceiver(mUpdateFolderWidgetReceiver);
+            }
         }
 
         @Override
@@ -618,5 +651,129 @@
                 }
             }
         }
+
+        public class UpdateFolderWidgetReceiver extends BroadcastReceiver {
+            @Override
+            public void onReceive(final Context context, Intent intent) {
+                if (intent.getAction() == Utils.ACTION_WIDGET_FOLDER_UPDATE) {
+                    final Bundle extras = intent.getExtras();
+                    if (extras == null) {
+                        return;
+                    }
+
+                    final Uri accountUri = extras.getParcelable(Utils.EXTRA_ACCOUNT_URI);
+                    final Uri folderUri = extras.getParcelable(Utils.EXTRA_FOLDER_URI);
+                    final Uri conversationListUri =
+                            extras.getParcelable(Utils.EXTRA_CONVERSATIONLIST_URI);
+                    final boolean updateAllWidgets =
+                            extras.getBoolean(BaseWidgetProvider.EXTRA_UPDATE_ALL_WIDGETS, false);
+
+                    if (mVirtualMailBoxLoader != null && !mVirtualMailBoxLoader.isReset()) {
+                        mVirtualMailBoxLoader.reset();
+                    }
+
+                    if (mVirtualMailBoxLoader == null) {
+                        mVirtualMailBoxLoader = new CursorLoader(mContext,
+                                builderConversationQueryUri(conversationListUri),
+                                UIProvider.CONVERSATION_PROJECTION, null, null, null);
+                        mMailBoxLoaderCompleterListener =
+                                new VirtualMailBoxLoaderCompleterListener();
+                        mVirtualMailBoxLoader.registerListener(VIRTUALMAILBOX_LOADER_ID,
+                                mMailBoxLoaderCompleterListener);
+                    } else {
+                        mVirtualMailBoxLoader
+                                .setUri(builderConversationQueryUri(conversationListUri));
+                    }
+
+                    mWidgetIds = getWidgetIds(accountUri, folderUri, updateAllWidgets);
+                    mVirtualMailBoxLoader.setUpdateThrottle(
+                            context.getResources().getInteger(R.integer.widget_refresh_delay_ms));
+                    mVirtualMailBoxLoader.startLoading();
+                }
+
+            }
+
+            private int[] getWidgetIds(Uri accountUri, Uri folderUri, boolean updateAllWidgets) {
+                if (accountUri == null && Utils.isEmpty(folderUri) && !updateAllWidgets) {
+                    return new int[0];
+                }
+                final Set<Integer> widgetsToUpdate = Sets.newHashSet();
+                for (int id : getCurrentWidgetIds(mContext)) {
+                    // Retrieve the persisted information for this widget from
+                    // preferences.
+                    final String accountFolder = MailPrefs.get(mContext).getWidgetConfiguration(id);
+                    // If the account matched, update the widget.
+                    if (accountFolder != null) {
+                        final String[] parsedInfo = TextUtils.split(accountFolder,
+                                BaseWidgetProvider.ACCOUNT_FOLDER_PREFERENCE_SEPARATOR);
+                        boolean updateThis = updateAllWidgets;
+                        if (!updateThis) {
+                            if (accountUri != null &&
+                                    TextUtils.equals(accountUri.toString(), parsedInfo[0])) {
+                                updateThis = true;
+                            } else if (folderUri != null &&
+                                    TextUtils.equals(folderUri.toString(), parsedInfo[1])) {
+                                updateThis = true;
+                            }
+                        }
+                        if (updateThis) {
+                            widgetsToUpdate.add(id);
+                        }
+                    }
+                }
+                if (widgetsToUpdate.size() == 0) {
+                    return new int[0];
+                }
+                return Ints.toArray(widgetsToUpdate);
+            }
+
+            private int[] getCurrentWidgetIds(Context context) {
+                final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
+                final ComponentName mailComponent = new ComponentName(context,
+                        BaseWidgetProvider.getProviderName(context));
+                return appWidgetManager.getAppWidgetIds(mailComponent);
+            }
+
+            private Uri builderConversationQueryUri(Uri conversationListUri) {
+                final Uri.Builder builder = conversationListUri.buildUpon();
+                final String maxConversations = Integer.toString(MAX_CONVERSATIONS_COUNT);
+                final Uri widgetConversationQueryUri = builder
+                        .appendQueryParameter(ConversationListQueryParameters.LIMIT,
+                                maxConversations)
+                        .appendQueryParameter(ConversationListQueryParameters.USE_NETWORK,
+                                Boolean.FALSE.toString())
+                        .appendQueryParameter(ConversationListQueryParameters.ALL_NOTIFICATIONS,
+                                Boolean.TRUE.toString()).build();
+                return widgetConversationQueryUri;
+            }
+
+        }
+
+        private class VirtualMailBoxLoaderCompleterListener implements
+                OnLoadCompleteListener<Cursor> {
+
+            @Override
+            public void onLoadComplete(Loader<Cursor> loader, Cursor data) {
+                final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
+                final RemoteViews remoteViews =
+                        new RemoteViews(mContext.getPackageName(), R.layout.widget);
+                if (data != null) {
+                    synchronized (sWidgetLock) {
+                        if (!isDataValid(data)) {
+                            mConversationCursor = null;
+                        } else {
+                            mConversationCursor = data;
+                        }
+                    }
+                    appWidgetManager.notifyAppWidgetViewDataChanged(mWidgetIds,
+                            R.id.conversation_list);
+                    if (mConversationCursor == null || mConversationCursor.getCount() == 0) {
+                        remoteViews.setTextViewText(R.id.empty_conversation_list,
+                                mContext.getString(R.string.empty_folder));
+                        appWidgetManager.partiallyUpdateAppWidget(mAppWidgetId, remoteViews);
+                    }
+                }
+            }
+        }
     }
 }