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);
+ }
+ }
+ }
+ }
}
}