From bc47398187c6ffd132435e51d8d61e6ec79a79db Mon Sep 17 00:00:00 2001 From: Paul Westbrook Date: Mon, 24 Sep 2012 15:15:24 -0700 Subject: [PATCH] Merge Email1 into MR1 Change-Id: I45289d46b65faffc7a3a3dd46382899162f3aaab --- Android.mk | 40 +- AndroidManifest.xml | 640 ++-- CleanSpec.mk | 37 +- build/res/values/strings.xml | 31 - build/res/xml/services.xml | 108 - emailcommon/Android.mk | 20 +- .../emailcommon/AccountManagerTypes.java | 12 +- .../src/com/android/emailcommon/Api.java | 3 +- .../emailcommon/CalendarProviderStub.java | 10 +- .../com/android/emailcommon/TrafficFlags.java | 24 +- .../emailcommon/internet/MimeMessage.java | 20 +- .../emailcommon/internet/MimeUtility.java | 43 +- .../emailcommon/internet/Rfc822Output.java | 230 +- .../android/emailcommon/provider/Account.aidl | 18 - .../android/emailcommon/provider/Account.java | 67 +- .../emailcommon/provider/EmailContent.java | 259 +- .../emailcommon/provider/HostAuth.java | 39 +- .../android/emailcommon/provider/Mailbox.java | 69 +- .../provider/MailboxUtilities.java | 274 -- .../android/emailcommon/provider/Policy.java | 7 +- .../emailcommon/provider/QuickResponse.java | 19 +- .../service/AccountServiceProxy.java | 7 +- .../service/EmailServiceCallback.java | 117 - .../service/EmailServiceProxy.java | 41 +- .../emailcommon/service/IAccountService.aidl | 2 +- .../emailcommon/service/IEmailService.aidl | 6 - .../service/PolicyServiceProxy.java | 6 +- .../emailcommon/service/ServiceProxy.java | 30 +- .../emailcommon/service/SyncWindow.java | 21 - .../utility/AttachmentUtilities.java | 82 +- .../utility/EmailClientConnectionManager.java | 23 +- .../android/emailcommon/utility/SSLUtils.java | 114 +- .../android/emailcommon/utility/Utility.java | 113 +- emailsync/Android.mk | 30 - .../emailsync/AbstractSyncService.java | 307 -- .../emailsync/EmailSyncAlarmReceiver.java | 111 - .../src/com/android/emailsync/FileLogger.java | 120 - .../emailsync/MailboxAlarmReceiver.java | 42 - .../android/emailsync/MessageMoveRequest.java | 42 - .../com/android/emailsync/PartRequest.java | 51 - .../src/com/android/emailsync/Request.java | 36 - .../com/android/emailsync/SyncManager.java | 2326 ------------- proguard.flags | 5 + remove-exchange-support.sh | 61 + res/drawable-hdpi/attachment_bg_holo.9.png | Bin 0 -> 288 bytes .../btn_check_off_normal_holo_light.png | Bin 0 -> 119 bytes .../btn_check_on_normal_holo_light.png | Bin 0 -> 374 bytes res/drawable-hdpi/btn_maybe_off.png | Bin 0 -> 1189 bytes res/drawable-hdpi/btn_no_off.png.png | Bin 0 -> 1020 bytes .../btn_star_off_convo_holo_light.png | Bin 0 -> 705 bytes .../btn_star_off_normal_email_holo_light.png | Bin 0 -> 325 bytes .../btn_star_on_convo_holo_light.png | Bin 0 -> 1464 bytes .../btn_star_on_normal_email_holo_light.png | Bin 0 -> 541 bytes res/drawable-hdpi/btn_yes_off.png.png | Bin 0 -> 1148 bytes .../divider_horizontal_holo_light.9.png | Bin 0 -> 105 bytes .../expander_close_holo_light.9.png | Bin 0 -> 425 bytes .../expander_open_holo_light.9.png | Bin 0 -> 428 bytes .../gradient_bg_email_widget_holo.9.png | Bin 0 -> 214 bytes .../header_bg_email_widget_holo.9.png | Bin 0 -> 395 bytes .../header_convo_view_sender_bg_holo.9.png | Bin 0 -> 141 bytes res/drawable-hdpi/ic_badge_attachment.png | Bin 0 -> 239 bytes .../ic_badge_forward_holo_light.png | Bin 0 -> 309 bytes .../ic_badge_invite_holo_light.png | Bin 0 -> 150 bytes .../ic_badge_reply_forward_holo_light.png | Bin 0 -> 312 bytes .../ic_badge_reply_holo_light.png | Bin 0 -> 287 bytes res/drawable-hdpi/ic_forward_holo_dark.png | Bin 0 -> 417 bytes res/drawable-hdpi/ic_list_combined_inbox.png | Bin 0 -> 786 bytes .../ic_mailbox_collapsed_holo_light.png | Bin 0 -> 350 bytes .../ic_menu_compose_normal_holo_light.png | Bin 0 -> 300 bytes .../ic_menu_mark_read_holo_light.png | Bin 0 -> 436 bytes .../ic_menu_mark_unread_holo_light.png | Bin 0 -> 393 bytes .../ic_menu_move_to_holo_light.png | Bin 0 -> 832 bytes .../ic_menu_refresh_holo_light.png | Bin 0 -> 566 bytes .../ic_menu_search_holo_light.png | Bin 0 -> 845 bytes .../ic_menu_send_disabled_holo_light.png | Bin 0 -> 409 bytes res/drawable-hdpi/ic_menu_send_holo_light.png | Bin 0 -> 432 bytes .../ic_menu_send_normal_holo_light.png | Bin 0 -> 951 bytes res/drawable-hdpi/ic_menu_star_holo_light.png | Bin 0 -> 501 bytes .../ic_menu_star_off_holo_light.png | Bin 0 -> 692 bytes .../ic_menu_trash_holo_light.png | Bin 0 -> 530 bytes .../ic_newer_arrow_disabled_holo_light.png | Bin 0 -> 844 bytes .../ic_newer_arrow_holo_light.png | Bin 0 -> 937 bytes .../ic_older_arrow_disabled_holo_light.png | Bin 0 -> 830 bytes .../ic_older_arrow_holo_light.png | Bin 0 -> 923 bytes .../ic_remove_attachment_holo_light.png | Bin 0 -> 806 bytes res/drawable-hdpi/ic_reply.png | Bin 0 -> 1540 bytes res/drawable-hdpi/ic_reply_all.png | Bin 0 -> 1944 bytes res/drawable-hdpi/ic_reply_all_holo_dark.png | Bin 0 -> 526 bytes res/drawable-hdpi/ic_reply_holo_dark.png | Bin 0 -> 428 bytes .../ic_show_images_holo_light.png | Bin 0 -> 1243 bytes .../ic_spam_normal_holo_light.png | Bin 0 -> 260 bytes .../list_div_top_btm_email_widget_holo.9.png | Bin 0 -> 140 bytes res/drawable-mdpi/attachment_bg_holo.9.png | Bin 0 -> 266 bytes .../btn_check_off_normal_holo_light.png | Bin 0 -> 127 bytes .../btn_check_on_normal_holo_light.png | Bin 0 -> 322 bytes res/drawable-mdpi/btn_maybe_off.png | Bin 0 -> 817 bytes res/drawable-mdpi/btn_no_off.png.png | Bin 0 -> 715 bytes .../btn_star_off_convo_holo_light.png | Bin 0 -> 538 bytes .../btn_star_off_normal_email_holo_light.png | Bin 0 -> 419 bytes .../btn_star_on_convo_holo_light.png | Bin 0 -> 1026 bytes .../btn_star_on_normal_email_holo_light.png | Bin 0 -> 740 bytes res/drawable-mdpi/btn_yes_off.png.png | Bin 0 -> 807 bytes .../divider_horizontal_holo_light.9.png | Bin 0 -> 105 bytes .../expander_close_holo_light.9.png | Bin 0 -> 304 bytes .../expander_open_holo_light.9.png | Bin 0 -> 304 bytes .../gradient_bg_email_widget_holo.9.png | Bin 0 -> 181 bytes .../header_bg_email_widget_holo.9.png | Bin 0 -> 303 bytes .../header_convo_view_sender_bg_holo.9.png | Bin 0 -> 140 bytes res/drawable-mdpi/ic_badge_attachment.png | Bin 0 -> 202 bytes .../ic_badge_forward_holo_light.png | Bin 0 -> 218 bytes .../ic_badge_invite_holo_light.png | Bin 0 -> 123 bytes .../ic_badge_reply_forward_holo_light.png | Bin 0 -> 216 bytes .../ic_badge_reply_holo_light.png | Bin 0 -> 215 bytes .../ic_exchange_minitab_selected.png | Bin 0 -> 933 bytes res/drawable-mdpi/ic_exchange_selected.png | Bin 0 -> 1403 bytes .../ic_folder_drafts_holo_light.png | Bin 0 -> 734 bytes .../ic_folder_inbox_holo_light.png | Bin 0 -> 604 bytes .../ic_folder_outbox_holo_light.png | Bin 0 -> 540 bytes .../ic_folder_sent_holo_light.png | Bin 0 -> 629 bytes res/drawable-mdpi/ic_forward_holo_dark.png | Bin 0 -> 315 bytes res/drawable-mdpi/ic_list_combined_inbox.png | Bin 0 -> 604 bytes .../ic_mailbox_collapsed_holo_light.png | Bin 0 -> 314 bytes .../ic_menu_compose_normal_holo_light.png | Bin 0 -> 223 bytes .../ic_menu_mark_read_holo_light.png | Bin 0 -> 317 bytes .../ic_menu_mark_unread_holo_light.png | Bin 0 -> 279 bytes .../ic_menu_move_to_holo_light.png | Bin 0 -> 669 bytes .../ic_menu_refresh_holo_light.png | Bin 0 -> 404 bytes .../ic_menu_search_holo_light.png | Bin 0 -> 562 bytes .../ic_menu_send_disabled_holo_light.png | Bin 0 -> 304 bytes res/drawable-mdpi/ic_menu_send_holo_light.png | Bin 0 -> 314 bytes .../ic_menu_send_normal_holo_light.png | Bin 0 -> 681 bytes res/drawable-mdpi/ic_menu_star_holo_light.png | Bin 0 -> 345 bytes .../ic_menu_star_off_holo_light.png | Bin 0 -> 441 bytes .../ic_menu_trash_holo_light.png | Bin 0 -> 343 bytes .../ic_newer_arrow_disabled_holo_light.png | Bin 0 -> 637 bytes .../ic_newer_arrow_holo_light.png | Bin 0 -> 697 bytes ...c_notification_multiple_mail_holo_dark.png | Bin 0 -> 214 bytes .../ic_older_arrow_disabled_holo_light.png | Bin 0 -> 623 bytes .../ic_older_arrow_holo_light.png | Bin 0 -> 679 bytes .../ic_remove_attachment_holo_light.png | Bin 0 -> 717 bytes res/drawable-mdpi/ic_reply.png | Bin 0 -> 1146 bytes res/drawable-mdpi/ic_reply_all.png | Bin 0 -> 1328 bytes res/drawable-mdpi/ic_reply_all_holo_dark.png | Bin 0 -> 418 bytes res/drawable-mdpi/ic_reply_holo_dark.png | Bin 0 -> 306 bytes .../ic_show_images_holo_light.png | Bin 0 -> 1172 bytes .../ic_spam_normal_holo_light.png | Bin 0 -> 218 bytes .../list_div_top_btm_email_widget_holo.9.png | Bin 0 -> 137 bytes .../stat_notify_email_generic.png | Bin 0 -> 617 bytes .../btn_check_off_normal_holo_light.png | Bin 0 -> 336 bytes .../btn_check_on_normal_holo_light.png | Bin 0 -> 693 bytes .../btn_star_off_normal_email_holo_light.png | Bin 0 -> 705 bytes .../btn_star_on_normal_email_holo_light.png | Bin 0 -> 1464 bytes .../btn_check_off_normal_holo_light.png | Bin 0 -> 127 bytes .../btn_check_on_normal_holo_light.png | Bin 0 -> 322 bytes .../btn_star_off_normal_email_holo_light.png | Bin 0 -> 377 bytes .../btn_star_on_normal_email_holo_light.png | Bin 0 -> 607 bytes .../btn_check_off_normal_holo_light.png | Bin 0 -> 362 bytes .../btn_check_on_normal_holo_light.png | Bin 0 -> 946 bytes .../btn_star_off_normal_email_holo_light.png | Bin 0 -> 935 bytes .../btn_star_on_normal_email_holo_light.png | Bin 0 -> 2034 bytes res/drawable-xhdpi/attachment_bg_holo.9.png | Bin 0 -> 391 bytes .../btn_check_off_normal_holo_light.png | Bin 0 -> 142 bytes .../btn_check_on_normal_holo_light.png | Bin 0 -> 496 bytes res/drawable-xhdpi/btn_maybe_off.png | Bin 0 -> 1705 bytes res/drawable-xhdpi/btn_no_off.png.png | Bin 0 -> 1511 bytes .../btn_star_off_convo_holo_light.png | Bin 0 -> 935 bytes .../btn_star_off_normal_email_holo_light.png | Bin 0 -> 713 bytes .../btn_star_on_convo_holo_light.png | Bin 0 -> 2034 bytes .../btn_star_on_normal_email_holo_light.png | Bin 0 -> 1902 bytes res/drawable-xhdpi/btn_yes_off.png.png | Bin 0 -> 1716 bytes .../divider_horizontal_holo_light.9.png | Bin 0 -> 174 bytes .../expander_close_holo_light.9.png | Bin 0 -> 545 bytes .../expander_open_holo_light.9.png | Bin 0 -> 558 bytes .../gradient_bg_email_widget_holo.9.png | Bin 0 -> 239 bytes .../header_bg_email_widget_holo.9.png | Bin 0 -> 473 bytes .../header_convo_view_sender_bg_holo.9.png | Bin 0 -> 141 bytes res/drawable-xhdpi/ic_badge_attachment.png | Bin 0 -> 335 bytes .../ic_badge_forward_holo_light.png | Bin 0 -> 407 bytes .../ic_badge_invite_holo_light.png | Bin 0 -> 161 bytes .../ic_badge_reply_forward_holo_light.png | Bin 0 -> 394 bytes .../ic_badge_reply_holo_light.png | Bin 0 -> 330 bytes res/drawable-xhdpi/ic_forward_holo_dark.png | Bin 0 -> 549 bytes res/drawable-xhdpi/ic_list_combined_inbox.png | Bin 0 -> 1006 bytes .../ic_mailbox_collapsed_holo_light.png | Bin 0 -> 385 bytes .../ic_menu_compose_normal_holo_light.png | Bin 0 -> 424 bytes .../ic_menu_mark_read_holo_light.png | Bin 0 -> 716 bytes .../ic_menu_mark_unread_holo_light.png | Bin 0 -> 690 bytes .../ic_menu_move_to_holo_light.png | Bin 0 -> 1006 bytes .../ic_menu_refresh_holo_light.png | Bin 0 -> 927 bytes .../ic_menu_search_holo_light.png | Bin 0 -> 1629 bytes .../ic_menu_send_disabled_holo_light.png | Bin 0 -> 703 bytes .../ic_menu_send_holo_light.png | Bin 0 -> 741 bytes .../ic_menu_send_normal_holo_light.png | Bin 0 -> 1206 bytes .../ic_menu_star_holo_light.png | Bin 0 -> 840 bytes .../ic_menu_star_off_holo_light.png | Bin 0 -> 1194 bytes .../ic_menu_trash_holo_light.png | Bin 0 -> 1295 bytes .../ic_newer_arrow_disabled_holo_light.png | Bin 0 -> 1069 bytes .../ic_newer_arrow_holo_light.png | Bin 0 -> 1210 bytes .../ic_older_arrow_disabled_holo_light.png | Bin 0 -> 1054 bytes .../ic_older_arrow_holo_light.png | Bin 0 -> 1191 bytes .../ic_remove_attachment_holo_light.png | Bin 0 -> 1187 bytes res/drawable-xhdpi/ic_reply.png | Bin 0 -> 1996 bytes res/drawable-xhdpi/ic_reply_all.png | Bin 0 -> 2585 bytes res/drawable-xhdpi/ic_reply_all_holo_dark.png | Bin 0 -> 672 bytes res/drawable-xhdpi/ic_reply_holo_dark.png | Bin 0 -> 546 bytes .../ic_show_images_holo_light.png | Bin 0 -> 794 bytes .../ic_spam_normal_holo_light.png | Bin 0 -> 399 bytes .../list_div_top_btm_email_widget_holo.9.png | Bin 0 -> 141 bytes res/drawable/attachment_background.xml | 33 + res/drawable/menu_item_newer.xml | 20 + res/drawable/menu_item_older.xml | 20 + .../message_view_header_actions.xml | 43 + .../account_setup_exchange.xml | 84 + .../message_view_invitation.xml | 139 + .../account_setup_exchange.xml | 96 + .../message_view_invitation.xml | 139 + .../account_setup_account_type.xml | 28 +- .../account_setup_exchange_fragment.xml | 149 + .../account_setup_incoming_fragment.xml | 24 +- res/layout-sw600dp/action_bar_spinner.xml | 66 + res/layout-sw600dp/address_text_view.xml | 21 + res/layout-sw600dp/compose_area_buttons.xml | 43 + res/layout-sw600dp/message_compose.xml | 101 + .../message_view_attachment.xml | 129 + res/layout-sw600dp/message_view_fragment.xml | 228 ++ .../message_view_header_actions.xml | 47 + .../waiting_for_sync_message.xml | 47 + res/layout-v14/compose_area_recipients.xml | 110 + .../account_settings_exchange_fragment.xml | 52 + .../account_settings_incoming_fragment.xml | 19 +- res/layout/account_setup_account_type.xml | 29 +- res/layout/account_setup_exchange.xml | 43 + .../account_setup_exchange_fragment.xml | 101 + res/layout/account_setup_incoming.xml | 16 +- .../account_setup_incoming_fragment.xml | 27 +- ...nt_type.xml => action_bar_custom_view.xml} | 25 +- ... => action_bar_indeterminate_progress.xml} | 26 +- res/layout/action_bar_search.xml | 25 + res/layout/action_bar_spinner.xml | 65 + res/layout/action_bar_spinner_dropdown.xml | 70 + .../action_bar_spinner_dropdown_header.xml | 20 + res/layout/compose_area_recipients.xml | 112 + .../compose_body.xml} | 14 +- res/layout/compose_from.xml | 31 + res/layout/conversation_item_view_normal.xml | 102 - res/layout/debug.xml | 16 +- res/layout/email_activity_one_pane.xml | 42 + res/layout/email_activity_two_pane.xml | 45 + .../mailbox_list_fragment.xml} | 17 +- res/layout/mailbox_list_header.xml | 22 + res/layout/mailbox_list_item.xml | 79 + res/layout/message_command_button_view.xml | 65 + res/layout/message_compose.xml | 59 + res/layout/message_file_view.xml | 29 + res/layout/message_list_fragment.xml | 40 + res/layout/message_list_item_footer.xml | 39 + res/layout/message_list_item_normal.xml | 125 + ...ew_wide.xml => message_list_item_wide.xml} | 82 +- res/layout/message_list_search_header.xml | 41 + res/layout/message_list_warning.xml | 54 + res/layout/message_view_attachment.xml | 122 + res/layout/message_view_details.xml | 89 + res/layout/message_view_fragment.xml | 198 ++ res/layout/message_view_header_actions.xml | 43 + res/layout/message_view_header_upper.xml | 76 + res/layout/message_view_invitation.xml | 130 + res/layout/message_view_subheader.xml | 85 + res/layout/quoted_text.xml | 62 + res/layout/recipient_dropdown_item.xml | 47 + .../recipient_dropdown_item_loading.xml | 44 + res/layout/three_pane.xml | 63 + res/layout/waiting_for_sync_message.xml | 53 + res/layout/widget.xml | 104 + res/layout/widget_list_item.xml | 81 + res/layout/widget_loading.xml | 33 + .../message_search_list_fragment_option.xml | 51 + .../message_view_fragment_option.xml | 44 + res/menu-sw600dp/email_activity_options.xml | 69 + res/menu-sw600dp/message_compose_option.xml | 54 + .../message_list_fragment_cab_options.xml | 36 + .../message_list_fragment_option.xml | 51 + .../message_search_list_fragment_option.xml | 51 + .../message_view_fragment_option.xml | 63 + res/menu/email_activity_options.xml | 79 + res/menu/message_compose_option.xml | 54 + res/menu/message_header_overflow_menu.xml | 32 + .../message_list_fragment_cab_options.xml | 36 + res/menu/message_list_fragment_option.xml | 61 + res/menu/message_view_fragment_option.xml | 62 + res/menu/welcome.xml | 27 + res/mipmap-hdpi/ic_launcher_email.png | Bin 0 -> 20889 bytes res/mipmap-hdpi/ic_launcher_mail.png | Bin 7010 -> 0 bytes .../ic_launcher_shortcut_folder.png | Bin 7010 -> 0 bytes res/mipmap-mdpi/ic_launcher_email.png | Bin 0 -> 17545 bytes res/mipmap-mdpi/ic_launcher_mail.png | Bin 3846 -> 0 bytes .../ic_launcher_shortcut_folder.png | Bin 3846 -> 0 bytes res/mipmap-xhdpi/ic_launcher_email.png | Bin 0 -> 24725 bytes res/mipmap-xhdpi/ic_launcher_mail.png | Bin 10781 -> 0 bytes .../ic_launcher_shortcut_folder.png | Bin 10781 -> 0 bytes res/values-af/strings.xml | 127 +- res/values-af/uploader.xml | 2 +- res/values-am/strings.xml | 145 +- res/values-am/uploader.xml | 2 +- res/values-ar/strings.xml | 127 +- res/values-ar/uploader.xml | 2 +- res/values-be/strings.xml | 127 +- res/values-be/uploader.xml | 2 +- res/values-bg/strings.xml | 127 +- res/values-bg/uploader.xml | 2 +- res/values-ca/strings.xml | 127 +- res/values-ca/uploader.xml | 2 +- res/values-cs/strings.xml | 129 +- res/values-cs/uploader.xml | 4 +- res/values-da/strings.xml | 129 +- res/values-da/uploader.xml | 4 +- res/values-de/strings.xml | 135 +- res/values-de/uploader.xml | 8 +- res/values-el/strings.xml | 127 +- res/values-el/uploader.xml | 2 +- res/values-en-rGB/strings.xml | 129 +- res/values-en-rGB/uploader.xml | 2 +- res/values-es-rUS/strings.xml | 127 +- res/values-es-rUS/uploader.xml | 2 +- res/values-es/strings.xml | 145 +- res/values-es/uploader.xml | 2 +- res/values-et/strings.xml | 127 +- res/values-et/uploader.xml | 2 +- res/values-fa/strings.xml | 257 +- res/values-fa/uploader.xml | 36 +- res/values-fi/strings.xml | 127 +- res/values-fi/uploader.xml | 2 +- res/values-fr/strings.xml | 127 +- res/values-fr/uploader.xml | 2 +- res/values-hi/strings.xml | 139 +- res/values-hi/uploader.xml | 2 +- res/values-hr/strings.xml | 127 +- res/values-hr/uploader.xml | 2 +- res/values-hu/strings.xml | 127 +- res/values-hu/uploader.xml | 2 +- res/values-in/strings.xml | 127 +- res/values-in/uploader.xml | 34 +- res/values-it/strings.xml | 127 +- res/values-it/uploader.xml | 2 +- res/values-iw/strings.xml | 127 +- res/values-iw/uploader.xml | 2 +- res/values-ja/strings.xml | 127 +- res/values-ja/uploader.xml | 2 +- res/values-ko/strings.xml | 127 +- res/values-ko/uploader.xml | 2 +- res/values-land/styles.xml | 17 + res/values-lt/strings.xml | 127 +- res/values-lt/uploader.xml | 2 +- res/values-lv/strings.xml | 129 +- res/values-lv/uploader.xml | 2 +- res/values-ms/strings.xml | 127 +- res/values-ms/uploader.xml | 2 +- res/values-nb/strings.xml | 127 +- res/values-nb/uploader.xml | 6 +- res/values-nl/strings.xml | 127 +- res/values-nl/uploader.xml | 2 +- res/values-pl/strings.xml | 127 +- res/values-pl/uploader.xml | 2 +- res/values-pt-rPT/strings.xml | 127 +- res/values-pt-rPT/uploader.xml | 2 +- res/values-pt/strings.xml | 127 +- res/values-pt/uploader.xml | 2 +- res/values-rm/strings.xml | 193 +- res/values-rm/uploader.xml | 2 +- res/values-ro/strings.xml | 127 +- res/values-ro/uploader.xml | 2 +- res/values-ru/strings.xml | 131 +- res/values-ru/uploader.xml | 2 +- res/values-sk/strings.xml | 127 +- res/values-sk/uploader.xml | 2 +- res/values-sl/strings.xml | 127 +- res/values-sl/uploader.xml | 2 +- res/values-sr/strings.xml | 127 +- res/values-sr/uploader.xml | 2 +- res/values-sv/strings.xml | 127 +- res/values-sv/uploader.xml | 137 + res/values-sw/strings.xml | 137 +- res/values-sw/uploader.xml | 2 +- res/values-sw600dp-land/dimensions.xml | 44 + res/values-sw600dp-land/styles.xml | 24 + res/values-sw600dp-port/bools.xml | 22 + res/values-sw600dp-port/dimensions.xml | 22 + res/values-sw600dp-port/styles.xml | 41 + res/values-sw600dp/bools.xml | 22 + .../colors.xml} | 7 +- res/values-sw600dp/dimensions.xml | 31 +- res/values-sw600dp/styles.xml | 115 +- res/values-sw720dp-port/dimensions.xml | 31 + res/values-sw720dp-port/styles.xml | 27 + res/values-sw720dp/dimen.xml | 31 + res/values-th/strings.xml | 127 +- res/values-th/uploader.xml | 2 +- res/values-tl/strings.xml | 127 +- res/values-tl/uploader.xml | 2 +- res/values-tr/strings.xml | 127 +- res/values-tr/uploader.xml | 2 +- res/values-uk/strings.xml | 127 +- res/values-uk/uploader.xml | 2 +- res/values-vi/strings.xml | 127 +- res/values-vi/uploader.xml | 2 +- res/values-zh-rCN/strings.xml | 127 +- res/values-zh-rCN/uploader.xml | 4 +- res/values-zh-rTW/strings.xml | 133 +- res/values-zh-rTW/uploader.xml | 2 +- res/values-zu/strings.xml | 127 +- res/values-zu/uploader.xml | 2 +- res/values/arrays.xml | 39 +- res/values/attrs.xml | 48 - res/values/bools.xml | 25 + res/values/colors.xml | 38 +- res/values/dimensions.xml | 55 +- res/values/strings.xml | 250 +- res/values/styles.xml | 178 + res/xml/account_preferences.xml | 4 +- res/xml/account_settings_preferences.xml | 42 +- res/xml/authenticator_alternate.xml | 6 +- res/xml/authenticator_eas.xml | 29 - res/xml/authenticator_imap.xml | 29 - res/xml/authenticator_legacy_email.xml | 29 - res/xml/authenticator_legacy_imap.xml | 29 - ...r_legacy_eas.xml => eas_authenticator.xml} | 4 +- res/xml/general_preferences.xml | 81 +- ...or_pop3.xml => pop_imap_authenticator.xml} | 6 +- {build/res => res}/xml/providers.xml | 54 +- res/xml/searchable.xml | 25 - res/xml/senders.xml | 1 + res/xml/syncadapter_pop3.xml | 28 - ...gacy_imap.xml => syncadapter_pop_imap.xml} | 6 +- res/xml/widget_info.xml | 8 +- src/com/android/email/AttachmentInfo.java | 4 +- src/com/android/email/Controller.java | 1903 ++++++++++ .../ControllerResultUiThreadWrapper.java | 133 + .../Email.java} | 65 +- .../android/email/EmailAddressAdapter.java | 81 + .../email/EmailConnectivityManager.java | 6 +- src/com/android/email/FolderProperties.java | 225 ++ .../android/email/GroupMessagingListener.java | 203 ++ src/com/android/email/LegacyConversions.java | 13 +- src/com/android/email/MessageListContext.java | 187 + .../android/email/MessagingController.java | 2186 ++++++++++++ src/com/android/email/MessagingListener.java | 124 + .../android/email/NotificationController.java | 563 ++- src/com/android/email/Preferences.java | 40 - src/com/android/email/RecipientAdapter.java | 52 + src/com/android/email/RefreshManager.java | 456 +++ src/com/android/email/SecurityPolicy.java | 200 +- src/com/android/email/SingleRunningTask.java | 65 + src/com/android/email/Throttle.java | 180 + .../android/email}/VendorPolicyLoader.java | 50 +- .../activity/AccountSelectorAdapter.java | 636 ++++ .../email/activity/ActionBarController.java | 574 ++++ .../email/activity/ActivityHelper.java | 53 +- .../email/activity/AddressTextView.java | 74 + .../email/activity/AttachmentInfoDialog.java | 120 + .../email/activity/BannerController.java | 106 + .../email/activity/ChipsAddressTextView.java | 56 + .../DeleteMessageConfirmationDialog.java | 93 + .../android/email/activity/EmailActivity.java | 433 +++ .../android/email/activity/EventViewer.java | 76 - .../email/activity/FragmentInstallable.java | 44 + .../android/email/activity/MailboxFinder.java | 264 ++ .../activity/MailboxFragmentAdapter.java | 659 ++++ .../email/activity/MailboxListFragment.java | 1198 +++++++ .../email/activity/MailboxListItem.java | 157 + .../email/activity/MailboxMoveToAdapter.java | 164 + .../activity/MessageCommandButtonView.java | 121 + .../email/activity/MessageCompose.java | 2335 +++++++++++++ .../email/activity/MessageFileView.java | 154 + .../activity/MessageFileViewFragment.java | 147 + .../android/email/activity/MessageList.java | 93 + .../email/activity/MessageListFragment.java | 1573 +++++++++ .../email/activity/MessageListItem.java | 603 ++++ .../activity/MessageListItemCoordinates.java | 313 ++ .../email/activity/MessageOrderManager.java | 383 +++ .../email/activity/MessageViewFragment.java | 523 +++ .../activity/MessageViewFragmentBase.java | 1961 +++++++++++ .../email/activity/MessagesAdapter.java | 429 +++ .../email/activity/MoveMessageToDialog.java | 345 ++ .../android/email/activity/NfcHandler.java | 98 + .../email/activity/RecentMailboxManager.java | 169 + .../email/activity/ShortcutPicker.java | 164 + .../activity/ShortcutPickerFragment.java | 422 +++ .../email/activity/ThreePaneLayout.java | 606 ++++ .../email/activity/UIControllerBase.java | 1048 ++++++ .../email/activity/UIControllerOnePane.java | 646 ++++ .../activity/UIControllerSearchTwoPane.java | 43 + .../email/activity/UIControllerTwoPane.java | 743 ++++ .../android/email/activity/UiUtilities.java | 49 +- src/com/android/email/activity/Welcome.java | 432 +++ .../setup/AccountCheckSettingsFragment.java | 21 +- .../email/activity/setup/AccountSecurity.java | 37 +- .../setup/AccountServerBaseFragment.java | 6 - .../email/activity/setup/AccountSettings.java | 215 +- ...untSettingsEditQuickResponsesFragment.java | 6 +- .../setup/AccountSettingsFragment.java | 460 ++- .../activity/setup/AccountSettingsUtils.java | 84 +- .../setup/AccountSetupAccountType.java | 148 + .../activity/setup/AccountSetupBasics.java | 195 +- .../activity/setup/AccountSetupExchange.java | 221 ++ .../setup/AccountSetupExchangeFragment.java | 490 +++ .../activity/setup/AccountSetupIncoming.java | 134 +- .../setup/AccountSetupIncomingFragment.java | 226 +- .../activity/setup/AccountSetupNames.java | 23 +- .../activity/setup/AccountSetupOptions.java | 110 +- .../setup/AccountSetupOutgoingFragment.java | 40 +- .../activity/setup/AccountSetupType.java | 139 - .../email/activity/setup/DebugFragment.java | 83 +- .../setup/EditQuickResponseDialog.java | 9 +- .../setup/EmailPreferenceFragment.java | 39 - .../activity/setup/ForwardingIntent.java | 30 - .../activity/setup/GeneralPreferences.java | 101 +- .../email/activity/setup/MailboxSettings.java | 17 +- .../activity/setup/PolicyListPreference.java | 45 - .../email/activity/setup/SetupData.java | 36 +- .../email/data/ThrottlingCursorLoader.java | 108 + src/com/android/email/mail/Store.java | 38 +- src/com/android/email/mail/Transport.java | 165 + .../email/mail/store/ExchangeStore.java | 55 + .../email/mail/store/ImapConnection.java | 19 +- .../android/email/mail/store/ImapFolder.java | 11 +- .../android/email/mail/store/ImapStore.java | 40 +- .../android/email/mail/store/Pop3Store.java | 446 ++- .../email/mail/store/ServiceStore.java | 16 +- .../mail/store/imap/ImapResponseParser.java | 16 +- .../mail/store/imap/ImapTempFileLiteral.java | 13 +- .../mail/transport}/CountingOutputStream.java | 2 +- .../transport}/EOLConvertingOutputStream.java | 2 +- .../email/mail/transport/MailTransport.java | 133 +- .../email/mail/transport/SmtpSender.java | 54 +- .../email/provider/AccountReconciler.java | 25 +- .../email/provider/AttachmentProvider.java | 6 +- .../android/email/provider/ContentCache.java | 24 +- src/com/android/email/provider/DBHelper.java | 223 +- .../android/email/provider/EmailProvider.java | 3058 +---------------- .../email/provider/FolderPickerActivity.java | 214 -- .../email/provider/FolderPickerCallback.java | 25 - .../email/provider/FolderPickerDialog.java | 160 - .../provider/FolderPickerSelectorAdapter.java | 46 - src/com/android/email/provider/Utilities.java | 171 - .../email/provider/WidgetProvider.java | 330 +- .../android/email/service/AccountService.java | 16 +- .../service/AttachmentDownloadService.java | 122 +- .../service/EasAuthenticatorService.java | 145 +- .../EasAuthenticatorServiceAlternate.java | 8 +- .../service/EasTestAuthenticatorService.java | 3 +- .../EmailBroadcastProcessorService.java | 47 +- .../email/service/EmailServiceStub.java | 562 --- .../email/service/EmailServiceUtils.java | 662 +--- .../android/email/service/ImapService.java | 1296 ------- .../email/service/ImapTempFileLiteral.java | 127 - .../LegacyEmailAuthenticatorService.java | 23 - .../service/LegacyImapSyncAdapterService.java | 20 - .../android/email/service/MailService.java | 699 +++- .../android/email/service/PolicyService.java | 28 +- .../service/Pop3AuthenticatorService.java | 23 - .../android/email/service/Pop3Service.java | 456 --- .../email/service/Pop3SyncAdapterService.java | 20 - ....java => PopImapAuthenticatorService.java} | 49 +- .../service/PopImapSyncAdapterService.java | 190 +- .../email/view/NonLockingScrollView.java | 139 + src/com/android/email/view/RigidWebView.java | 103 + .../email/view/SizeBoundingFrameLayout.java | 115 + src/com/android/email/widget/EmailWidget.java | 555 +++ .../email/widget/EmailWidgetLoader.java | 208 ++ .../email/widget/WidgetConfiguration.java | 133 + .../android/email/widget/WidgetManager.java | 158 + .../ui/CreateShortcutActivityEmail.java | 50 - .../ui/MailboxSelectionActivityEmail.java | 23 - .../browse/EmailConversationProvider.java | 32 - .../providers/EmailAccountCacheProvider.java | 52 - .../android/mail/providers/UIProvider.java | 1453 ++++++++ .../protos/boot/AccountReceiver.java | 39 - src/com/android/mail/utils/LogTag.java | 28 - src/com/android/mail/utils/LogUtils.java | 416 +++ .../mail/utils/LoggingInputStream.java | 108 + src/com/beetstra/ThirdPartyProject.prop | 9 + src/org/apache/commons/io/CopyUtils.java | 332 ++ .../apache/commons/io/DirectoryWalker.java | 620 ++++ src/org/apache/commons/io/EndianUtils.java | 489 +++ src/org/apache/commons/io/FileCleaner.java | 154 + .../commons/io/FileCleaningTracker.java | 259 ++ .../apache/commons/io/FileDeleteStrategy.java | 156 + .../apache/commons/io/FileSystemUtils.java | 457 +++ src/org/apache/commons/io/FileUtils.java | 1890 ++++++++++ src/org/apache/commons/io/FilenameUtils.java | 1260 +++++++ src/org/apache/commons/io/HexDump.java | 149 + src/org/apache/commons/io/IOCase.java | 238 ++ .../commons/io/IOExceptionWithCause.java | 69 + src/org/apache/commons/io/IOUtils.java | 1274 +++++++ src/org/apache/commons/io/LineIterator.java | 181 + .../apache/commons/io/ThirdPartyProject.prop | 9 + .../io/comparator/DefaultFileComparator.java | 66 + .../comparator/ExtensionFileComparator.java | 110 + .../LastModifiedFileComparator.java | 77 + .../io/comparator/NameFileComparator.java | 104 + .../io/comparator/PathFileComparator.java | 105 + .../io/comparator/ReverseComparator.java | 57 + .../io/comparator/SizeFileComparator.java | 130 + .../apache/commons/io/comparator/package.html | 25 + .../io/filefilter/AbstractFileFilter.java | 67 + .../commons/io/filefilter/AgeFileFilter.java | 150 + .../commons/io/filefilter/AndFileFilter.java | 171 + .../io/filefilter/CanReadFileFilter.java | 92 + .../io/filefilter/CanWriteFileFilter.java | 80 + .../io/filefilter/ConditionalFileFilter.java | 67 + .../io/filefilter/DelegateFileFilter.java | 104 + .../io/filefilter/DirectoryFileFilter.java | 73 + .../io/filefilter/EmptyFileFilter.java | 84 + .../io/filefilter/FalseFileFilter.java | 72 + .../commons/io/filefilter/FileFileFilter.java | 60 + .../io/filefilter/FileFilterUtils.java | 361 ++ .../io/filefilter/HiddenFileFilter.java | 76 + .../commons/io/filefilter/IOFileFilter.java | 55 + .../commons/io/filefilter/NameFileFilter.java | 194 ++ .../commons/io/filefilter/NotFileFilter.java | 78 + .../commons/io/filefilter/OrFileFilter.java | 164 + .../io/filefilter/PrefixFileFilter.java | 200 ++ .../io/filefilter/RegexFileFilter.java | 122 + .../commons/io/filefilter/SizeFileFilter.java | 103 + .../io/filefilter/SuffixFileFilter.java | 201 ++ .../commons/io/filefilter/TrueFileFilter.java | 72 + .../io/filefilter/WildcardFileFilter.java | 199 ++ .../apache/commons/io/filefilter/package.html | 143 + .../io/input/AutoCloseInputStream.java | 129 + .../commons/io/input/CharSequenceReader.java | 155 + .../input/ClassLoaderObjectInputStream.java | 77 + .../io/input/CloseShieldInputStream.java | 52 + .../commons/io/input/ClosedInputStream.java | 48 + .../commons/io/input/CountingInputStream.java | 175 + .../commons/io/input/DemuxInputStream.java | 93 + .../commons/io/input/NullInputStream.java | 329 ++ .../apache/commons/io/input/NullReader.java | 313 ++ .../commons/io/input/ProxyInputStream.java | 129 + .../apache/commons/io/input/ProxyReader.java | 130 + .../io/input/SwappedDataInputStream.java | 251 ++ .../commons/io/input/TeeInputStream.java | 147 + src/org/apache/commons/io/input/package.html | 25 + .../io/output/ByteArrayOutputStream.java | 312 ++ .../io/output/CloseShieldOutputStream.java | 52 + .../commons/io/output/ClosedOutputStream.java | 50 + .../io/output/CountingOutputStream.java | 154 + .../io/output/DeferredFileOutputStream.java | 269 ++ .../commons/io/output/DemuxOutputStream.java | 105 + .../io/output/FileWriterWithEncoding.java | 324 ++ .../commons/io/output/LockableFileWriter.java | 333 ++ .../commons/io/output/NullOutputStream.java | 65 + .../apache/commons/io/output/NullWriter.java | 96 + .../commons/io/output/ProxyOutputStream.java | 89 + .../apache/commons/io/output/ProxyWriter.java | 111 + .../commons/io/output/TeeOutputStream.java | 94 + .../io/output/ThresholdingOutputStream.java | 257 ++ src/org/apache/commons/io/output/package.html | 25 + src/org/apache/commons/io/overview.html | 32 + src/org/apache/commons/io/package.html | 47 + .../james/mime4j/AbstractContentHandler.java | 113 + .../apache/james/mime4j/BodyDescriptor.java | 392 +++ .../james/mime4j/CloseShieldInputStream.java | 129 + .../apache/james/mime4j/ContentHandler.java | 177 + .../mime4j/EOLConvertingInputStream.java | 108 + src/org/apache/james/mime4j/Log.java | 116 + .../apache/james/mime4j/LogFactory.java} | 14 +- .../james/mime4j/MimeBoundaryInputStream.java | 184 + .../apache/james/mime4j/MimeStreamParser.java | 325 ++ .../apache/james/mime4j/RootInputStream.java | 111 + .../james/mime4j/SimpleContentHandler.java | 100 + .../james/mime4j/ThirdPartyProject.prop | 10 + .../james/mime4j/codec/EncoderUtil.java | 630 ++++ .../mime4j/decoder/Base64InputStream.java | 151 + .../james/mime4j/decoder/ByteQueue.java | 62 + .../james/mime4j/decoder/DecoderUtil.java | 279 ++ .../decoder/QuotedPrintableInputStream.java | 229 ++ .../decoder/UnboundedFifoByteBuffer.java | 272 ++ .../james/mime4j/field/AddressListField.java | 65 + .../field/ContentTransferEncodingField.java | 88 + .../james/mime4j/field/ContentTypeField.java | 259 ++ .../james/mime4j/field/DateTimeField.java | 73 + .../mime4j/field/DefaultFieldParser.java | 45 + .../mime4j/field/DelegatingFieldParser.java | 47 + src/org/apache/james/mime4j/field/Field.java | 192 ++ .../james/mime4j/field/FieldParser.java | 21 + .../james/mime4j/field/MailboxField.java | 70 + .../james/mime4j/field/MailboxListField.java | 67 + .../james/mime4j/field/UnstructuredField.java | 49 + .../james/mime4j/field/address/Address.java | 52 + .../mime4j/field/address/AddressList.java | 138 + .../james/mime4j/field/address/Builder.java | 243 ++ .../mime4j/field/address/DomainList.java | 76 + .../james/mime4j/field/address/Group.java | 75 + .../james/mime4j/field/address/Mailbox.java | 121 + .../mime4j/field/address/MailboxList.java | 71 + .../mime4j/field/address/NamedMailbox.java | 71 + .../field/address/parser/ASTaddr_spec.java | 19 + .../field/address/parser/ASTaddress.java | 19 + .../field/address/parser/ASTaddress_list.java | 19 + .../field/address/parser/ASTangle_addr.java | 19 + .../field/address/parser/ASTdomain.java | 19 + .../field/address/parser/ASTgroup_body.java | 19 + .../field/address/parser/ASTlocal_part.java | 19 + .../field/address/parser/ASTmailbox.java | 19 + .../field/address/parser/ASTname_addr.java | 19 + .../field/address/parser/ASTphrase.java | 19 + .../mime4j/field/address/parser/ASTroute.java | 19 + .../address/parser/AddressListParser.java | 977 ++++++ .../field/address/parser/AddressListParser.jj | 595 ++++ .../parser/AddressListParserConstants.java | 76 + .../parser/AddressListParserTokenManager.java | 1009 ++++++ .../AddressListParserTreeConstants.java | 35 + .../parser/AddressListParserVisitor.java | 19 + .../mime4j/field/address/parser/BaseNode.java | 30 + .../parser/JJTAddressListParserState.java | 123 + .../mime4j/field/address/parser/Node.java | 37 + .../field/address/parser/ParseException.java | 207 ++ .../address/parser/SimpleCharStream.java | 454 +++ .../field/address/parser/SimpleNode.java | 87 + .../mime4j/field/address/parser/Token.java | 96 + .../field/address/parser/TokenMgrError.java | 148 + .../contenttype/parser/ContentTypeParser.java | 268 ++ .../parser/ContentTypeParserConstants.java | 62 + .../parser/ContentTypeParserTokenManager.java | 877 +++++ .../contenttype/parser/ParseException.java | 207 ++ .../contenttype/parser/SimpleCharStream.java | 454 +++ .../field/contenttype/parser/Token.java | 96 + .../contenttype/parser/TokenMgrError.java | 148 + .../james/mime4j/field/datetime/DateTime.java | 127 + .../field/datetime/parser/DateTimeParser.java | 570 +++ .../parser/DateTimeParserConstants.java | 86 + .../parser/DateTimeParserTokenManager.java | 882 +++++ .../field/datetime/parser/ParseException.java | 207 ++ .../datetime/parser/SimpleCharStream.java | 454 +++ .../mime4j/field/datetime/parser/Token.java | 96 + .../field/datetime/parser/TokenMgrError.java | 148 + .../james/mime4j/message/AbstractBody.java | 47 + .../james/mime4j/message/BinaryBody.java | 42 + src/org/apache/james/mime4j/message/Body.java | 54 + .../apache/james/mime4j/message/BodyPart.java | 42 + .../apache/james/mime4j/message/Entity.java | 170 + .../apache/james/mime4j/message/Header.java | 158 + .../mime4j/message/MemoryBinaryBody.java | 92 + .../james/mime4j/message/MemoryTextBody.java | 118 + .../apache/james/mime4j/message/Message.java | 257 ++ .../james/mime4j/message/Multipart.java | 203 ++ .../mime4j/message/TempFileBinaryBody.java | 91 + .../mime4j/message/TempFileTextBody.java | 117 + .../apache/james/mime4j/message/TextBody.java | 42 + .../apache/james/mime4j/util/CharsetUtil.java | 1246 +++++++ .../james/mime4j/util/PartialInputStream.java | 63 + .../mime4j/util/PositionInputStream.java | 87 + .../james/mime4j/util/SimpleTempStorage.java | 238 ++ .../apache/james/mime4j/util/TempFile.java | 84 + .../apache/james/mime4j/util/TempPath.java | 73 + .../apache/james/mime4j/util/TempStorage.java | 72 + tests/{oldAndroid.mk => Android.mk} | 0 .../email/NotificationControllerTest.java | 6 +- .../android/email/SecurityPolicyTests.java | 602 ---- .../setup/AccountSetupAccountTypeTests.java | 106 + .../android/email/provider/PolicyTests.java | 216 -- .../android/email/provider/ProviderTests.java | 2567 -------------- .../utility/UtilityMediumTests.java | 8 +- 762 files changed, 73554 insertions(+), 23226 deletions(-) delete mode 100644 build/res/values/strings.xml delete mode 100644 build/res/xml/services.xml rename src/com/android/email/service/LegacyImapAuthenticatorService.java => emailcommon/src/com/android/emailcommon/AccountManagerTypes.java (68%) rename src/com/android/email/service/ImapAuthenticatorService.java => emailcommon/src/com/android/emailcommon/CalendarProviderStub.java (63%) delete mode 100644 emailcommon/src/com/android/emailcommon/provider/Account.aidl delete mode 100644 emailcommon/src/com/android/emailcommon/provider/MailboxUtilities.java delete mode 100644 emailcommon/src/com/android/emailcommon/service/EmailServiceCallback.java delete mode 100644 emailsync/Android.mk delete mode 100644 emailsync/src/com/android/emailsync/AbstractSyncService.java delete mode 100644 emailsync/src/com/android/emailsync/EmailSyncAlarmReceiver.java delete mode 100644 emailsync/src/com/android/emailsync/FileLogger.java delete mode 100644 emailsync/src/com/android/emailsync/MailboxAlarmReceiver.java delete mode 100644 emailsync/src/com/android/emailsync/MessageMoveRequest.java delete mode 100644 emailsync/src/com/android/emailsync/PartRequest.java delete mode 100644 emailsync/src/com/android/emailsync/Request.java delete mode 100644 emailsync/src/com/android/emailsync/SyncManager.java create mode 100755 remove-exchange-support.sh create mode 100644 res/drawable-hdpi/attachment_bg_holo.9.png create mode 100644 res/drawable-hdpi/btn_check_off_normal_holo_light.png create mode 100644 res/drawable-hdpi/btn_check_on_normal_holo_light.png create mode 100644 res/drawable-hdpi/btn_maybe_off.png create mode 100644 res/drawable-hdpi/btn_no_off.png.png create mode 100644 res/drawable-hdpi/btn_star_off_convo_holo_light.png create mode 100644 res/drawable-hdpi/btn_star_off_normal_email_holo_light.png create mode 100644 res/drawable-hdpi/btn_star_on_convo_holo_light.png create mode 100644 res/drawable-hdpi/btn_star_on_normal_email_holo_light.png create mode 100644 res/drawable-hdpi/btn_yes_off.png.png create mode 100644 res/drawable-hdpi/divider_horizontal_holo_light.9.png create mode 100644 res/drawable-hdpi/expander_close_holo_light.9.png create mode 100644 res/drawable-hdpi/expander_open_holo_light.9.png create mode 100644 res/drawable-hdpi/gradient_bg_email_widget_holo.9.png create mode 100644 res/drawable-hdpi/header_bg_email_widget_holo.9.png create mode 100644 res/drawable-hdpi/header_convo_view_sender_bg_holo.9.png create mode 100644 res/drawable-hdpi/ic_badge_attachment.png create mode 100644 res/drawable-hdpi/ic_badge_forward_holo_light.png create mode 100644 res/drawable-hdpi/ic_badge_invite_holo_light.png create mode 100644 res/drawable-hdpi/ic_badge_reply_forward_holo_light.png create mode 100644 res/drawable-hdpi/ic_badge_reply_holo_light.png create mode 100644 res/drawable-hdpi/ic_forward_holo_dark.png create mode 100644 res/drawable-hdpi/ic_list_combined_inbox.png create mode 100644 res/drawable-hdpi/ic_mailbox_collapsed_holo_light.png create mode 100644 res/drawable-hdpi/ic_menu_compose_normal_holo_light.png create mode 100644 res/drawable-hdpi/ic_menu_mark_read_holo_light.png create mode 100644 res/drawable-hdpi/ic_menu_mark_unread_holo_light.png create mode 100644 res/drawable-hdpi/ic_menu_move_to_holo_light.png create mode 100644 res/drawable-hdpi/ic_menu_refresh_holo_light.png create mode 100644 res/drawable-hdpi/ic_menu_search_holo_light.png create mode 100644 res/drawable-hdpi/ic_menu_send_disabled_holo_light.png create mode 100644 res/drawable-hdpi/ic_menu_send_holo_light.png create mode 100644 res/drawable-hdpi/ic_menu_send_normal_holo_light.png create mode 100644 res/drawable-hdpi/ic_menu_star_holo_light.png create mode 100644 res/drawable-hdpi/ic_menu_star_off_holo_light.png create mode 100644 res/drawable-hdpi/ic_menu_trash_holo_light.png create mode 100644 res/drawable-hdpi/ic_newer_arrow_disabled_holo_light.png create mode 100644 res/drawable-hdpi/ic_newer_arrow_holo_light.png create mode 100644 res/drawable-hdpi/ic_older_arrow_disabled_holo_light.png create mode 100644 res/drawable-hdpi/ic_older_arrow_holo_light.png create mode 100644 res/drawable-hdpi/ic_remove_attachment_holo_light.png create mode 100644 res/drawable-hdpi/ic_reply.png create mode 100644 res/drawable-hdpi/ic_reply_all.png create mode 100644 res/drawable-hdpi/ic_reply_all_holo_dark.png create mode 100644 res/drawable-hdpi/ic_reply_holo_dark.png create mode 100644 res/drawable-hdpi/ic_show_images_holo_light.png create mode 100644 res/drawable-hdpi/ic_spam_normal_holo_light.png create mode 100644 res/drawable-hdpi/list_div_top_btm_email_widget_holo.9.png create mode 100644 res/drawable-mdpi/attachment_bg_holo.9.png create mode 100644 res/drawable-mdpi/btn_check_off_normal_holo_light.png create mode 100644 res/drawable-mdpi/btn_check_on_normal_holo_light.png create mode 100644 res/drawable-mdpi/btn_maybe_off.png create mode 100644 res/drawable-mdpi/btn_no_off.png.png create mode 100644 res/drawable-mdpi/btn_star_off_convo_holo_light.png create mode 100644 res/drawable-mdpi/btn_star_off_normal_email_holo_light.png create mode 100644 res/drawable-mdpi/btn_star_on_convo_holo_light.png create mode 100644 res/drawable-mdpi/btn_star_on_normal_email_holo_light.png create mode 100644 res/drawable-mdpi/btn_yes_off.png.png create mode 100644 res/drawable-mdpi/divider_horizontal_holo_light.9.png create mode 100644 res/drawable-mdpi/expander_close_holo_light.9.png create mode 100644 res/drawable-mdpi/expander_open_holo_light.9.png create mode 100644 res/drawable-mdpi/gradient_bg_email_widget_holo.9.png create mode 100644 res/drawable-mdpi/header_bg_email_widget_holo.9.png create mode 100644 res/drawable-mdpi/header_convo_view_sender_bg_holo.9.png create mode 100644 res/drawable-mdpi/ic_badge_attachment.png create mode 100644 res/drawable-mdpi/ic_badge_forward_holo_light.png create mode 100644 res/drawable-mdpi/ic_badge_invite_holo_light.png create mode 100644 res/drawable-mdpi/ic_badge_reply_forward_holo_light.png create mode 100644 res/drawable-mdpi/ic_badge_reply_holo_light.png create mode 100644 res/drawable-mdpi/ic_exchange_minitab_selected.png create mode 100644 res/drawable-mdpi/ic_exchange_selected.png create mode 100644 res/drawable-mdpi/ic_folder_drafts_holo_light.png create mode 100644 res/drawable-mdpi/ic_folder_inbox_holo_light.png create mode 100644 res/drawable-mdpi/ic_folder_outbox_holo_light.png create mode 100644 res/drawable-mdpi/ic_folder_sent_holo_light.png create mode 100644 res/drawable-mdpi/ic_forward_holo_dark.png create mode 100644 res/drawable-mdpi/ic_list_combined_inbox.png create mode 100644 res/drawable-mdpi/ic_mailbox_collapsed_holo_light.png create mode 100644 res/drawable-mdpi/ic_menu_compose_normal_holo_light.png create mode 100644 res/drawable-mdpi/ic_menu_mark_read_holo_light.png create mode 100644 res/drawable-mdpi/ic_menu_mark_unread_holo_light.png create mode 100644 res/drawable-mdpi/ic_menu_move_to_holo_light.png create mode 100644 res/drawable-mdpi/ic_menu_refresh_holo_light.png create mode 100644 res/drawable-mdpi/ic_menu_search_holo_light.png create mode 100644 res/drawable-mdpi/ic_menu_send_disabled_holo_light.png create mode 100644 res/drawable-mdpi/ic_menu_send_holo_light.png create mode 100644 res/drawable-mdpi/ic_menu_send_normal_holo_light.png create mode 100644 res/drawable-mdpi/ic_menu_star_holo_light.png create mode 100644 res/drawable-mdpi/ic_menu_star_off_holo_light.png create mode 100644 res/drawable-mdpi/ic_menu_trash_holo_light.png create mode 100644 res/drawable-mdpi/ic_newer_arrow_disabled_holo_light.png create mode 100644 res/drawable-mdpi/ic_newer_arrow_holo_light.png create mode 100644 res/drawable-mdpi/ic_notification_multiple_mail_holo_dark.png create mode 100644 res/drawable-mdpi/ic_older_arrow_disabled_holo_light.png create mode 100644 res/drawable-mdpi/ic_older_arrow_holo_light.png create mode 100644 res/drawable-mdpi/ic_remove_attachment_holo_light.png create mode 100644 res/drawable-mdpi/ic_reply.png create mode 100644 res/drawable-mdpi/ic_reply_all.png create mode 100644 res/drawable-mdpi/ic_reply_all_holo_dark.png create mode 100644 res/drawable-mdpi/ic_reply_holo_dark.png create mode 100644 res/drawable-mdpi/ic_show_images_holo_light.png create mode 100644 res/drawable-mdpi/ic_spam_normal_holo_light.png create mode 100644 res/drawable-mdpi/list_div_top_btm_email_widget_holo.9.png create mode 100644 res/drawable-mdpi/stat_notify_email_generic.png create mode 100644 res/drawable-sw600dp-hdpi/btn_check_off_normal_holo_light.png create mode 100644 res/drawable-sw600dp-hdpi/btn_check_on_normal_holo_light.png create mode 100644 res/drawable-sw600dp-hdpi/btn_star_off_normal_email_holo_light.png create mode 100644 res/drawable-sw600dp-hdpi/btn_star_on_normal_email_holo_light.png create mode 100644 res/drawable-sw600dp-mdpi/btn_check_off_normal_holo_light.png create mode 100644 res/drawable-sw600dp-mdpi/btn_check_on_normal_holo_light.png create mode 100644 res/drawable-sw600dp-mdpi/btn_star_off_normal_email_holo_light.png create mode 100644 res/drawable-sw600dp-mdpi/btn_star_on_normal_email_holo_light.png create mode 100644 res/drawable-sw600dp-xhdpi/btn_check_off_normal_holo_light.png create mode 100644 res/drawable-sw600dp-xhdpi/btn_check_on_normal_holo_light.png create mode 100644 res/drawable-sw600dp-xhdpi/btn_star_off_normal_email_holo_light.png create mode 100644 res/drawable-sw600dp-xhdpi/btn_star_on_normal_email_holo_light.png create mode 100644 res/drawable-xhdpi/attachment_bg_holo.9.png create mode 100644 res/drawable-xhdpi/btn_check_off_normal_holo_light.png create mode 100644 res/drawable-xhdpi/btn_check_on_normal_holo_light.png create mode 100644 res/drawable-xhdpi/btn_maybe_off.png create mode 100644 res/drawable-xhdpi/btn_no_off.png.png create mode 100644 res/drawable-xhdpi/btn_star_off_convo_holo_light.png create mode 100644 res/drawable-xhdpi/btn_star_off_normal_email_holo_light.png create mode 100644 res/drawable-xhdpi/btn_star_on_convo_holo_light.png create mode 100644 res/drawable-xhdpi/btn_star_on_normal_email_holo_light.png create mode 100644 res/drawable-xhdpi/btn_yes_off.png.png create mode 100644 res/drawable-xhdpi/divider_horizontal_holo_light.9.png create mode 100644 res/drawable-xhdpi/expander_close_holo_light.9.png create mode 100644 res/drawable-xhdpi/expander_open_holo_light.9.png create mode 100644 res/drawable-xhdpi/gradient_bg_email_widget_holo.9.png create mode 100644 res/drawable-xhdpi/header_bg_email_widget_holo.9.png create mode 100644 res/drawable-xhdpi/header_convo_view_sender_bg_holo.9.png create mode 100644 res/drawable-xhdpi/ic_badge_attachment.png create mode 100644 res/drawable-xhdpi/ic_badge_forward_holo_light.png create mode 100644 res/drawable-xhdpi/ic_badge_invite_holo_light.png create mode 100644 res/drawable-xhdpi/ic_badge_reply_forward_holo_light.png create mode 100644 res/drawable-xhdpi/ic_badge_reply_holo_light.png create mode 100644 res/drawable-xhdpi/ic_forward_holo_dark.png create mode 100644 res/drawable-xhdpi/ic_list_combined_inbox.png create mode 100644 res/drawable-xhdpi/ic_mailbox_collapsed_holo_light.png create mode 100644 res/drawable-xhdpi/ic_menu_compose_normal_holo_light.png create mode 100644 res/drawable-xhdpi/ic_menu_mark_read_holo_light.png create mode 100644 res/drawable-xhdpi/ic_menu_mark_unread_holo_light.png create mode 100644 res/drawable-xhdpi/ic_menu_move_to_holo_light.png create mode 100644 res/drawable-xhdpi/ic_menu_refresh_holo_light.png create mode 100644 res/drawable-xhdpi/ic_menu_search_holo_light.png create mode 100644 res/drawable-xhdpi/ic_menu_send_disabled_holo_light.png create mode 100644 res/drawable-xhdpi/ic_menu_send_holo_light.png create mode 100644 res/drawable-xhdpi/ic_menu_send_normal_holo_light.png create mode 100644 res/drawable-xhdpi/ic_menu_star_holo_light.png create mode 100644 res/drawable-xhdpi/ic_menu_star_off_holo_light.png create mode 100644 res/drawable-xhdpi/ic_menu_trash_holo_light.png create mode 100644 res/drawable-xhdpi/ic_newer_arrow_disabled_holo_light.png create mode 100644 res/drawable-xhdpi/ic_newer_arrow_holo_light.png create mode 100644 res/drawable-xhdpi/ic_older_arrow_disabled_holo_light.png create mode 100644 res/drawable-xhdpi/ic_older_arrow_holo_light.png create mode 100644 res/drawable-xhdpi/ic_remove_attachment_holo_light.png create mode 100644 res/drawable-xhdpi/ic_reply.png create mode 100644 res/drawable-xhdpi/ic_reply_all.png create mode 100644 res/drawable-xhdpi/ic_reply_all_holo_dark.png create mode 100644 res/drawable-xhdpi/ic_reply_holo_dark.png create mode 100644 res/drawable-xhdpi/ic_show_images_holo_light.png create mode 100644 res/drawable-xhdpi/ic_spam_normal_holo_light.png create mode 100644 res/drawable-xhdpi/list_div_top_btm_email_widget_holo.9.png create mode 100644 res/drawable/attachment_background.xml create mode 100644 res/drawable/menu_item_newer.xml create mode 100644 res/drawable/menu_item_older.xml create mode 100644 res/layout-land/message_view_header_actions.xml create mode 100644 res/layout-sw600dp-land/account_setup_exchange.xml create mode 100644 res/layout-sw600dp-land/message_view_invitation.xml create mode 100644 res/layout-sw600dp-port/account_setup_exchange.xml create mode 100644 res/layout-sw600dp-port/message_view_invitation.xml create mode 100644 res/layout-sw600dp/account_setup_exchange_fragment.xml create mode 100644 res/layout-sw600dp/action_bar_spinner.xml create mode 100644 res/layout-sw600dp/address_text_view.xml create mode 100644 res/layout-sw600dp/compose_area_buttons.xml create mode 100644 res/layout-sw600dp/message_compose.xml create mode 100644 res/layout-sw600dp/message_view_attachment.xml create mode 100644 res/layout-sw600dp/message_view_fragment.xml create mode 100644 res/layout-sw600dp/message_view_header_actions.xml create mode 100644 res/layout-sw600dp/waiting_for_sync_message.xml create mode 100644 res/layout-v14/compose_area_recipients.xml create mode 100644 res/layout/account_settings_exchange_fragment.xml create mode 100644 res/layout/account_setup_exchange.xml create mode 100644 res/layout/account_setup_exchange_fragment.xml rename res/layout/{account_type.xml => action_bar_custom_view.xml} (57%) rename res/layout/{quick_response_edit_dialog.xml => action_bar_indeterminate_progress.xml} (66%) create mode 100644 res/layout/action_bar_search.xml create mode 100644 res/layout/action_bar_spinner.xml create mode 100644 res/layout/action_bar_spinner_dropdown.xml create mode 100644 res/layout/action_bar_spinner_dropdown_header.xml create mode 100644 res/layout/compose_area_recipients.xml rename res/{layout-sw600dp/account_type.xml => layout/compose_body.xml} (63%) create mode 100644 res/layout/compose_from.xml delete mode 100644 res/layout/conversation_item_view_normal.xml create mode 100644 res/layout/email_activity_one_pane.xml create mode 100644 res/layout/email_activity_two_pane.xml rename res/{xml-sw600dp/widget_info.xml => layout/mailbox_list_fragment.xml} (62%) create mode 100644 res/layout/mailbox_list_header.xml create mode 100644 res/layout/mailbox_list_item.xml create mode 100644 res/layout/message_command_button_view.xml create mode 100644 res/layout/message_compose.xml create mode 100644 res/layout/message_file_view.xml create mode 100644 res/layout/message_list_fragment.xml create mode 100644 res/layout/message_list_item_footer.xml create mode 100644 res/layout/message_list_item_normal.xml rename res/layout/{conversation_item_view_wide.xml => message_list_item_wide.xml} (62%) create mode 100644 res/layout/message_list_search_header.xml create mode 100644 res/layout/message_list_warning.xml create mode 100644 res/layout/message_view_attachment.xml create mode 100644 res/layout/message_view_details.xml create mode 100644 res/layout/message_view_fragment.xml create mode 100644 res/layout/message_view_header_actions.xml create mode 100644 res/layout/message_view_header_upper.xml create mode 100644 res/layout/message_view_invitation.xml create mode 100644 res/layout/message_view_subheader.xml create mode 100644 res/layout/quoted_text.xml create mode 100644 res/layout/recipient_dropdown_item.xml create mode 100644 res/layout/recipient_dropdown_item_loading.xml create mode 100644 res/layout/three_pane.xml create mode 100644 res/layout/waiting_for_sync_message.xml create mode 100644 res/layout/widget.xml create mode 100644 res/layout/widget_list_item.xml create mode 100644 res/layout/widget_loading.xml create mode 100644 res/menu-sw600dp-port/message_search_list_fragment_option.xml create mode 100644 res/menu-sw600dp-port/message_view_fragment_option.xml create mode 100644 res/menu-sw600dp/email_activity_options.xml create mode 100644 res/menu-sw600dp/message_compose_option.xml create mode 100644 res/menu-sw600dp/message_list_fragment_cab_options.xml create mode 100644 res/menu-sw600dp/message_list_fragment_option.xml create mode 100644 res/menu-sw600dp/message_search_list_fragment_option.xml create mode 100644 res/menu-sw600dp/message_view_fragment_option.xml create mode 100644 res/menu/email_activity_options.xml create mode 100644 res/menu/message_compose_option.xml create mode 100644 res/menu/message_header_overflow_menu.xml create mode 100644 res/menu/message_list_fragment_cab_options.xml create mode 100644 res/menu/message_list_fragment_option.xml create mode 100644 res/menu/message_view_fragment_option.xml create mode 100644 res/menu/welcome.xml create mode 100644 res/mipmap-hdpi/ic_launcher_email.png delete mode 100644 res/mipmap-hdpi/ic_launcher_mail.png delete mode 100644 res/mipmap-hdpi/ic_launcher_shortcut_folder.png create mode 100644 res/mipmap-mdpi/ic_launcher_email.png delete mode 100644 res/mipmap-mdpi/ic_launcher_mail.png delete mode 100644 res/mipmap-mdpi/ic_launcher_shortcut_folder.png create mode 100644 res/mipmap-xhdpi/ic_launcher_email.png delete mode 100644 res/mipmap-xhdpi/ic_launcher_mail.png delete mode 100644 res/mipmap-xhdpi/ic_launcher_shortcut_folder.png create mode 100644 res/values-sv/uploader.xml create mode 100644 res/values-sw600dp-land/dimensions.xml create mode 100644 res/values-sw600dp-land/styles.xml create mode 100644 res/values-sw600dp-port/bools.xml create mode 100644 res/values-sw600dp-port/styles.xml create mode 100644 res/values-sw600dp/bools.xml rename res/{values-sw720dp-land/dimensions.xml => values-sw600dp/colors.xml} (75%) create mode 100644 res/values-sw720dp-port/dimensions.xml create mode 100644 res/values-sw720dp-port/styles.xml create mode 100644 res/values-sw720dp/dimen.xml create mode 100644 res/values/bools.xml mode change 100755 => 100644 res/xml/account_settings_preferences.xml delete mode 100644 res/xml/authenticator_eas.xml delete mode 100644 res/xml/authenticator_imap.xml delete mode 100644 res/xml/authenticator_legacy_email.xml delete mode 100644 res/xml/authenticator_legacy_imap.xml rename res/xml/{authenticator_legacy_eas.xml => eas_authenticator.xml} (89%) rename res/xml/{authenticator_pop3.xml => pop_imap_authenticator.xml} (87%) rename {build/res => res}/xml/providers.xml (92%) delete mode 100644 res/xml/searchable.xml delete mode 100644 res/xml/syncadapter_pop3.xml rename res/xml/{syncadapter_legacy_imap.xml => syncadapter_pop_imap.xml} (83%) create mode 100644 src/com/android/email/Controller.java create mode 100644 src/com/android/email/ControllerResultUiThreadWrapper.java rename src/com/android/{email2/ui/MailActivityEmail.java => email/Email.java} (78%) create mode 100644 src/com/android/email/EmailAddressAdapter.java create mode 100644 src/com/android/email/FolderProperties.java create mode 100644 src/com/android/email/GroupMessagingListener.java create mode 100644 src/com/android/email/MessageListContext.java create mode 100644 src/com/android/email/MessagingController.java create mode 100644 src/com/android/email/MessagingListener.java create mode 100644 src/com/android/email/RecipientAdapter.java create mode 100644 src/com/android/email/RefreshManager.java create mode 100644 src/com/android/email/SingleRunningTask.java create mode 100644 src/com/android/email/Throttle.java rename {emailcommon/src/com/android/emailcommon => src/com/android/email}/VendorPolicyLoader.java (82%) create mode 100644 src/com/android/email/activity/AccountSelectorAdapter.java create mode 100644 src/com/android/email/activity/ActionBarController.java create mode 100644 src/com/android/email/activity/AddressTextView.java create mode 100644 src/com/android/email/activity/AttachmentInfoDialog.java create mode 100644 src/com/android/email/activity/BannerController.java create mode 100644 src/com/android/email/activity/ChipsAddressTextView.java create mode 100644 src/com/android/email/activity/DeleteMessageConfirmationDialog.java create mode 100644 src/com/android/email/activity/EmailActivity.java delete mode 100644 src/com/android/email/activity/EventViewer.java create mode 100644 src/com/android/email/activity/FragmentInstallable.java create mode 100644 src/com/android/email/activity/MailboxFinder.java create mode 100644 src/com/android/email/activity/MailboxFragmentAdapter.java create mode 100644 src/com/android/email/activity/MailboxListFragment.java create mode 100644 src/com/android/email/activity/MailboxListItem.java create mode 100644 src/com/android/email/activity/MailboxMoveToAdapter.java create mode 100644 src/com/android/email/activity/MessageCommandButtonView.java create mode 100644 src/com/android/email/activity/MessageCompose.java create mode 100644 src/com/android/email/activity/MessageFileView.java create mode 100644 src/com/android/email/activity/MessageFileViewFragment.java create mode 100644 src/com/android/email/activity/MessageList.java create mode 100644 src/com/android/email/activity/MessageListFragment.java create mode 100644 src/com/android/email/activity/MessageListItem.java create mode 100644 src/com/android/email/activity/MessageListItemCoordinates.java create mode 100644 src/com/android/email/activity/MessageOrderManager.java create mode 100644 src/com/android/email/activity/MessageViewFragment.java create mode 100644 src/com/android/email/activity/MessageViewFragmentBase.java create mode 100644 src/com/android/email/activity/MessagesAdapter.java create mode 100644 src/com/android/email/activity/MoveMessageToDialog.java create mode 100644 src/com/android/email/activity/NfcHandler.java create mode 100644 src/com/android/email/activity/RecentMailboxManager.java create mode 100644 src/com/android/email/activity/ShortcutPicker.java create mode 100644 src/com/android/email/activity/ShortcutPickerFragment.java create mode 100644 src/com/android/email/activity/ThreePaneLayout.java create mode 100644 src/com/android/email/activity/UIControllerBase.java create mode 100644 src/com/android/email/activity/UIControllerOnePane.java create mode 100644 src/com/android/email/activity/UIControllerSearchTwoPane.java create mode 100644 src/com/android/email/activity/UIControllerTwoPane.java create mode 100644 src/com/android/email/activity/Welcome.java create mode 100644 src/com/android/email/activity/setup/AccountSetupAccountType.java create mode 100644 src/com/android/email/activity/setup/AccountSetupExchange.java create mode 100644 src/com/android/email/activity/setup/AccountSetupExchangeFragment.java delete mode 100644 src/com/android/email/activity/setup/AccountSetupType.java delete mode 100644 src/com/android/email/activity/setup/EmailPreferenceFragment.java delete mode 100644 src/com/android/email/activity/setup/ForwardingIntent.java delete mode 100644 src/com/android/email/activity/setup/PolicyListPreference.java create mode 100644 src/com/android/email/data/ThrottlingCursorLoader.java create mode 100644 src/com/android/email/mail/Transport.java create mode 100644 src/com/android/email/mail/store/ExchangeStore.java rename {emailcommon/src/com/android/emailcommon/utility => src/com/android/email/mail/transport}/CountingOutputStream.java (96%) rename {emailcommon/src/com/android/emailcommon/utility => src/com/android/email/mail/transport}/EOLConvertingOutputStream.java (96%) delete mode 100644 src/com/android/email/provider/FolderPickerActivity.java delete mode 100644 src/com/android/email/provider/FolderPickerCallback.java delete mode 100644 src/com/android/email/provider/FolderPickerDialog.java delete mode 100644 src/com/android/email/provider/FolderPickerSelectorAdapter.java delete mode 100644 src/com/android/email/provider/Utilities.java delete mode 100644 src/com/android/email/service/EmailServiceStub.java delete mode 100644 src/com/android/email/service/ImapService.java delete mode 100644 src/com/android/email/service/ImapTempFileLiteral.java delete mode 100644 src/com/android/email/service/LegacyEmailAuthenticatorService.java delete mode 100644 src/com/android/email/service/LegacyImapSyncAdapterService.java delete mode 100644 src/com/android/email/service/Pop3AuthenticatorService.java delete mode 100644 src/com/android/email/service/Pop3Service.java delete mode 100644 src/com/android/email/service/Pop3SyncAdapterService.java rename src/com/android/email/service/{AuthenticatorService.java => PopImapAuthenticatorService.java} (72%) create mode 100644 src/com/android/email/view/NonLockingScrollView.java create mode 100644 src/com/android/email/view/RigidWebView.java create mode 100644 src/com/android/email/view/SizeBoundingFrameLayout.java create mode 100644 src/com/android/email/widget/EmailWidget.java create mode 100644 src/com/android/email/widget/EmailWidgetLoader.java create mode 100644 src/com/android/email/widget/WidgetConfiguration.java create mode 100644 src/com/android/email/widget/WidgetManager.java delete mode 100644 src/com/android/email2/ui/CreateShortcutActivityEmail.java delete mode 100644 src/com/android/email2/ui/MailboxSelectionActivityEmail.java delete mode 100644 src/com/android/mail/browse/EmailConversationProvider.java delete mode 100644 src/com/android/mail/providers/EmailAccountCacheProvider.java create mode 100644 src/com/android/mail/providers/UIProvider.java delete mode 100644 src/com/android/mail/providers/protos/boot/AccountReceiver.java delete mode 100644 src/com/android/mail/utils/LogTag.java create mode 100644 src/com/android/mail/utils/LogUtils.java create mode 100644 src/com/android/mail/utils/LoggingInputStream.java create mode 100644 src/com/beetstra/ThirdPartyProject.prop create mode 100644 src/org/apache/commons/io/CopyUtils.java create mode 100644 src/org/apache/commons/io/DirectoryWalker.java create mode 100644 src/org/apache/commons/io/EndianUtils.java create mode 100644 src/org/apache/commons/io/FileCleaner.java create mode 100644 src/org/apache/commons/io/FileCleaningTracker.java create mode 100644 src/org/apache/commons/io/FileDeleteStrategy.java create mode 100644 src/org/apache/commons/io/FileSystemUtils.java create mode 100644 src/org/apache/commons/io/FileUtils.java create mode 100644 src/org/apache/commons/io/FilenameUtils.java create mode 100644 src/org/apache/commons/io/HexDump.java create mode 100644 src/org/apache/commons/io/IOCase.java create mode 100644 src/org/apache/commons/io/IOExceptionWithCause.java create mode 100644 src/org/apache/commons/io/IOUtils.java create mode 100644 src/org/apache/commons/io/LineIterator.java create mode 100644 src/org/apache/commons/io/ThirdPartyProject.prop create mode 100644 src/org/apache/commons/io/comparator/DefaultFileComparator.java create mode 100644 src/org/apache/commons/io/comparator/ExtensionFileComparator.java create mode 100644 src/org/apache/commons/io/comparator/LastModifiedFileComparator.java create mode 100644 src/org/apache/commons/io/comparator/NameFileComparator.java create mode 100644 src/org/apache/commons/io/comparator/PathFileComparator.java create mode 100644 src/org/apache/commons/io/comparator/ReverseComparator.java create mode 100644 src/org/apache/commons/io/comparator/SizeFileComparator.java create mode 100644 src/org/apache/commons/io/comparator/package.html create mode 100644 src/org/apache/commons/io/filefilter/AbstractFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/AgeFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/AndFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/CanReadFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/CanWriteFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/ConditionalFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/DelegateFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/DirectoryFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/EmptyFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/FalseFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/FileFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/FileFilterUtils.java create mode 100644 src/org/apache/commons/io/filefilter/HiddenFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/IOFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/NameFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/NotFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/OrFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/PrefixFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/RegexFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/SizeFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/SuffixFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/TrueFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/WildcardFileFilter.java create mode 100644 src/org/apache/commons/io/filefilter/package.html create mode 100644 src/org/apache/commons/io/input/AutoCloseInputStream.java create mode 100644 src/org/apache/commons/io/input/CharSequenceReader.java create mode 100644 src/org/apache/commons/io/input/ClassLoaderObjectInputStream.java create mode 100644 src/org/apache/commons/io/input/CloseShieldInputStream.java create mode 100644 src/org/apache/commons/io/input/ClosedInputStream.java create mode 100644 src/org/apache/commons/io/input/CountingInputStream.java create mode 100644 src/org/apache/commons/io/input/DemuxInputStream.java create mode 100644 src/org/apache/commons/io/input/NullInputStream.java create mode 100644 src/org/apache/commons/io/input/NullReader.java create mode 100644 src/org/apache/commons/io/input/ProxyInputStream.java create mode 100644 src/org/apache/commons/io/input/ProxyReader.java create mode 100644 src/org/apache/commons/io/input/SwappedDataInputStream.java create mode 100644 src/org/apache/commons/io/input/TeeInputStream.java create mode 100644 src/org/apache/commons/io/input/package.html create mode 100644 src/org/apache/commons/io/output/ByteArrayOutputStream.java create mode 100644 src/org/apache/commons/io/output/CloseShieldOutputStream.java create mode 100644 src/org/apache/commons/io/output/ClosedOutputStream.java create mode 100644 src/org/apache/commons/io/output/CountingOutputStream.java create mode 100644 src/org/apache/commons/io/output/DeferredFileOutputStream.java create mode 100644 src/org/apache/commons/io/output/DemuxOutputStream.java create mode 100644 src/org/apache/commons/io/output/FileWriterWithEncoding.java create mode 100644 src/org/apache/commons/io/output/LockableFileWriter.java create mode 100644 src/org/apache/commons/io/output/NullOutputStream.java create mode 100644 src/org/apache/commons/io/output/NullWriter.java create mode 100644 src/org/apache/commons/io/output/ProxyOutputStream.java create mode 100644 src/org/apache/commons/io/output/ProxyWriter.java create mode 100644 src/org/apache/commons/io/output/TeeOutputStream.java create mode 100644 src/org/apache/commons/io/output/ThresholdingOutputStream.java create mode 100644 src/org/apache/commons/io/output/package.html create mode 100644 src/org/apache/commons/io/overview.html create mode 100644 src/org/apache/commons/io/package.html create mode 100644 src/org/apache/james/mime4j/AbstractContentHandler.java create mode 100644 src/org/apache/james/mime4j/BodyDescriptor.java create mode 100644 src/org/apache/james/mime4j/CloseShieldInputStream.java create mode 100644 src/org/apache/james/mime4j/ContentHandler.java create mode 100644 src/org/apache/james/mime4j/EOLConvertingInputStream.java create mode 100644 src/org/apache/james/mime4j/Log.java rename src/{com/android/email/service/LegacyEasAuthenticatorService.java => org/apache/james/mime4j/LogFactory.java} (67%) create mode 100644 src/org/apache/james/mime4j/MimeBoundaryInputStream.java create mode 100644 src/org/apache/james/mime4j/MimeStreamParser.java create mode 100644 src/org/apache/james/mime4j/RootInputStream.java create mode 100644 src/org/apache/james/mime4j/SimpleContentHandler.java create mode 100644 src/org/apache/james/mime4j/ThirdPartyProject.prop create mode 100644 src/org/apache/james/mime4j/codec/EncoderUtil.java create mode 100644 src/org/apache/james/mime4j/decoder/Base64InputStream.java create mode 100644 src/org/apache/james/mime4j/decoder/ByteQueue.java create mode 100644 src/org/apache/james/mime4j/decoder/DecoderUtil.java create mode 100644 src/org/apache/james/mime4j/decoder/QuotedPrintableInputStream.java create mode 100644 src/org/apache/james/mime4j/decoder/UnboundedFifoByteBuffer.java create mode 100644 src/org/apache/james/mime4j/field/AddressListField.java create mode 100644 src/org/apache/james/mime4j/field/ContentTransferEncodingField.java create mode 100644 src/org/apache/james/mime4j/field/ContentTypeField.java create mode 100644 src/org/apache/james/mime4j/field/DateTimeField.java create mode 100644 src/org/apache/james/mime4j/field/DefaultFieldParser.java create mode 100644 src/org/apache/james/mime4j/field/DelegatingFieldParser.java create mode 100644 src/org/apache/james/mime4j/field/Field.java create mode 100644 src/org/apache/james/mime4j/field/FieldParser.java create mode 100644 src/org/apache/james/mime4j/field/MailboxField.java create mode 100644 src/org/apache/james/mime4j/field/MailboxListField.java create mode 100644 src/org/apache/james/mime4j/field/UnstructuredField.java create mode 100644 src/org/apache/james/mime4j/field/address/Address.java create mode 100644 src/org/apache/james/mime4j/field/address/AddressList.java create mode 100644 src/org/apache/james/mime4j/field/address/Builder.java create mode 100644 src/org/apache/james/mime4j/field/address/DomainList.java create mode 100644 src/org/apache/james/mime4j/field/address/Group.java create mode 100644 src/org/apache/james/mime4j/field/address/Mailbox.java create mode 100644 src/org/apache/james/mime4j/field/address/MailboxList.java create mode 100644 src/org/apache/james/mime4j/field/address/NamedMailbox.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/ASTaddr_spec.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/ASTaddress.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/ASTaddress_list.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/ASTangle_addr.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/ASTdomain.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/ASTgroup_body.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/ASTlocal_part.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/ASTmailbox.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/ASTname_addr.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/ASTphrase.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/ASTroute.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/AddressListParser.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/AddressListParser.jj create mode 100644 src/org/apache/james/mime4j/field/address/parser/AddressListParserConstants.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/AddressListParserTokenManager.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/AddressListParserTreeConstants.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/AddressListParserVisitor.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/BaseNode.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/JJTAddressListParserState.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/Node.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/ParseException.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/SimpleCharStream.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/SimpleNode.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/Token.java create mode 100644 src/org/apache/james/mime4j/field/address/parser/TokenMgrError.java create mode 100644 src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParser.java create mode 100644 src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserConstants.java create mode 100644 src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserTokenManager.java create mode 100644 src/org/apache/james/mime4j/field/contenttype/parser/ParseException.java create mode 100644 src/org/apache/james/mime4j/field/contenttype/parser/SimpleCharStream.java create mode 100644 src/org/apache/james/mime4j/field/contenttype/parser/Token.java create mode 100644 src/org/apache/james/mime4j/field/contenttype/parser/TokenMgrError.java create mode 100644 src/org/apache/james/mime4j/field/datetime/DateTime.java create mode 100644 src/org/apache/james/mime4j/field/datetime/parser/DateTimeParser.java create mode 100644 src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserConstants.java create mode 100644 src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserTokenManager.java create mode 100644 src/org/apache/james/mime4j/field/datetime/parser/ParseException.java create mode 100644 src/org/apache/james/mime4j/field/datetime/parser/SimpleCharStream.java create mode 100644 src/org/apache/james/mime4j/field/datetime/parser/Token.java create mode 100644 src/org/apache/james/mime4j/field/datetime/parser/TokenMgrError.java create mode 100644 src/org/apache/james/mime4j/message/AbstractBody.java create mode 100644 src/org/apache/james/mime4j/message/BinaryBody.java create mode 100644 src/org/apache/james/mime4j/message/Body.java create mode 100644 src/org/apache/james/mime4j/message/BodyPart.java create mode 100644 src/org/apache/james/mime4j/message/Entity.java create mode 100644 src/org/apache/james/mime4j/message/Header.java create mode 100644 src/org/apache/james/mime4j/message/MemoryBinaryBody.java create mode 100644 src/org/apache/james/mime4j/message/MemoryTextBody.java create mode 100644 src/org/apache/james/mime4j/message/Message.java create mode 100644 src/org/apache/james/mime4j/message/Multipart.java create mode 100644 src/org/apache/james/mime4j/message/TempFileBinaryBody.java create mode 100644 src/org/apache/james/mime4j/message/TempFileTextBody.java create mode 100644 src/org/apache/james/mime4j/message/TextBody.java create mode 100644 src/org/apache/james/mime4j/util/CharsetUtil.java create mode 100644 src/org/apache/james/mime4j/util/PartialInputStream.java create mode 100644 src/org/apache/james/mime4j/util/PositionInputStream.java create mode 100644 src/org/apache/james/mime4j/util/SimpleTempStorage.java create mode 100644 src/org/apache/james/mime4j/util/TempFile.java create mode 100644 src/org/apache/james/mime4j/util/TempPath.java create mode 100644 src/org/apache/james/mime4j/util/TempStorage.java rename tests/{oldAndroid.mk => Android.mk} (100%) delete mode 100644 tests/src/com/android/email/SecurityPolicyTests.java create mode 100644 tests/src/com/android/email/activity/setup/AccountSetupAccountTypeTests.java delete mode 100644 tests/src/com/android/email/provider/PolicyTests.java delete mode 100644 tests/src/com/android/email/provider/ProviderTests.java diff --git a/Android.mk b/Android.mk index b2dad7af9..8b1df5b53 100644 --- a/Android.mk +++ b/Android.mk @@ -14,42 +14,38 @@ LOCAL_PATH := $(call my-dir) -# Build the Email application itself, along with its tests and tests for the emailcommon +# Build the Email application itself, along with its tests and the tests for the emailcommon # static library. All tests can be run via runtest email include $(CLEAR_VARS) - -# Include res dir from chips, unified, and photoviewer +# Include res dir from chips chips_dir := ../../../frameworks/ex/chips/res -unified_email_dir := ../UnifiedEmail -photo_dir := ../../../frameworks/ex/photoviewer/res -res_dir := $(chips_dir) res $(unified_email_dir)/res $(photo_dir) build/res +mail_common_dir := ../../../frameworks/opt/mailcommon/res +res_dir := $(chips_dir) $(mail_common_dir) res LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := $(call all-java-files-under, $(unified_email_dir)/src) -LOCAL_SRC_FILES += $(call all-java-files-under, src/com/android) +LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/email) LOCAL_SRC_FILES += $(call all-java-files-under, src/com/beetstra) - LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dir)) - -# Use assets dir from UnifiedEmail -# (the default package target doesn't seem to deal with multiple asset dirs) -LOCAL_ASSET_DIR := $(LOCAL_PATH)/$(unified_email_dir)/assets - LOCAL_AAPT_FLAGS := --auto-add-overlay -LOCAL_AAPT_FLAGS += --extra-packages com.android.ex.chips:com.android.mail:com.android.email:com.android.ex.photo +LOCAL_AAPT_FLAGS += --extra-packages com.android.ex.chips -LOCAL_STATIC_JAVA_LIBRARIES := android-common com.android.emailcommon2 com.android.emailsync guava android-common-chips android-common-photoviewer -LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4 -LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13 +LOCAL_STATIC_JAVA_LIBRARIES := android-common com.android.emailcommon guava android-common-chips -LOCAL_PACKAGE_NAME := Email2 -LOCAL_OVERRIDES_PACKAGES := Email +LOCAL_PACKAGE_NAME := Email -LOCAL_PROGUARD_FLAG_FILES := proguard.flags $(unified_email_dir)/proguard.flags +LOCAL_PROGUARD_FLAG_FILES := proguard.flags -LOCAL_SDK_VERSION := current # TODO change this to "17" once the MR1 sdk version is set to 17 +LOCAL_SDK_VERSION := 16 + +# The Emma tool analyzes code coverage when running unit tests on the +# application. This configuration line selects which packages will be analyzed, +# leaving out code which is tested by other means (e.g. static libraries) that +# would dilute the coverage results. These options do not affect regular +# production builds. +LOCAL_EMMA_COVERAGE_FILTER := +com.android.emailcommon.*,+com.android.email.*, \ + +org.apache.james.mime4j.*,+com.beetstra.jutf7.*,+org.apache.commons.io.* include $(BUILD_PACKAGE) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 552ea7bee..d048b8ca8 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,6 +1,5 @@ - - - - - - - - - - - - - - - - - - - - - + - + - + + + + + + + + + + + + + + + + + + + + + + + - + - - + android:name="Email" + android:theme="@style/EmailTheme" + android:hardwareAccelerated="false" + > + android:name=".activity.Welcome" + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -113,189 +266,15 @@ - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -374,16 +291,14 @@ android:name=".service.EmailBroadcastReceiver" android:enabled="true"> - - - - - - - - - + + + + @@ -415,6 +330,12 @@ + + + @@ -427,7 +348,7 @@ @@ -437,46 +358,20 @@ - - - - - - - - - - - - - - + android:resource="@xml/syncadapter_pop_imap" /> @@ -502,28 +397,6 @@ - - - - - - - - - - - - @@ -572,49 +445,12 @@ android:resource="@xml/authenticator_alternate" /> - - - - - - - - - - - - - - - - @@ -624,45 +460,41 @@ android:name=".provider.EmailProvider" android:authorities="com.android.email.provider;com.android.email.notifier" android:multiprocess="true" - android:exported="true" android:permission="com.android.email.permission.ACCESS_PROVIDER" android:label="@string/app_name" /> - - - + + + + + + + + + android:name="android.appwidget.action.APPWIDGET_UPDATE" /> - - - - - + - - + android:name="android.appwidget.provider" + android:resource="@xml/widget_info" /> + - - - diff --git a/CleanSpec.mk b/CleanSpec.mk index a546bfddf..b5064f0a3 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -43,8 +43,37 @@ #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) +$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/APPS/Email*) +$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/APPS/Email*) +$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/APPS/Exchange*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/JAVA_LIBRARIES/com.android.emailcommon*) +$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes/com/android/emailcommon*) +$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon*) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/EmailGoogle_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/EmailGoogle_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/EmailGoogle_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email2_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/EmailGoogle_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email2_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon*) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/EmailGoogle_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email2_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon*) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/EmailGoogle_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email2_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon*) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email*) +$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon*) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email*) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.email*) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email*) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.email*) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email*) +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ diff --git a/build/res/values/strings.xml b/build/res/values/strings.xml deleted file mode 100644 index f0b3eed92..000000000 --- a/build/res/values/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - com.android.exchange - com.android.email - com.android.email - com.android.email - com.android.email.EXCHANGE_INTENT - com.android.email.ACCOUNT_MANAGER_ENTRY_INTENT - com.android.email.provider - imap - imap - pop3 - eas - application/email-ls - diff --git a/build/res/xml/services.xml b/build/res/xml/services.xml deleted file mode 100644 index a1780e585..000000000 --- a/build/res/xml/services.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - diff --git a/emailcommon/Android.mk b/emailcommon/Android.mk index b4dddf02f..cd2acc8db 100644 --- a/emailcommon/Android.mk +++ b/emailcommon/Android.mk @@ -20,24 +20,24 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -unified_email_src_dir := ../../UnifiedEmail/src -apache_src_dir := ../../UnifiedEmail/src/org +unified_email_src_dir := ../src +apache_src_dir := ../src/org imported_unified_email_files := \ $(unified_email_src_dir)/com/android/mail/utils/LogUtils.java \ + $(unified_email_src_dir)/com/android/mail/utils/LoggingInputStream.java \ $(unified_email_src_dir)/com/android/mail/providers/UIProvider.java -LOCAL_MODULE := com.android.emailcommon2 +LOCAL_MODULE := com.android.emailcommon LOCAL_STATIC_JAVA_LIBRARIES := guava android-common -LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/emailcommon) -LOCAL_SRC_FILES += \ - src/com/android/emailcommon/service/IEmailService.aidl \ - src/com/android/emailcommon/service/IEmailServiceCallback.aidl \ - src/com/android/emailcommon/service/IPolicyService.aidl \ - src/com/android/emailcommon/service/IAccountService.aidl +LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SRC_FILES += $(call all-java-files-under, $(apache_src_dir)) +LOCAL_SRC_FILES += src/com/android/emailcommon/service/IEmailService.aidl +LOCAL_SRC_FILES += src/com/android/emailcommon/service/IAccountService.aidl +LOCAL_SRC_FILES += src/com/android/emailcommon/service/IPolicyService.aidl +LOCAL_SRC_FILES += src/com/android/emailcommon/service/IEmailServiceCallback.aidl LOCAL_SRC_FILES += $(imported_unified_email_files) -LOCAL_SDK_VERSION := 14 +LOCAL_SDK_VERSION := current include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/src/com/android/email/service/LegacyImapAuthenticatorService.java b/emailcommon/src/com/android/emailcommon/AccountManagerTypes.java similarity index 68% rename from src/com/android/email/service/LegacyImapAuthenticatorService.java rename to emailcommon/src/com/android/emailcommon/AccountManagerTypes.java index 8480d1e8d..4ccd480a5 100644 --- a/src/com/android/email/service/LegacyImapAuthenticatorService.java +++ b/emailcommon/src/com/android/emailcommon/AccountManagerTypes.java @@ -1,5 +1,6 @@ /* - * Copyright (C) 2010 The Android Open Source Project + /* + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +15,9 @@ * limitations under the License. */ -package com.android.email.service; +package com.android.emailcommon; -/** - * This service needs to be declared separately from the base service - */ -public class LegacyImapAuthenticatorService extends AuthenticatorService { +public class AccountManagerTypes { + public static final String TYPE_EXCHANGE = "com.android.exchange"; + public static final String TYPE_POP_IMAP = "com.android.email"; } diff --git a/emailcommon/src/com/android/emailcommon/Api.java b/emailcommon/src/com/android/emailcommon/Api.java index e20452ebe..2b1e89b03 100644 --- a/emailcommon/src/com/android/emailcommon/Api.java +++ b/emailcommon/src/com/android/emailcommon/Api.java @@ -21,9 +21,8 @@ package com.android.emailcommon; * * Level 1: As shipped in HC/MR1 * Level 2: Adds searchMessages to EmailService - * Level 3: Adds capabilities query * */ public class Api { - public static final int LEVEL = 3; + public static final int LEVEL = 2; } diff --git a/src/com/android/email/service/ImapAuthenticatorService.java b/emailcommon/src/com/android/emailcommon/CalendarProviderStub.java similarity index 63% rename from src/com/android/email/service/ImapAuthenticatorService.java rename to emailcommon/src/com/android/emailcommon/CalendarProviderStub.java index 975583da3..eac371ada 100644 --- a/src/com/android/email/service/ImapAuthenticatorService.java +++ b/emailcommon/src/com/android/emailcommon/CalendarProviderStub.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,12 @@ * limitations under the License. */ -package com.android.email.service; +package com.android.emailcommon; /** - * This service needs to be declared separately from the base service + * This is the only non-SDK reference in the com.android.email project, referencing the once and + * future CalendarProvider authority name. */ -public class ImapAuthenticatorService extends AuthenticatorService { +public class CalendarProviderStub { + public static final String AUTHORITY = "com.android.calendar"; } diff --git a/emailcommon/src/com/android/emailcommon/TrafficFlags.java b/emailcommon/src/com/android/emailcommon/TrafficFlags.java index 6b858c4c2..c8c4e034c 100644 --- a/emailcommon/src/com/android/emailcommon/TrafficFlags.java +++ b/emailcommon/src/com/android/emailcommon/TrafficFlags.java @@ -19,17 +19,29 @@ package com.android.emailcommon; import android.content.Context; import com.android.emailcommon.provider.Account; +import com.android.emailcommon.provider.HostAuth; +import com.android.emailcommon.utility.Utility; /** * Constants for tagging threads for traffic stats, and associated utilities * * Example usage: - * TrafficStats.setThreadStatsTag(accountId | DATA_EMAIL | REASON_SYNC); + * TrafficStats.setThreadStatsTag(accountId | PROTOCOL_IMAP | DATA_EMAIL | REASON_SYNC); */ public class TrafficFlags { // Bits 0->15, account id private static final int ACCOUNT_MASK = 0x0000FFFF; + // Bits 16&17, protocol (0 = POP3) + private static final int PROTOCOL_SHIFT = 16; + private static final int PROTOCOL_MASK = 3 << PROTOCOL_SHIFT; + public static final int PROTOCOL_POP3 = 0 << PROTOCOL_SHIFT; + public static final int PROTOCOL_IMAP = 1 << PROTOCOL_SHIFT; + public static final int PROTOCOL_EAS = 2 << PROTOCOL_SHIFT; + public static final int PROTOCOL_SMTP = 3 << PROTOCOL_SHIFT; + private static final String[] PROTOCOLS = new String[] {HostAuth.SCHEME_POP3, + HostAuth.SCHEME_IMAP, HostAuth.SCHEME_EAS, HostAuth.SCHEME_SMTP}; + // Bits 18&19, type (0 = EMAIL) private static final int DATA_SHIFT = 18; private static final int DATA_MASK = 3 << DATA_SHIFT; @@ -56,7 +68,8 @@ public class TrafficFlags { * @return flags for syncing this account */ public static int getSyncFlags(Context context, Account account) { - return (int)account.mId | REASON_SYNC; + int protocolIndex = Utility.arrayIndex(PROTOCOLS, account.getProtocol(context)); + return (int)account.mId | REASON_SYNC | (protocolIndex << PROTOCOL_SHIFT); } /** @@ -67,7 +80,8 @@ public class TrafficFlags { * @return flags for loading an attachment in this account */ public static int getAttachmentFlags(Context context, Account account) { - return (int)account.mId | REASON_ATTACHMENT_USER; + int protocolIndex = Utility.arrayIndex(PROTOCOLS, account.getProtocol(context)); + return (int)account.mId | REASON_ATTACHMENT_USER | (protocolIndex << PROTOCOL_SHIFT); } /** @@ -78,7 +92,7 @@ public class TrafficFlags { * @return flags for sending SMTP email from this account */ public static int getSmtpFlags(Context context, Account account) { - return (int)account.mId | REASON_SYNC; + return (int)account.mId | REASON_SYNC | PROTOCOL_SMTP; } public static String toString(int flags) { @@ -87,6 +101,8 @@ public class TrafficFlags { sb.append(flags & ACCOUNT_MASK); sb.append(','); sb.append(REASONS[(flags & REASON_MASK) >> REASON_SHIFT]); + sb.append(','); + sb.append(PROTOCOLS[(flags & PROTOCOL_MASK) >> PROTOCOL_SHIFT]); int maskedData = flags & DATA_MASK; if (maskedData != 0) { sb.append(','); diff --git a/emailcommon/src/com/android/emailcommon/internet/MimeMessage.java b/emailcommon/src/com/android/emailcommon/internet/MimeMessage.java index b3ee70ee4..412092da4 100644 --- a/emailcommon/src/com/android/emailcommon/internet/MimeMessage.java +++ b/emailcommon/src/com/android/emailcommon/internet/MimeMessage.java @@ -67,7 +67,6 @@ public class MimeMessage extends Message { private Body mBody; protected int mSize; private boolean mInhibitLocalMessageId = false; - private boolean mComplete = true; // Shared random source for generating local message-id values private static final java.util.Random sRandom = new java.util.Random(); @@ -120,7 +119,7 @@ public class MimeMessage extends Message { parse(in); } - private MimeStreamParser init() { + protected void parse(InputStream in) throws IOException, MessagingException { // Before parsing the input stream, clear all local fields that may be superceded by // the new incoming message. getMimeHeaders().clear(); @@ -135,20 +134,7 @@ public class MimeMessage extends Message { MimeStreamParser parser = new MimeStreamParser(); parser.setContentHandler(new MimeMessageBuilder()); - return parser; - } - - protected void parse(InputStream in) throws IOException, MessagingException { - MimeStreamParser parser = init(); parser.parse(new EOLConvertingInputStream(in)); - mComplete = !parser.getPrematureEof(); - } - - public void parse(InputStream in, EOLConvertingInputStream.Callback callback) - throws IOException, MessagingException { - MimeStreamParser parser = init(); - parser.parse(new EOLConvertingInputStream(in, getSize(), callback)); - mComplete = !parser.getPrematureEof(); } /** @@ -216,10 +202,6 @@ public class MimeMessage extends Message { } } - public boolean isComplete() { - return mComplete; - } - public String getMimeType() throws MessagingException { return MimeUtility.getHeaderParameter(getContentType(), null); } diff --git a/emailcommon/src/com/android/emailcommon/internet/MimeUtility.java b/emailcommon/src/com/android/emailcommon/internet/MimeUtility.java index b96e01e59..0c4ffa124 100644 --- a/emailcommon/src/com/android/emailcommon/internet/MimeUtility.java +++ b/emailcommon/src/com/android/emailcommon/internet/MimeUtility.java @@ -16,7 +16,6 @@ package com.android.emailcommon.internet; -import android.text.TextUtils; import android.util.Base64; import android.util.Base64DataException; import android.util.Base64InputStream; @@ -407,12 +406,33 @@ public class MimeUtility { public static void collectParts(Part part, ArrayList viewables, ArrayList attachments) throws MessagingException { String disposition = part.getDisposition(); - String dispositionType = MimeUtility.getHeaderParameter(disposition, null); + String dispositionType = null; + String dispositionFilename = null; + if (disposition != null) { + dispositionType = MimeUtility.getHeaderParameter(disposition, null); + dispositionFilename = MimeUtility.getHeaderParameter(disposition, "filename"); + } + // An attachment filename can be defined in either the Content-Disposition header + // or the Content-Type header. Content-Disposition is preferred, so we only try + // the Content-Type header as a last resort. + if (dispositionFilename == null) { + String contentType = part.getContentType(); + dispositionFilename = MimeUtility.getHeaderParameter(contentType, "name"); + } + boolean attachmentDisposition = "attachment".equalsIgnoreCase(dispositionType); // If a disposition is not specified, default to "inline" - boolean inline = - TextUtils.isEmpty(dispositionType) || "inline".equalsIgnoreCase(dispositionType); - // The lower-case mime type - String mimeType = part.getMimeType().toLowerCase(); + boolean inlineDisposition = dispositionType == null + || "inline".equalsIgnoreCase(dispositionType); + + // A guess that this part is intended to be an attachment + boolean attachment = attachmentDisposition + || (dispositionFilename != null && !inlineDisposition); + + // A guess that this part is intended to be an inline. + boolean inline = inlineDisposition && (dispositionFilename != null); + + // One or the other + boolean attachmentOrInline = attachment || inline; if (part.getBody() instanceof Multipart) { // If the part is Multipart but not alternative it's either mixed or @@ -442,11 +462,14 @@ public class MimeUtility { // it, pulling any viewables or attachments into the running list. Message message = (Message)part.getBody(); collectParts(message, viewables, attachments); - } else if (inline && (mimeType.startsWith("text") || (mimeType.startsWith("image")))) { - // We'll treat text and images as viewables + } else if ((!attachmentOrInline) && ("text/html".equalsIgnoreCase(part.getMimeType()))) { + // If the part is HTML and we got this far, it's a viewable part of a mixed viewables.add(part); - } else { - // Everything else is an attachment. + } else if ((!attachmentOrInline) && ("text/plain".equalsIgnoreCase(part.getMimeType()))) { + // If the part is text and we got this far, it's a viewable part of a mixed + viewables.add(part); + } else if (attachmentOrInline) { + // Finally, if it's an attachment or an inline we will include it as an attachment. attachments.add(part); } } diff --git a/emailcommon/src/com/android/emailcommon/internet/Rfc822Output.java b/emailcommon/src/com/android/emailcommon/internet/Rfc822Output.java index 6d8b21abd..51a62fd1b 100644 --- a/emailcommon/src/com/android/emailcommon/internet/Rfc822Output.java +++ b/emailcommon/src/com/android/emailcommon/internet/Rfc822Output.java @@ -16,8 +16,12 @@ package com.android.emailcommon.internet; +import android.content.ContentUris; import android.content.Context; +import android.database.Cursor; import android.net.Uri; +import android.text.Html; +import android.text.TextUtils; import android.util.Base64; import android.util.Base64OutputStream; @@ -38,9 +42,7 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.text.SimpleDateFormat; -import java.util.Arrays; import java.util.Date; -import java.util.List; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -50,17 +52,28 @@ import java.util.regex.Pattern; */ public class Rfc822Output { + private static final Pattern PATTERN_START_OF_LINE = Pattern.compile("(?m)^"); + private static final Pattern PATTERN_ENDLINE_CRLF = Pattern.compile("\r\n"); + // In MIME, en_US-like date format should be used. In other words "MMM" should be encoded to // "Jan", not the other localized format like "Ene" (meaning January in locale es). private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); + private static final String WHERE_NOT_SMART_FORWARD = "(" + Attachment.FLAGS + "&" + + Attachment.FLAG_SMART_FORWARD + ")=0"; + /** A less-than-perfect pattern to pull out content */ private static final Pattern BODY_PATTERN = Pattern.compile( "(?:<\\s*body[^>]*>)(.*)(?:<\\s*/\\s*body\\s*>)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); /** Match group in {@code BODDY_PATTERN} for the body HTML */ private static final int BODY_PATTERN_GROUP = 1; + /** Pattern to find both dos and unix newlines */ + private static final Pattern NEWLINE_PATTERN = + Pattern.compile("\\r?\\n"); + /** HTML string to use when replacing text newlines */ + private static final String NEWLINE_HTML = "
"; /** Index of the plain text version of the message body */ private final static int INDEX_BODY_TEXT = 0; /** Index of the HTML version of the message body */ @@ -82,20 +95,78 @@ public class Rfc822Output { } } + /** + * Returns an HTML encoded message alternate + */ + /*package*/ static String getHtmlAlternate(Body body, boolean useSmartReply) { + if (body.mHtmlReply == null) { + return null; + } + StringBuffer altMessage = new StringBuffer(); + String htmlContent = TextUtils.htmlEncode(body.mTextContent); // Escape HTML reserved chars + htmlContent = NEWLINE_PATTERN.matcher(htmlContent).replaceAll(NEWLINE_HTML); + altMessage.append(htmlContent); + if (body.mIntroText != null) { + String htmlIntro = TextUtils.htmlEncode(body.mIntroText); + htmlIntro = NEWLINE_PATTERN.matcher(htmlIntro).replaceAll(NEWLINE_HTML); + altMessage.append(htmlIntro); + } + if (!useSmartReply) { + String htmlBody = getHtmlBody(body.mHtmlReply); + altMessage.append(htmlBody); + } + return altMessage.toString(); + } + /** * Gets both the plain text and HTML versions of the message body. */ /*package*/ static String[] buildBodyText(Body body, int flags, boolean useSmartReply) { + String[] messageBody = new String[] { null, null }; if (body == null) { - return new String[2]; + return messageBody; } - String[] messageBody = new String[] { body.mTextContent, body.mHtmlContent }; - if (useSmartReply && body.mQuotedTextStartPos > 0) { - if (messageBody[0] != null) { - messageBody[0] = messageBody[0].substring(0, body.mQuotedTextStartPos); - } else if (messageBody[1] != null) { - messageBody[1] = messageBody[1].substring(0, body.mQuotedTextStartPos); + String text = body.mTextContent; + boolean isReply = (flags & Message.FLAG_TYPE_REPLY) != 0; + boolean isForward = (flags & Message.FLAG_TYPE_FORWARD) != 0; + // For all forwards/replies, we add the intro text + if (isReply || isForward) { + String intro = body.mIntroText == null ? "" : body.mIntroText; + text += intro; + } + if (useSmartReply) { + // useSmartReply is set to true for use by SmartReply/SmartForward in EAS. + // SmartForward doesn't put a break between the original and new text, so we add an LF + if (isForward) { + text += "\n"; } + } else { + String quotedText = body.mTextReply; + // If there is no plain-text body, use de-tagified HTML as the text body + if (quotedText == null && body.mHtmlReply != null) { + quotedText = Html.fromHtml(body.mHtmlReply).toString(); + } + if (quotedText != null) { + // fix CR-LF line endings to LF-only needed by EditText. + Matcher matcher = PATTERN_ENDLINE_CRLF.matcher(quotedText); + quotedText = matcher.replaceAll("\n"); + } + if (isReply) { + if (quotedText != null) { + Matcher matcher = PATTERN_START_OF_LINE.matcher(quotedText); + text += matcher.replaceAll(">"); + } + } else if (isForward) { + if (quotedText != null) { + text += quotedText; + } + } + } + messageBody[INDEX_BODY_TEXT] = text; + // Exchange 2003 doesn't seem to support multipart w/SmartReply and SmartForward, so + // we'll skip this. Really, it would only matter if we could compose HTML replies + if (!useSmartReply) { + messageBody[INDEX_BODY_HTML] = getHtmlAlternate(body, useSmartReply); } return messageBody; } @@ -108,16 +179,9 @@ public class Rfc822Output { * @param messageId the message to write out * @param out the output stream to write the message to * @param useSmartReply whether or not quoted text is appended to a reply/forward - * @param a list of attachments to send (or null if retrieved from the message itself) */ public static void writeTo(Context context, long messageId, OutputStream out, boolean useSmartReply, boolean sendBcc) throws IOException, MessagingException { - writeTo(context, messageId, out, useSmartReply, sendBcc, null); - } - - public static void writeTo(Context context, long messageId, OutputStream out, - boolean useSmartReply, boolean sendBcc, List attachments) - throws IOException, MessagingException { Message message = Message.restoreMessageWithId(context, messageId); if (message == null) { // throw something? @@ -152,53 +216,59 @@ public class Rfc822Output { Body body = Body.restoreBodyWithMessageId(context, message.mId); String[] bodyText = buildBodyText(body, message.mFlags, useSmartReply); - // If a list of attachments hasn't been passed in, build one from the message - if (attachments == null) { - attachments = - Arrays.asList(Attachment.restoreAttachmentsWithMessageId(context, messageId)); - } + Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, messageId); + Cursor attachmentsCursor = context.getContentResolver().query(uri, + Attachment.CONTENT_PROJECTION, WHERE_NOT_SMART_FORWARD, null, null); - boolean multipart = attachments.size() > 0; - String multipartBoundary = null; - String multipartType = "mixed"; + try { + int attachmentCount = attachmentsCursor.getCount(); + boolean multipart = attachmentCount > 0; + String multipartBoundary = null; + String multipartType = "mixed"; - // Simplified case for no multipart - just emit text and be done. - if (!multipart) { - writeTextWithHeaders(writer, stream, bodyText); - } else { - // continue with multipart headers, then into multipart body - multipartBoundary = getNextBoundary(); - - // Move to the first attachment; this must succeed because multipart is true - if (attachments.size() == 1) { - // If we've got one attachment and it's an ics "attachment", we want to send - // this as multipart/alternative instead of multipart/mixed - int flags = attachments.get(0).mFlags; - if ((flags & Attachment.FLAG_ICS_ALTERNATIVE_PART) != 0) { - multipartType = "alternative"; - } - } - - writeHeader(writer, "Content-Type", - "multipart/" + multipartType + "; boundary=\"" + multipartBoundary + "\""); - // Finish headers and prepare for body section(s) - writer.write("\r\n"); - - // first multipart element is the body - if (bodyText[INDEX_BODY_TEXT] != null || bodyText[INDEX_BODY_HTML] != null) { - writeBoundary(writer, multipartBoundary, false); + // Simplified case for no multipart - just emit text and be done. + if (!multipart) { writeTextWithHeaders(writer, stream, bodyText); - } + } else { + // continue with multipart headers, then into multipart body + multipartBoundary = getNextBoundary(); - // Write out the attachments until we run out - for (Attachment att: attachments) { - writeBoundary(writer, multipartBoundary, false); - writeOneAttachment(context, writer, stream, att); + // Move to the first attachment; this must succeed because multipart is true + attachmentsCursor.moveToFirst(); + if (attachmentCount == 1) { + // If we've got one attachment and it's an ics "attachment", we want to send + // this as multipart/alternative instead of multipart/mixed + int flags = attachmentsCursor.getInt(Attachment.CONTENT_FLAGS_COLUMN); + if ((flags & Attachment.FLAG_ICS_ALTERNATIVE_PART) != 0) { + multipartType = "alternative"; + } + } + + writeHeader(writer, "Content-Type", + "multipart/" + multipartType + "; boundary=\"" + multipartBoundary + "\""); + // Finish headers and prepare for body section(s) writer.write("\r\n"); - } - // end of multipart section - writeBoundary(writer, multipartBoundary, true); + // first multipart element is the body + if (bodyText[INDEX_BODY_TEXT] != null) { + writeBoundary(writer, multipartBoundary, false); + writeTextWithHeaders(writer, stream, bodyText); + } + + // Write out the attachments until we run out + do { + writeBoundary(writer, multipartBoundary, false); + Attachment attachment = + Attachment.getContent(attachmentsCursor, Attachment.class); + writeOneAttachment(context, writer, stream, attachment); + writer.write("\r\n"); + } while (attachmentsCursor.moveToNext()); + + // end of multipart section + writeBoundary(writer, multipartBoundary, true); + } + } finally { + attachmentsCursor.close(); } writer.flush(); @@ -234,7 +304,7 @@ public class Rfc822Output { inStream = new ByteArrayInputStream(attachment.mContentBytes); } else { // try to open the file - Uri fileUri = Uri.parse(attachment.getContentUri()); + Uri fileUri = Uri.parse(attachment.mContentUri); inStream = context.getContentResolver().openInputStream(fileUri); } // switch to output stream for base64 text output @@ -328,7 +398,9 @@ public class Rfc822Output { } /** - * Write the body text. + * Write the body text. If only one version of the body is specified (either plain text + * or HTML), the text is written directly. Otherwise, the plain text and HTML bodies + * are both written with the appropriate headers. * * Note this always uses base64, even when not required. Slightly less efficient for * US-ASCII text, but handles all formats even when non-ascii chars are involved. A small @@ -340,23 +412,49 @@ public class Rfc822Output { */ private static void writeTextWithHeaders(Writer writer, OutputStream out, String[] bodyText) throws IOException { - boolean html = false; String text = bodyText[INDEX_BODY_TEXT]; - if (text == null) { - text = bodyText[INDEX_BODY_HTML]; - html = true; - } + String html = bodyText[INDEX_BODY_HTML]; + if (text == null) { writer.write("\r\n"); // a truly empty message } else { + String multipartBoundary = null; + boolean multipart = html != null; + + // Simplified case for no multipart - just emit text and be done. + if (multipart) { + // continue with multipart headers, then into multipart body + multipartBoundary = getNextBoundary(); + + writeHeader(writer, "Content-Type", + "multipart/alternative; boundary=\"" + multipartBoundary + "\""); + // Finish headers and prepare for body section(s) + writer.write("\r\n"); + writeBoundary(writer, multipartBoundary, false); + } + // first multipart element is the body - String mimeType = "text/" + (html ? "html" : "plain"); - writeHeader(writer, "Content-Type", mimeType + "; charset=utf-8"); + writeHeader(writer, "Content-Type", "text/plain; charset=utf-8"); writeHeader(writer, "Content-Transfer-Encoding", "base64"); writer.write("\r\n"); byte[] textBytes = text.getBytes("UTF-8"); writer.flush(); out.write(Base64.encode(textBytes, Base64.CRLF)); + + if (multipart) { + // next multipart section + writeBoundary(writer, multipartBoundary, false); + + writeHeader(writer, "Content-Type", "text/html; charset=utf-8"); + writeHeader(writer, "Content-Transfer-Encoding", "base64"); + writer.write("\r\n"); + byte[] htmlBytes = html.getBytes("UTF-8"); + writer.flush(); + out.write(Base64.encode(htmlBytes, Base64.CRLF)); + + // end of multipart section + writeBoundary(writer, multipartBoundary, true); + } } } diff --git a/emailcommon/src/com/android/emailcommon/provider/Account.aidl b/emailcommon/src/com/android/emailcommon/provider/Account.aidl deleted file mode 100644 index 06bac3447..000000000 --- a/emailcommon/src/com/android/emailcommon/provider/Account.aidl +++ /dev/null @@ -1,18 +0,0 @@ -/* Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.emailcommon.provider; - -parcelable Account; \ No newline at end of file diff --git a/emailcommon/src/com/android/emailcommon/provider/Account.java b/emailcommon/src/com/android/emailcommon/provider/Account.java index 5e6d9fc2b..97a6f120b 100755 --- a/emailcommon/src/com/android/emailcommon/provider/Account.java +++ b/emailcommon/src/com/android/emailcommon/provider/Account.java @@ -40,6 +40,16 @@ import java.util.UUID; public final class Account extends EmailContent implements AccountColumns, Parcelable { public static final String TABLE_NAME = "Account"; + @SuppressWarnings("hiding") + public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/account"); + public static final Uri ADD_TO_FIELD_URI = + Uri.parse(EmailContent.CONTENT_URI + "/accountIdAddToField"); + public static final Uri RESET_NEW_MESSAGE_COUNT_URI = + Uri.parse(EmailContent.CONTENT_URI + "/resetNewMessageCount"); + public static final Uri NOTIFIER_URI = + Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account"); + public static final Uri DEFAULT_ACCOUNT_ID_URI = + Uri.parse(EmailContent.CONTENT_URI + "/account/default"); // Define all pseudo account IDs here to avoid conflict with one another. /** @@ -90,8 +100,6 @@ public final class Account extends EmailContent implements AccountColumns, Parce // Whether or not server-side search supports global search (i.e. all mailboxes); only valid // if FLAGS_SUPPORTS_SEARCH is true public static final int FLAGS_SUPPORTS_GLOBAL_SEARCH = 1<<12; - // Whether or not the initial folder list has been loaded - public static final int FLAGS_INITIAL_FOLDER_LIST_LOADED = 1<<13; // Deletion policy (see FLAGS_DELETE_POLICY_MASK, above) public static final int DELETE_POLICY_NEVER = 0; @@ -102,20 +110,6 @@ public final class Account extends EmailContent implements AccountColumns, Parce public static final int CHECK_INTERVAL_NEVER = -1; public static final int CHECK_INTERVAL_PUSH = -2; - public static Uri CONTENT_URI; - public static Uri ADD_TO_FIELD_URI; - public static Uri RESET_NEW_MESSAGE_COUNT_URI; - public static Uri NOTIFIER_URI; - public static Uri DEFAULT_ACCOUNT_ID_URI; - - public static void initAccount() { - CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/account"); - ADD_TO_FIELD_URI = Uri.parse(EmailContent.CONTENT_URI + "/accountIdAddToField"); - RESET_NEW_MESSAGE_COUNT_URI = Uri.parse(EmailContent.CONTENT_URI + "/resetNewMessageCount"); - NOTIFIER_URI = Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account"); - DEFAULT_ACCOUNT_ID_URI = Uri.parse(EmailContent.CONTENT_URI + "/account/default"); - } - public String mDisplayName; public String mEmailAddress; public String mSyncKey; @@ -134,6 +128,10 @@ public final class Account extends EmailContent implements AccountColumns, Parce public String mSignature; public long mPolicyKey; + // For compatibility with Email1 + public long mNotifiedMessageId; + public int mNotifiedMessageCount; + // Convenience for creating/working with an account public transient HostAuth mHostAuthRecv; public transient HostAuth mHostAuthSend; @@ -159,6 +157,8 @@ public final class Account extends EmailContent implements AccountColumns, Parce public static final int CONTENT_SECURITY_SYNC_KEY_COLUMN = 15; public static final int CONTENT_SIGNATURE_COLUMN = 16; public static final int CONTENT_POLICY_KEY = 17; + public static final int CONTENT_NOTIFIED_MESSAGE_ID_COLUMN = 18; + public static final int CONTENT_NOTIFIED_MESSAGE_COUNT_COLUMN = 19; public static final String[] CONTENT_PROJECTION = new String[] { RECORD_ID, AccountColumns.DISPLAY_NAME, @@ -168,7 +168,8 @@ public final class Account extends EmailContent implements AccountColumns, Parce AccountColumns.COMPATIBILITY_UUID, AccountColumns.SENDER_NAME, AccountColumns.RINGTONE_URI, AccountColumns.PROTOCOL_VERSION, AccountColumns.NEW_MESSAGE_COUNT, AccountColumns.SECURITY_SYNC_KEY, - AccountColumns.SIGNATURE, AccountColumns.POLICY_KEY + AccountColumns.SIGNATURE, AccountColumns.POLICY_KEY, + AccountColumns.NOTIFIED_MESSAGE_ID, AccountColumns.NOTIFIED_MESSAGE_COUNT }; public static final int CONTENT_MAILBOX_TYPE_COLUMN = 1; @@ -200,6 +201,13 @@ public final class Account extends EmailContent implements AccountColumns, Parce MailboxColumns.TYPE + " = " + Mailbox.TYPE_INBOX + " AND " + MailboxColumns.ACCOUNT_KEY + " =?"; + /** + * This projection is for searching for the default account + */ + private static final String[] DEFAULT_ID_PROJECTION = new String[] { + RECORD_ID, IS_DEFAULT + }; + /** * no public constructor since this is a utility class */ @@ -266,6 +274,8 @@ public final class Account extends EmailContent implements AccountColumns, Parce mSecuritySyncKey = cursor.getString(CONTENT_SECURITY_SYNC_KEY_COLUMN); mSignature = cursor.getString(CONTENT_SIGNATURE_COLUMN); mPolicyKey = cursor.getLong(CONTENT_POLICY_KEY); + mNotifiedMessageId = cursor.getLong(CONTENT_NOTIFIED_MESSAGE_ID_COLUMN); + mNotifiedMessageCount = cursor.getInt(CONTENT_NOTIFIED_MESSAGE_COUNT_COLUMN); } private long getId(Uri u) { @@ -453,6 +463,21 @@ public final class Account extends EmailContent implements AccountColumns, Parce return "local://localhost/" + context.getDatabasePath(getUuid() + ".db"); } + /** + * @return true if the instance is of an EAS account. + * + * NOTE This method accesses the DB if {@link #mHostAuthRecv} hasn't been restored yet. + * Use caution when you use this on the main thread. + */ + public boolean isEasAccount(Context context) { + return "eas".equals(getProtocol(context)); + } + + public boolean supportsMoveMessages(Context context) { + String protocol = getProtocol(context); + return "eas".equals(protocol) || "imap".equals(protocol); + } + /** * @return true if the account supports "search". */ @@ -502,7 +527,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce public static long getAccountIdFromShortcutSafeUri(Context context, Uri uri) { // Make sure the URI is in the correct format. if (!"content".equals(uri.getScheme()) - || !EmailContent.AUTHORITY.equals(uri.getAuthority())) { + || !AUTHORITY.equals(uri.getAuthority())) { return -1; } @@ -706,7 +731,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce .newUpdate(ContentUris.withAppendedId(CONTENT_URI, mId)) .withValues(cv).build()); try { - context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops); + context.getContentResolver().applyBatch(AUTHORITY, ops); return 1; } catch (RemoteException e) { // There is nothing to be done here; fail by returning 0 @@ -786,7 +811,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce try { ContentProviderResult[] results = - context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops); + context.getContentResolver().applyBatch(AUTHORITY, ops); // If saving, set the mId's of the various saved objects if (recvIndex >= 0) { long newId = getId(results[recvIndex].uri); @@ -829,6 +854,8 @@ public final class Account extends EmailContent implements AccountColumns, Parce values.put(AccountColumns.SECURITY_SYNC_KEY, mSecuritySyncKey); values.put(AccountColumns.SIGNATURE, mSignature); values.put(AccountColumns.POLICY_KEY, mPolicyKey); + values.put(AccountColumns.NOTIFIED_MESSAGE_ID, mNotifiedMessageId); + values.put(AccountColumns.NOTIFIED_MESSAGE_COUNT, mNotifiedMessageCount); return values; } diff --git a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java index 05ba4e28b..f9fe69602 100755 --- a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java +++ b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java @@ -29,7 +29,6 @@ import android.os.Environment; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; -import android.util.Log; import com.android.emailcommon.utility.TextUtilities; import com.android.emailcommon.utility.Utility; @@ -60,12 +59,31 @@ import java.util.ArrayList; * */ public abstract class EmailContent { + + public static final String AUTHORITY = "com.android.email.provider"; + // The notifier authority is used to send notifications regarding changes to messages (insert, + // delete, or update) and is intended as an optimization for use by clients of message list + // cursors (initially, the email AppWidget). + public static final String NOTIFIER_AUTHORITY = "com.android.email.notifier"; + + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); + public static final String PARAMETER_LIMIT = "limit"; + + public static final Uri CONTENT_NOTIFIER_URI = Uri.parse("content://" + NOTIFIER_AUTHORITY); + + public static final Uri MAILBOX_NOTIFICATION_URI = + Uri.parse("content://" + EmailContent.AUTHORITY + "/mailboxNotification"); public static final String[] NOTIFICATION_PROJECTION = new String[] {MailboxColumns.ID, MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT}; public static final int NOTIFICATION_MAILBOX_ID_COLUMN = 0; public static final int NOTIFICATION_MAILBOX_UNREAD_COUNT_COLUMN = 1; public static final int NOTIFICATION_MAILBOX_MESSAGE_COUNT_COLUMN = 2; + public static final Uri MAILBOX_MOST_RECENT_MESSAGE_URI = + Uri.parse("content://" + EmailContent.AUTHORITY + "/mailboxMostRecentMessage"); + + public static final String PROVIDER_PERMISSION = "com.android.email.permission.ACCESS_PROVIDER"; + // All classes share this public static final String RECORD_ID = "_id"; @@ -113,57 +131,6 @@ public abstract class EmailContent { // Read the Content from a ContentCursor public abstract void restore (Cursor cursor); - - public static String PACKAGE_NAME; - public static String EMAIL_PACKAGE_NAME; - public static String AUTHORITY; - // The notifier authority is used to send notifications regarding changes to messages (insert, - // delete, or update) and is intended as an optimization for use by clients of message list - // cursors (initially, the email AppWidget). - public static String NOTIFIER_AUTHORITY; - public static Uri CONTENT_URI; - public static final String PARAMETER_LIMIT = "limit"; - public static Uri CONTENT_NOTIFIER_URI; - public static Uri PICK_TRASH_FOLDER_URI; - public static Uri PICK_SENT_FOLDER_URI; - public static Uri MAILBOX_NOTIFICATION_URI; - public static Uri MAILBOX_MOST_RECENT_MESSAGE_URI; - public static Uri ACCOUNT_CHECK_URI; - public static String PROVIDER_PERMISSION; - - public static void init(Context context) { - if (AUTHORITY == null) { - PACKAGE_NAME = context.getPackageName(); - EMAIL_PACKAGE_NAME = PACKAGE_NAME; - // If our package is com...exchange, the provider is com...email.provider - if (PACKAGE_NAME.endsWith("exchange")) { - int lastDot = EMAIL_PACKAGE_NAME.lastIndexOf('.'); - EMAIL_PACKAGE_NAME = PACKAGE_NAME.substring(0, lastDot + 1) + "email"; - } - AUTHORITY = EMAIL_PACKAGE_NAME + ".provider"; - Log.d("EmailContent", "init for " + AUTHORITY); - NOTIFIER_AUTHORITY = EMAIL_PACKAGE_NAME + ".notifier"; - CONTENT_URI = Uri.parse("content://" + AUTHORITY); - CONTENT_NOTIFIER_URI = Uri.parse("content://" + NOTIFIER_AUTHORITY); - PICK_TRASH_FOLDER_URI = Uri.parse("content://" + AUTHORITY + "/pickTrashFolder"); - PICK_SENT_FOLDER_URI = Uri.parse("content://" + AUTHORITY + "/pickSentFolder"); - MAILBOX_NOTIFICATION_URI = Uri.parse("content://" + AUTHORITY + "/mailboxNotification"); - MAILBOX_MOST_RECENT_MESSAGE_URI = Uri.parse("content://" + AUTHORITY + - "/mailboxMostRecentMessage"); - ACCOUNT_CHECK_URI = Uri.parse("content://" + AUTHORITY + "/accountCheck"); - PROVIDER_PERMISSION = EMAIL_PACKAGE_NAME + ".permission.ACCESS_PROVIDER"; - // Initialize subclasses - Account.initAccount(); - Mailbox.initMailbox(); - QuickResponse.initQuickResponse(); - HostAuth.initHostAuth(); - Policy.initPolicy(); - Message.initMessage(); - Body.initBody(); - Attachment.initAttachment(); - } - } - // The Uri is lazily initialized public Uri getUri() { if (mUri == null) { @@ -293,17 +260,14 @@ public abstract class EmailContent { // The plain text content itself public static final String TEXT_CONTENT = "textContent"; // Replied-to or forwarded body (in html form) - @Deprecated public static final String HTML_REPLY = "htmlReply"; // Replied-to or forwarded body (in text form) - @Deprecated public static final String TEXT_REPLY = "textReply"; // A reference to a message's unique id used in reply/forward. // Protocol code can be expected to use this column in determining whether a message can be // deleted safely (i.e. isn't referenced by other messages) public static final String SOURCE_MESSAGE_KEY = "sourceMessageKey"; // The text to be placed between a reply/forward response and the original message - @Deprecated public static final String INTRO_TEXT = "introText"; // The start of quoted text within our text content public static final String QUOTED_TEXT_START_POS = "quotedTextStartPos"; @@ -312,22 +276,16 @@ public abstract class EmailContent { public static final class Body extends EmailContent implements BodyColumns { public static final String TABLE_NAME = "Body"; - public static Uri CONTENT_URI; - - public static void initBody() { - CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/body"); - } + @SuppressWarnings("hiding") + public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/body"); public static final int CONTENT_ID_COLUMN = 0; public static final int CONTENT_MESSAGE_KEY_COLUMN = 1; public static final int CONTENT_HTML_CONTENT_COLUMN = 2; public static final int CONTENT_TEXT_CONTENT_COLUMN = 3; - @Deprecated public static final int CONTENT_HTML_REPLY_COLUMN = 4; - @Deprecated public static final int CONTENT_TEXT_REPLY_COLUMN = 5; public static final int CONTENT_SOURCE_KEY_COLUMN = 6; - @Deprecated public static final int CONTENT_INTRO_TEXT_COLUMN = 7; public static final int CONTENT_QUOTED_TEXT_START_POS_COLUMN = 8; @@ -343,22 +301,19 @@ public abstract class EmailContent { public static final String[] COMMON_PROJECTION_HTML = new String[] { RECORD_ID, BodyColumns.HTML_CONTENT }; - @Deprecated public static final String[] COMMON_PROJECTION_REPLY_TEXT = new String[] { RECORD_ID, BodyColumns.TEXT_REPLY }; - @Deprecated public static final String[] COMMON_PROJECTION_REPLY_HTML = new String[] { RECORD_ID, BodyColumns.HTML_REPLY }; - @Deprecated public static final String[] COMMON_PROJECTION_INTRO = new String[] { RECORD_ID, BodyColumns.INTRO_TEXT }; public static final String[] COMMON_PROJECTION_SOURCE = new String[] { RECORD_ID, BodyColumns.SOURCE_MESSAGE_KEY }; - public static final int COMMON_PROJECTION_COLUMN_TEXT = 1; + public static final int COMMON_PROJECTION_COLUMN_TEXT = 1; private static final String[] PROJECTION_SOURCE_KEY = new String[] { BodyColumns.SOURCE_MESSAGE_KEY }; @@ -366,9 +321,7 @@ public abstract class EmailContent { public long mMessageKey; public String mHtmlContent; public String mTextContent; - @Deprecated public String mHtmlReply; - @Deprecated public String mTextReply; public int mQuotedTextStartPos; @@ -378,7 +331,6 @@ public abstract class EmailContent { * want to include quoted text. */ public long mSourceKey; - @Deprecated public String mIntroText; public Body() { @@ -493,17 +445,14 @@ public abstract class EmailContent { return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_HTML); } - @Deprecated public static String restoreReplyTextWithMessageId(Context context, long messageId) { return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_TEXT); } - @Deprecated public static String restoreReplyHtmlWithMessageId(Context context, long messageId) { return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_HTML); } - @Deprecated public static String restoreIntroTextWithMessageId(Context context, long messageId) { return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_INTRO); } @@ -548,8 +497,8 @@ public abstract class EmailContent { public static final String FLAGS = "flags"; // Sync related identifiers - // Saved draft info (reusing the never-used "clientId" column) - public static final String DRAFT_INFO = "clientId"; + // Any client-required identifier + public static final String CLIENT_ID = "clientId"; // The message-id in the message's header public static final String MESSAGE_ID = "messageId"; @@ -576,8 +525,6 @@ public abstract class EmailContent { public static final String PROTOCOL_SEARCH_INFO = "protocolSearchInfo"; // Simple thread topic public static final String THREAD_TOPIC = "threadTopic"; - // For sync adapter use - public static final String SYNC_DATA = "syncData"; } public static final class Message extends EmailContent implements SyncColumns, MessageColumns { @@ -586,28 +533,17 @@ public abstract class EmailContent { public static final String DELETED_TABLE_NAME = "Message_Deletes"; // To refer to a specific message, use ContentUris.withAppendedId(CONTENT_URI, id) - public static Uri CONTENT_URI; - public static Uri CONTENT_URI_LIMIT_1; - public static Uri SYNCED_CONTENT_URI; - public static Uri SELECTED_MESSAGE_CONTENT_URI ; - public static Uri DELETED_CONTENT_URI; - public static Uri UPDATED_CONTENT_URI; - public static Uri NOTIFIER_URI; - - public static void initMessage() { - CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message"); - CONTENT_URI_LIMIT_1 = uriWithLimit(CONTENT_URI, 1); - SYNCED_CONTENT_URI = - Uri.parse(EmailContent.CONTENT_URI + "/syncedMessage"); - SELECTED_MESSAGE_CONTENT_URI = - Uri.parse(EmailContent.CONTENT_URI + "/messageBySelection"); - DELETED_CONTENT_URI = - Uri.parse(EmailContent.CONTENT_URI + "/deletedMessage"); - UPDATED_CONTENT_URI = - Uri.parse(EmailContent.CONTENT_URI + "/updatedMessage"); - NOTIFIER_URI = - Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/message"); - } + @SuppressWarnings("hiding") + public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message"); + public static final Uri CONTENT_URI_LIMIT_1 = uriWithLimit(CONTENT_URI, 1); + public static final Uri SYNCED_CONTENT_URI = + Uri.parse(EmailContent.CONTENT_URI + "/syncedMessage"); + public static final Uri DELETED_CONTENT_URI = + Uri.parse(EmailContent.CONTENT_URI + "/deletedMessage"); + public static final Uri UPDATED_CONTENT_URI = + Uri.parse(EmailContent.CONTENT_URI + "/updatedMessage"); + public static final Uri NOTIFIER_URI = + Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/message"); public static final String KEY_TIMESTAMP_DESC = MessageColumns.TIMESTAMP + " desc"; @@ -621,7 +557,7 @@ public abstract class EmailContent { public static final int CONTENT_FLAG_ATTACHMENT_COLUMN = 7; public static final int CONTENT_FLAGS_COLUMN = 8; public static final int CONTENT_SERVER_ID_COLUMN = 9; - public static final int CONTENT_DRAFT_INFO_COLUMN = 10; + public static final int CONTENT_CLIENT_ID_COLUMN = 10; public static final int CONTENT_MESSAGE_ID_COLUMN = 11; public static final int CONTENT_MAILBOX_KEY_COLUMN = 12; public static final int CONTENT_ACCOUNT_KEY_COLUMN = 13; @@ -635,7 +571,6 @@ public abstract class EmailContent { public static final int CONTENT_SNIPPET_COLUMN = 21; public static final int CONTENT_PROTOCOL_SEARCH_INFO_COLUMN = 22; public static final int CONTENT_THREAD_TOPIC_COLUMN = 23; - public static final int CONTENT_SYNC_DATA_COLUMN = 24; public static final String[] CONTENT_PROJECTION = new String[] { RECORD_ID, @@ -643,14 +578,14 @@ public abstract class EmailContent { MessageColumns.SUBJECT, MessageColumns.FLAG_READ, MessageColumns.FLAG_LOADED, MessageColumns.FLAG_FAVORITE, MessageColumns.FLAG_ATTACHMENT, MessageColumns.FLAGS, - SyncColumns.SERVER_ID, MessageColumns.DRAFT_INFO, + SyncColumns.SERVER_ID, MessageColumns.CLIENT_ID, MessageColumns.MESSAGE_ID, MessageColumns.MAILBOX_KEY, MessageColumns.ACCOUNT_KEY, MessageColumns.FROM_LIST, MessageColumns.TO_LIST, MessageColumns.CC_LIST, MessageColumns.BCC_LIST, MessageColumns.REPLY_TO_LIST, SyncColumns.SERVER_TIMESTAMP, MessageColumns.MEETING_INFO, MessageColumns.SNIPPET, MessageColumns.PROTOCOL_SEARCH_INFO, - MessageColumns.THREAD_TOPIC, MessageColumns.SYNC_DATA + MessageColumns.THREAD_TOPIC }; public static final int LIST_ID_COLUMN = 0; @@ -766,7 +701,7 @@ public abstract class EmailContent { public String mServerId; public long mServerTimeStamp; - public int mDraftInfo; + public String mClientId; public String mMessageId; public long mMailboxKey; @@ -787,8 +722,6 @@ public abstract class EmailContent { public String mThreadTopic; - public String mSyncData; - /** * Base64-encoded representation of the byte array provided by servers for identifying * messages belonging to the same conversation thread. Currently unsupported and not @@ -817,7 +750,6 @@ public abstract class EmailContent { public static final int FLAG_LOADED_COMPLETE = 1; public static final int FLAG_LOADED_PARTIAL = 2; public static final int FLAG_LOADED_DELETED = 3; - public static final int FLAG_LOADED_UNKNOWN = 4; // Bits used in mFlags // The following three states are mutually exclusive, and indicate whether the message is an @@ -858,10 +790,6 @@ public abstract class EmailContent { // compatibility public static final int FLAG_TYPE_REPLY_ALL = 1 << 21; - // Flag used in draftInfo to indicate that the reference message should be appended - public static final int DRAFT_INFO_APPEND_REF_MESSAGE = 1 << 24; - public static final int DRAFT_INFO_QUOTE_POS_MASK = 0xFFFFFF; - /** a pseudo ID for "no message". */ public static final long NO_MESSAGE = -1L; @@ -882,22 +810,28 @@ public abstract class EmailContent { values.put(MessageColumns.FLAG_FAVORITE, mFlagFavorite); values.put(MessageColumns.FLAG_ATTACHMENT, mFlagAttachment); values.put(MessageColumns.FLAGS, mFlags); + values.put(SyncColumns.SERVER_ID, mServerId); values.put(SyncColumns.SERVER_TIMESTAMP, mServerTimeStamp); - values.put(MessageColumns.DRAFT_INFO, mDraftInfo); + values.put(MessageColumns.CLIENT_ID, mClientId); values.put(MessageColumns.MESSAGE_ID, mMessageId); + values.put(MessageColumns.MAILBOX_KEY, mMailboxKey); values.put(MessageColumns.ACCOUNT_KEY, mAccountKey); + values.put(MessageColumns.FROM_LIST, mFrom); values.put(MessageColumns.TO_LIST, mTo); values.put(MessageColumns.CC_LIST, mCc); values.put(MessageColumns.BCC_LIST, mBcc); values.put(MessageColumns.REPLY_TO_LIST, mReplyTo); + values.put(MessageColumns.MEETING_INFO, mMeetingInfo); + values.put(MessageColumns.SNIPPET, mSnippet); + values.put(MessageColumns.PROTOCOL_SEARCH_INFO, mProtocolSearchInfo); + values.put(MessageColumns.THREAD_TOPIC, mThreadTopic); - values.put(MessageColumns.SYNC_DATA, mSyncData); return values; } @@ -920,7 +854,7 @@ public abstract class EmailContent { mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN); mServerId = cursor.getString(CONTENT_SERVER_ID_COLUMN); mServerTimeStamp = cursor.getLong(CONTENT_SERVER_TIMESTAMP_COLUMN); - mDraftInfo = cursor.getInt(CONTENT_DRAFT_INFO_COLUMN); + mClientId = cursor.getString(CONTENT_CLIENT_ID_COLUMN); mMessageId = cursor.getString(CONTENT_MESSAGE_ID_COLUMN); mMailboxKey = cursor.getLong(CONTENT_MAILBOX_KEY_COLUMN); mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN); @@ -933,7 +867,6 @@ public abstract class EmailContent { mSnippet = cursor.getString(CONTENT_SNIPPET_COLUMN); mProtocolSearchInfo = cursor.getString(CONTENT_PROTOCOL_SEARCH_INFO_COLUMN); mThreadTopic = cursor.getString(CONTENT_THREAD_TOPIC_COLUMN); - mSyncData = cursor.getString(CONTENT_SYNC_DATA_COLUMN); } public boolean update() { @@ -1030,31 +963,37 @@ public abstract class EmailContent { if (mHtml != null) { cv.put(Body.HTML_CONTENT, mHtml); } + if (mTextReply != null) { + cv.put(Body.TEXT_REPLY, mTextReply); + } + if (mHtmlReply != null) { + cv.put(Body.HTML_REPLY, mHtmlReply); + } if (mSourceKey != 0) { cv.put(Body.SOURCE_MESSAGE_KEY, mSourceKey); } + if (mIntroText != null) { + cv.put(Body.INTRO_TEXT, mIntroText); + } if (mQuotedTextStartPos != 0) { cv.put(Body.QUOTED_TEXT_START_POS, mQuotedTextStartPos); } + b = ContentProviderOperation.newInsert(Body.CONTENT_URI); + // Put our message id in the Body + if (!isNew) { + cv.put(Body.MESSAGE_KEY, mId); + } + b.withValues(cv); // We'll need this if we're new int messageBackValue = ops.size() - 1; - // Only create a body if we've got some data - if (!cv.keySet().isEmpty()) { - b = ContentProviderOperation.newInsert(Body.CONTENT_URI); - // Put our message id in the Body - if (!isNew) { - cv.put(Body.MESSAGE_KEY, mId); - } - b.withValues(cv); - // If we're new, create a back value entry - if (isNew) { - ContentValues backValues = new ContentValues(); - backValues.put(Body.MESSAGE_KEY, messageBackValue); - b.withValueBackReferences(backValues); - } - // And add the Body operation - ops.add(b.build()); + // If we're new, create a back value entry + if (isNew) { + ContentValues backValues = new ContentValues(); + backValues.put(Body.MESSAGE_KEY, messageBackValue); + b.withValueBackReferences(backValues); } + // And add the Body operation + ops.add(b.build()); // Create the attaachments, if any if (mAttachments != null) { @@ -1181,30 +1120,17 @@ public abstract class EmailContent { public static final class Attachment extends EmailContent implements AttachmentColumns, Parcelable { public static final String TABLE_NAME = "Attachment"; - public static final String ATTACHMENT_PROVIDER_LEGACY_URI_PREFIX = - "content://com.android.email.attachmentprovider"; - - public static Uri CONTENT_URI; + @SuppressWarnings("hiding") + public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/attachment"); // This must be used with an appended id: ContentUris.withAppendedId(MESSAGE_ID_URI, id) - public static Uri MESSAGE_ID_URI; - public static String ATTACHMENT_PROVIDER_URI_PREFIX; - public static boolean sUsingLegacyPrefix; - - public static void initAttachment() { - CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/attachment"); - MESSAGE_ID_URI = Uri.parse( - EmailContent.CONTENT_URI + "/attachment/message"); - ATTACHMENT_PROVIDER_URI_PREFIX = "content://" + EmailContent.EMAIL_PACKAGE_NAME + - ".attachmentprovider"; - sUsingLegacyPrefix = - ATTACHMENT_PROVIDER_URI_PREFIX.equals(ATTACHMENT_PROVIDER_LEGACY_URI_PREFIX); - } + public static final Uri MESSAGE_ID_URI = Uri.parse( + EmailContent.CONTENT_URI + "/attachment/message"); public String mFileName; public String mMimeType; public long mSize; public String mContentId; - private String mContentUri; + public String mContentUri; public long mMessageKey; public String mLocation; public String mEncoding; @@ -1280,31 +1206,6 @@ public abstract class EmailContent { mBaseUri = CONTENT_URI; } - public void setContentUri(String contentUri) { - mContentUri = contentUri; - } - - public String getContentUri() { - if (mContentUri == null) return null; // - // If we're not using the legacy prefix and the uri IS, we need to modify it - if (!Attachment.sUsingLegacyPrefix && - mContentUri.startsWith(Attachment.ATTACHMENT_PROVIDER_LEGACY_URI_PREFIX)) { - // In an upgrade scenario, we may still have legacy attachment Uri's - // Skip past content:// - int prefix = mContentUri.indexOf('/', 10); - if (prefix > 0) { - // Create a proper uri string using the actual provider - return ATTACHMENT_PROVIDER_URI_PREFIX + "/" + mContentUri.substring(prefix); - } else { - Log.e("Attachment", "Improper contentUri format: " + mContentUri); - // Belt & suspenders; can't really happen - return mContentUri; - } - } else { - return mContentUri; - } - } - /** * Restore an Attachment from the database, given its unique id * @param context @@ -1534,6 +1435,10 @@ public abstract class EmailContent { public static final String SIGNATURE = "signature"; // A foreign key into the Policy table public static final String POLICY_KEY = "policyKey"; + // For compatibility w/ Email1 + public static final String NOTIFIED_MESSAGE_ID = "notifiedMessageId"; + // For compatibility w/ Email1 + public static final String NOTIFIED_MESSAGE_COUNT = "notifiedMessageCount"; } public interface QuickResponseColumns { @@ -1591,8 +1496,8 @@ public abstract class EmailContent { public static final String LAST_NOTIFIED_MESSAGE_COUNT = "lastNotifiedMessageCount"; // The total number of messages in the remote mailbox public static final String TOTAL_COUNT = "totalCount"; - // The full hierarchical name of this folder, in the form a/b/c - public static final String HIERARCHICAL_NAME = "hierarchicalName"; + // For compatibility with Email1 + public static final String LAST_SEEN_MESSAGE_KEY = "lastSeenMessageKey"; } public interface HostAuthColumns { @@ -1615,8 +1520,6 @@ public abstract class EmailContent { static final String CLIENT_CERT_ALIAS = "certAlias"; // DEPRECATED - Will not be set or stored static final String ACCOUNT_KEY = "accountKey"; - // A blob containing an X509 server certificate - static final String SERVER_CERT = "serverCert"; } public interface PolicyColumns { diff --git a/emailcommon/src/com/android/emailcommon/provider/HostAuth.java b/emailcommon/src/com/android/emailcommon/provider/HostAuth.java index 72b6606e3..8729418bc 100644 --- a/emailcommon/src/com/android/emailcommon/provider/HostAuth.java +++ b/emailcommon/src/com/android/emailcommon/provider/HostAuth.java @@ -34,15 +34,14 @@ import java.net.URISyntaxException; public final class HostAuth extends EmailContent implements HostAuthColumns, Parcelable { public static final String TABLE_NAME = "HostAuth"; - public static Uri CONTENT_URI; - - public static void initHostAuth() { - CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/hostauth"); - } - - // These legacy constants should be used in code created prior to Email2 - public static final String LEGACY_SCHEME_SMTP = "smtp"; - + @SuppressWarnings("hiding") + public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/hostauth"); + // TODO the three following constants duplicate constants in Store.java; remove those and + // just reference these. + public static final String SCHEME_IMAP = "imap"; + public static final String SCHEME_POP3 = "pop3"; + public static final String SCHEME_EAS = "eas"; + public static final String SCHEME_SMTP = "smtp"; public static final String SCHEME_TRUST_ALL_CERTS = "trustallcerts"; public static final int PORT_UNKNOWN = -1; @@ -63,8 +62,6 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par public String mPassword; public String mDomain; public String mClientCertAlias = null; - // NOTE: The server certificate is NEVER automatically retrieved from EmailProvider - public byte[] mServerCert = null; public static final int CONTENT_ID_COLUMN = 0; public static final int CONTENT_PROTOCOL_COLUMN = 1; @@ -272,7 +269,19 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par mPort = port; if (mPort == PORT_UNKNOWN) { boolean useSSL = ((mFlags & FLAG_SSL) != 0); - if (LEGACY_SCHEME_SMTP.equals(mProtocol)) { + // infer port# from protocol + security + // SSL implies a different port - TLS runs in the "regular" port + // NOTE: Although the port should be setup in the various setup screens, this + // block cannot easily be moved because we get process URIs from other sources + // (e.g. for tests, provider templates and account restore) that may or may not + // have a port specified. + if (SCHEME_POP3.equals(mProtocol)) { + mPort = useSSL ? 995 : 110; + } else if (SCHEME_IMAP.equals(mProtocol)) { + mPort = useSSL ? 993 : 143; + } else if (SCHEME_EAS.equals(mProtocol)) { + mPort = useSSL ? 443 : 80; + } else if (SCHEME_SMTP.equals(mProtocol)) { mPort = useSSL ? 465 : 587; } } @@ -280,6 +289,10 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par mClientCertAlias = clientCertAlias; } + /** Returns {@code true} if this is an EAS connection; otherwise, {@code false}. */ + public boolean isEasConnection() { + return SCHEME_EAS.equals(mProtocol); + } /** Convenience method to determine if SSL is used. */ public boolean shouldUseSsl() { @@ -355,7 +368,6 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par } HostAuth that = (HostAuth)o; return mPort == that.mPort - && mId == that.mId && mFlags == that.mFlags && Utility.areStringsEqual(mProtocol, that.mProtocol) && Utility.areStringsEqual(mAddress, that.mAddress) @@ -363,7 +375,6 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par && Utility.areStringsEqual(mPassword, that.mPassword) && Utility.areStringsEqual(mDomain, that.mDomain) && Utility.areStringsEqual(mClientCertAlias, that.mClientCertAlias); - // We don't care about the server certificate for equals } /** diff --git a/emailcommon/src/com/android/emailcommon/provider/Mailbox.java b/emailcommon/src/com/android/emailcommon/provider/Mailbox.java index 7307dbd47..56cbecf99 100644 --- a/emailcommon/src/com/android/emailcommon/provider/Mailbox.java +++ b/emailcommon/src/com/android/emailcommon/provider/Mailbox.java @@ -33,17 +33,12 @@ import com.android.emailcommon.utility.Utility; public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns, Parcelable { public static final String TABLE_NAME = "Mailbox"; - - public static Uri CONTENT_URI; - public static Uri ADD_TO_FIELD_URI; - public static Uri FROM_ACCOUNT_AND_TYPE_URI; - - public static void initMailbox() { - CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailbox"); - ADD_TO_FIELD_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdAddToField"); - FROM_ACCOUNT_AND_TYPE_URI = Uri.parse(EmailContent.CONTENT_URI + - "/mailboxIdFromAccountAndType"); - } + @SuppressWarnings("hiding") + public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailbox"); + public static final Uri ADD_TO_FIELD_URI = + Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdAddToField"); + public static final Uri FROM_ACCOUNT_AND_TYPE_URI = + Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdFromAccountAndType"); public String mDisplayName; public String mServerId; @@ -66,7 +61,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns public long mLastNotifiedMessageKey; public int mLastNotifiedMessageCount; public int mTotalCount; - public String mHierarchicalName; + public long mLastSeenMessageKey; public static final int CONTENT_ID_COLUMN = 0; public static final int CONTENT_DISPLAY_NAME_COLUMN = 1; @@ -90,7 +85,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns public static final int CONTENT_LAST_NOTIFIED_MESSAGE_KEY_COLUMN = 19; public static final int CONTENT_LAST_NOTIFIED_MESSAGE_COUNT_COLUMN = 20; public static final int CONTENT_TOTAL_COUNT_COLUMN = 21; - public static final int CONTENT_HIERARCHICAL_NAME_COLUMN = 22; + public static final int CONTENT_LAST_SEEN_MESSAGE_KEY_COLUMN = 22; /** * NOTE: If fields are added or removed, the method {@link #getHashes()} @@ -105,7 +100,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns MailboxColumns.SYNC_STATUS, MailboxColumns.PARENT_KEY, MailboxColumns.LAST_TOUCHED_TIME, MailboxColumns.UI_SYNC_STATUS, MailboxColumns.UI_LAST_SYNC_RESULT, MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY, MailboxColumns.LAST_NOTIFIED_MESSAGE_COUNT, - MailboxColumns.TOTAL_COUNT, MailboxColumns.HIERARCHICAL_NAME + MailboxColumns.TOTAL_COUNT, MailboxColumns.LAST_SEEN_MESSAGE_KEY }; private static final String ACCOUNT_AND_MAILBOX_TYPE_SELECTION = @@ -337,7 +332,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns mLastNotifiedMessageKey = cursor.getLong(CONTENT_LAST_NOTIFIED_MESSAGE_KEY_COLUMN); mLastNotifiedMessageCount = cursor.getInt(CONTENT_LAST_NOTIFIED_MESSAGE_COUNT_COLUMN); mTotalCount = cursor.getInt(CONTENT_TOTAL_COUNT_COLUMN); - mHierarchicalName = cursor.getString(CONTENT_HIERARCHICAL_NAME_COLUMN); + mLastSeenMessageKey = cursor.getLong(CONTENT_LAST_SEEN_MESSAGE_KEY_COLUMN); } @Override @@ -364,7 +359,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns values.put(MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY, mLastNotifiedMessageKey); values.put(MailboxColumns.LAST_NOTIFIED_MESSAGE_COUNT, mLastNotifiedMessageCount); values.put(MailboxColumns.TOTAL_COUNT, mTotalCount); - values.put(MailboxColumns.HIERARCHICAL_NAME, mHierarchicalName); + values.put(MailboxColumns.LAST_SEEN_MESSAGE_KEY, mLastSeenMessageKey); return values; } @@ -497,9 +492,43 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns case TYPE_MAIL: case TYPE_TRASH: case TYPE_JUNK: + case TYPE_SENT: return true; } - return false; // TYPE_DRAFTS, TYPE_OUTBOX, TYPE_SENT, etc + return false; // TYPE_DRAFTS, TYPE_OUTBOX, etc + } + + /** + * @return whether or not this mailbox retrieves its data from the server (as opposed to just + * a local mailbox that is never synced). + */ + public boolean loadsFromServer(String protocol) { + if (HostAuth.SCHEME_EAS.equals(protocol)) { + return mType != Mailbox.TYPE_DRAFTS + && mType != Mailbox.TYPE_OUTBOX + && mType != Mailbox.TYPE_SEARCH + && mType < Mailbox.TYPE_NOT_SYNCABLE; + + } else if (HostAuth.SCHEME_IMAP.equals(protocol)) { + // TODO: actually use a sync flag when creating the mailboxes. Right now we use an + // approximation for IMAP. + return mType != Mailbox.TYPE_DRAFTS + && mType != Mailbox.TYPE_OUTBOX + && mType != Mailbox.TYPE_SEARCH; + + } else if (HostAuth.SCHEME_POP3.equals(protocol)) { + return TYPE_INBOX == mType; + } + + return false; + } + + public boolean uploadsToServer(Context context) { + if (mType == TYPE_DRAFTS || mType == TYPE_OUTBOX || mType == TYPE_SEARCH) { + return false; + } + String protocol = Account.getProtocol(context, mAccountKey); + return (!protocol.equals(HostAuth.SCHEME_POP3)); } /** @@ -560,8 +589,6 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns = mLastNotifiedMessageCount; hash[CONTENT_TOTAL_COUNT_COLUMN] = mTotalCount; - hash[CONTENT_HIERARCHICAL_NAME_COLUMN] - = mHierarchicalName; return hash; } @@ -597,7 +624,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns dest.writeLong(mLastNotifiedMessageKey); dest.writeInt(mLastNotifiedMessageCount); dest.writeInt(mTotalCount); - dest.writeString(mHierarchicalName); + dest.writeLong(mLastSeenMessageKey); } public Mailbox(Parcel in) { @@ -624,7 +651,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns mLastNotifiedMessageKey = in.readLong(); mLastNotifiedMessageCount = in.readInt(); mTotalCount = in.readInt(); - mHierarchicalName = in.readString(); + mLastSeenMessageKey = in.readLong(); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { diff --git a/emailcommon/src/com/android/emailcommon/provider/MailboxUtilities.java b/emailcommon/src/com/android/emailcommon/provider/MailboxUtilities.java deleted file mode 100644 index adba81402..000000000 --- a/emailcommon/src/com/android/emailcommon/provider/MailboxUtilities.java +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.emailcommon.provider; - -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.os.Debug; -import android.text.TextUtils; -import android.util.Log; - -import com.android.emailcommon.Logging; -import com.android.emailcommon.provider.Account; -import com.android.emailcommon.provider.EmailContent.MailboxColumns; -import com.android.emailcommon.provider.Mailbox; - -import java.util.HashMap; - -public class MailboxUtilities { - public static final String WHERE_PARENT_KEY_UNINITIALIZED = - "(" + MailboxColumns.PARENT_KEY + " isnull OR " + MailboxColumns.PARENT_KEY + "=" + - Mailbox.PARENT_KEY_UNINITIALIZED + ")"; - // The flag we use in Account to indicate a mailbox change in progress - private static final int ACCOUNT_MAILBOX_CHANGE_FLAG = Account.FLAGS_SYNC_ADAPTER; - - /** - * Recalculate a mailbox's flags and the parent key of any children - * @param context the caller's context - * @param parentCursor a cursor to a mailbox that requires fixup - */ - public static void setFlagsAndChildrensParentKey(Context context, Cursor parentCursor, - String accountSelector) { - ContentResolver resolver = context.getContentResolver(); - String[] selectionArgs = new String[1]; - ContentValues parentValues = new ContentValues(); - // Get the data we need first - long parentId = parentCursor.getLong(Mailbox.CONTENT_ID_COLUMN); - int parentFlags = 0; - int parentType = parentCursor.getInt(Mailbox.CONTENT_TYPE_COLUMN); - String parentServerId = parentCursor.getString(Mailbox.CONTENT_SERVER_ID_COLUMN); - // All email-type boxes hold mail - if (parentType <= Mailbox.TYPE_NOT_EMAIL) { - parentFlags |= Mailbox.FLAG_HOLDS_MAIL + Mailbox.FLAG_SUPPORTS_SETTINGS; - } - // Outbox, Drafts, and Sent don't allow mail to be moved to them - if (parentType == Mailbox.TYPE_MAIL || parentType == Mailbox.TYPE_TRASH || - parentType == Mailbox.TYPE_JUNK || parentType == Mailbox.TYPE_INBOX) { - parentFlags |= Mailbox.FLAG_ACCEPTS_MOVED_MAIL; - } - // There's no concept of "append" in EAS so FLAG_ACCEPTS_APPENDED_MAIL is never used - // Mark parent mailboxes as parents & add parent key to children - // An example of a mailbox with a null serverId would be an Outbox that we create locally - // for hotmail accounts (which don't have a server-based Outbox) - if (parentServerId != null) { - selectionArgs[0] = parentServerId; - Cursor childCursor = resolver.query(Mailbox.CONTENT_URI, - Mailbox.ID_PROJECTION, MailboxColumns.PARENT_SERVER_ID + "=? AND " + - accountSelector, selectionArgs, null); - if (childCursor == null) return; - try { - while (childCursor.moveToNext()) { - parentFlags |= Mailbox.FLAG_HAS_CHILDREN | Mailbox.FLAG_CHILDREN_VISIBLE; - ContentValues childValues = new ContentValues(); - childValues.put(Mailbox.PARENT_KEY, parentId); - long childId = childCursor.getLong(Mailbox.ID_PROJECTION_COLUMN); - resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, childId), - childValues, null, null); - } - } finally { - childCursor.close(); - } - } else { - // Mark this is having no parent, so that we don't examine this mailbox again - parentValues.put(Mailbox.PARENT_KEY, Mailbox.NO_MAILBOX); - Log.w(Logging.LOG_TAG, "Mailbox with null serverId: " + - parentCursor.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN) + ", type: " + - parentType); - } - // Save away updated flags and parent key (if any) - parentValues.put(Mailbox.FLAGS, parentFlags); - resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, parentId), - parentValues, null, null); - } - - /** - * Recalculate a mailbox's flags and the parent key of any children - * @param context the caller's context - * @param accountSelector (see description below in fixupUninitializedParentKeys) - * @param serverId the server id of an individual mailbox - */ - public static void setFlagsAndChildrensParentKey(Context context, String accountSelector, - String serverId) { - Cursor cursor = context.getContentResolver().query(Mailbox.CONTENT_URI, - Mailbox.CONTENT_PROJECTION, MailboxColumns.SERVER_ID + "=? AND " + accountSelector, - new String[] {serverId}, null); - if (cursor == null) return; - try { - if (cursor.moveToFirst()) { - setFlagsAndChildrensParentKey(context, cursor, accountSelector); - } - } finally { - cursor.close(); - } - } - - /** - * Given an account selector, specifying the account(s) on which to work, create the parentKey - * and flags for each mailbox in the account(s) that is uninitialized (parentKey = 0 or null) - * - * @param accountSelector a sqlite WHERE clause expression to be used in determining the - * mailboxes to be acted upon, e.g. accountKey IN (1, 2), accountKey = 12, etc. - */ - public static void fixupUninitializedParentKeys(Context context, String accountSelector) { - // Sanity check first on our arguments - if (accountSelector == null) throw new IllegalArgumentException(); - // The selection we'll use to find uninitialized parent key mailboxes - String noParentKeySelection = WHERE_PARENT_KEY_UNINITIALIZED + " AND " + accountSelector; - - // We'll loop through mailboxes with an uninitialized parent key - ContentResolver resolver = context.getContentResolver(); - Cursor noParentKeyMailboxCursor = - resolver.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION, - noParentKeySelection, null, null); - if (noParentKeyMailboxCursor == null) return; - try { - while (noParentKeyMailboxCursor.moveToNext()) { - setFlagsAndChildrensParentKey(context, noParentKeyMailboxCursor, accountSelector); - String parentServerId = - noParentKeyMailboxCursor.getString(Mailbox.CONTENT_PARENT_SERVER_ID_COLUMN); - // Fixup the parent so that the children's parentKey is updated - if (parentServerId != null) { - setFlagsAndChildrensParentKey(context, accountSelector, parentServerId); - } - } - } finally { - noParentKeyMailboxCursor.close(); - } - - // Any mailboxes without a parent key should have parentKey set to -1 (no parent) - ContentValues values = new ContentValues(); - values.put(Mailbox.PARENT_KEY, Mailbox.NO_MAILBOX); - resolver.update(Mailbox.CONTENT_URI, values, noParentKeySelection, null); - } - - private static void setAccountSyncAdapterFlag(Context context, long accountId, boolean start) { - Account account = Account.restoreAccountWithId(context, accountId); - if (account == null) return; - // Set temporary flag indicating state of update of mailbox list - ContentValues cv = new ContentValues(); - cv.put(Account.FLAGS, start ? (account.mFlags | ACCOUNT_MAILBOX_CHANGE_FLAG) : - account.mFlags & ~ACCOUNT_MAILBOX_CHANGE_FLAG); - context.getContentResolver().update( - ContentUris.withAppendedId(Account.CONTENT_URI, account.mId), cv, null, null); - } - - /** - * Indicate that the specified account is starting the process of changing its mailbox list - * @param context the caller's context - * @param accountId the account that is starting to change its mailbox list - */ - public static void startMailboxChanges(Context context, long accountId) { - setAccountSyncAdapterFlag(context, accountId, true); - } - - /** - * Indicate that the specified account is ending the process of changing its mailbox list - * @param context the caller's context - * @param accountId the account that is finished with changes to its mailbox list - */ - public static void endMailboxChanges(Context context, long accountId) { - setAccountSyncAdapterFlag(context, accountId, false); - } - - /** - * Check that we didn't leave the account's mailboxes in a (possibly) inconsistent state - * If we did, make them consistent again - * @param context the caller's context - * @param accountId the account whose mailboxes are to be checked - */ - public static void checkMailboxConsistency(Context context, long accountId) { - // If our temporary flag is set, we were interrupted during an update - // First, make sure we're current (really fast w/ caching) - Account account = Account.restoreAccountWithId(context, accountId); - if (account == null) return; - if ((account.mFlags & ACCOUNT_MAILBOX_CHANGE_FLAG) != 0) { - Log.w(Logging.LOG_TAG, "Account " + account.mDisplayName + - " has inconsistent mailbox data; fixing up..."); - // Set all account mailboxes to uninitialized parent key - ContentValues values = new ContentValues(); - values.put(Mailbox.PARENT_KEY, Mailbox.PARENT_KEY_UNINITIALIZED); - String accountSelector = Mailbox.ACCOUNT_KEY + "=" + account.mId; - ContentResolver resolver = context.getContentResolver(); - resolver.update(Mailbox.CONTENT_URI, values, accountSelector, null); - // Fix up keys and flags - MailboxUtilities.fixupUninitializedParentKeys(context, accountSelector); - // Clear the temporary flag - endMailboxChanges(context, accountId); - } - } - - private static final String[] HIERARCHY_PROJECTION = new String[] { - MailboxColumns.ID, MailboxColumns.DISPLAY_NAME, MailboxColumns.PARENT_KEY, - MailboxColumns.HIERARCHICAL_NAME - }; - private static final int HIERARCHY_ID = 0; - private static final int HIERARCHY_NAME = 1; - private static final int HIERARCHY_PARENT_KEY = 2; - private static final int HIERARCHY_HIERARCHICAL_NAME = 3; - - private static String getHierarchicalName(Context context, long id, HashMap map, - String name, long parentId) { - String hierarchicalName; - if (map.containsKey(id)) { - return map.get(id); - } else if (parentId == Mailbox.NO_MAILBOX) { - hierarchicalName = name; - } else { - Mailbox parent = Mailbox.restoreMailboxWithId(context, parentId); - if (parent == null) return name + "/" + "??"; - hierarchicalName = getHierarchicalName(context, parentId, map, parent.mDisplayName, - parent.mParentKey) + "/" + name; - } - map.put(id, hierarchicalName); - return hierarchicalName; - } - - public static void setupHierarchicalNames(Context context, long accountId) { - Account account = Account.restoreAccountWithId(context, accountId); - if (account == null) return; - // Start by clearing all names - ContentValues values = new ContentValues(); - String accountSelector = Mailbox.ACCOUNT_KEY + "=" + account.mId; - ContentResolver resolver = context.getContentResolver(); - HashMap nameMap = new HashMap(); - Cursor c = resolver.query(Mailbox.CONTENT_URI, HIERARCHY_PROJECTION, accountSelector, - null, null); - try { - while(c.moveToNext()) { - long id = c.getLong(HIERARCHY_ID); - String displayName = c.getString(HIERARCHY_NAME); - String name = getHierarchicalName(context, id, nameMap, displayName, - c.getLong(HIERARCHY_PARENT_KEY)); - String oldHierarchicalName = c.getString(HIERARCHY_HIERARCHICAL_NAME); - // Don't write the name unless it has changed or we don't need one (it's top-level) - if (name.equals(oldHierarchicalName) || - ((name.equals(displayName)) && TextUtils.isEmpty(oldHierarchicalName))) { - continue; - } - // If the name has changed, update it - values.put(MailboxColumns.HIERARCHICAL_NAME, name); - resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, id), values, null, - null); - } - } finally { - c.close(); - } - } -} diff --git a/emailcommon/src/com/android/emailcommon/provider/Policy.java b/emailcommon/src/com/android/emailcommon/provider/Policy.java index 0c2410d34..d43290f9f 100755 --- a/emailcommon/src/com/android/emailcommon/provider/Policy.java +++ b/emailcommon/src/com/android/emailcommon/provider/Policy.java @@ -40,11 +40,8 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol public static final String TAG = "Email/Policy"; public static final String TABLE_NAME = "Policy"; - public static Uri CONTENT_URI; - - public static void initPolicy() { - CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/policy"); - } + @SuppressWarnings("hiding") + public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/policy"); /* Convert days to mSec (used for password expiration) */ private static final long DAYS_TO_MSEC = 24 * 60 * 60 * 1000; diff --git a/emailcommon/src/com/android/emailcommon/provider/QuickResponse.java b/emailcommon/src/com/android/emailcommon/provider/QuickResponse.java index 3df082442..e88e01d95 100644 --- a/emailcommon/src/com/android/emailcommon/provider/QuickResponse.java +++ b/emailcommon/src/com/android/emailcommon/provider/QuickResponse.java @@ -17,6 +17,10 @@ package com.android.emailcommon.provider; +import com.android.emailcommon.provider.EmailContent; +import com.android.emailcommon.provider.EmailContent.QuickResponseColumns; +import com.google.common.base.Objects; + import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; @@ -25,9 +29,6 @@ import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; -import com.android.emailcommon.provider.EmailContent.QuickResponseColumns; -import com.google.common.base.Objects; - /** * A user-modifiable message that may be quickly inserted into the body while user is composing * a message. Tied to a specific account. @@ -35,13 +36,11 @@ import com.google.common.base.Objects; public final class QuickResponse extends EmailContent implements QuickResponseColumns, Parcelable { public static final String TABLE_NAME = "QuickResponse"; - public static Uri CONTENT_URI; - public static Uri ACCOUNT_ID_URI; - - public static void initQuickResponse() { - CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/quickresponse"); - ACCOUNT_ID_URI = Uri.parse(EmailContent.CONTENT_URI + "/quickresponse/account"); - } + @SuppressWarnings("hiding") + public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + + "/quickresponse"); + public static final Uri ACCOUNT_ID_URI = Uri.parse( + EmailContent.CONTENT_URI + "/quickresponse/account"); private String mText; private long mAccountKey; diff --git a/emailcommon/src/com/android/emailcommon/service/AccountServiceProxy.java b/emailcommon/src/com/android/emailcommon/service/AccountServiceProxy.java index 1667295cb..f4eb93009 100644 --- a/emailcommon/src/com/android/emailcommon/service/AccountServiceProxy.java +++ b/emailcommon/src/com/android/emailcommon/service/AccountServiceProxy.java @@ -24,13 +24,14 @@ import android.os.RemoteException; public class AccountServiceProxy extends ServiceProxy implements IAccountService { + public static final String ACCOUNT_INTENT = "com.android.email.ACCOUNT_INTENT"; public static final int DEFAULT_ACCOUNT_COLOR = 0xFF0000FF; private IAccountService mService = null; private Object mReturn; public AccountServiceProxy(Context _context) { - super(_context, getIntentForEmailPackage(_context, "ACCOUNT_INTENT")); + super(_context, new Intent(ACCOUNT_INTENT)); } @Override @@ -44,11 +45,11 @@ public class AccountServiceProxy extends ServiceProxy implements IAccountService } @Override - public void notifyLoginFailed(final long accountId, final String reason) { + public void notifyLoginFailed(final long accountId) { setTask(new ProxyTask() { @Override public void run() throws RemoteException { - mService.notifyLoginFailed(accountId, reason); + mService.notifyLoginFailed(accountId); } }, "notifyLoginFailed"); } diff --git a/emailcommon/src/com/android/emailcommon/service/EmailServiceCallback.java b/emailcommon/src/com/android/emailcommon/service/EmailServiceCallback.java deleted file mode 100644 index e00e5a6ca..000000000 --- a/emailcommon/src/com/android/emailcommon/service/EmailServiceCallback.java +++ /dev/null @@ -1,117 +0,0 @@ -/* Copyright (C) 2012 The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.emailcommon.service; - -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.util.Log; - -import com.android.emailcommon.service.IEmailServiceCallback.Stub; - -public class EmailServiceCallback extends Stub { - - private final RemoteCallbackList mCallbackList; - - public EmailServiceCallback(RemoteCallbackList callbackList) { - mCallbackList = callbackList; - } - /** - * Broadcast a callback to the everyone that's registered - * - * @param wrapper the ServiceCallbackWrapper used in the broadcast - */ - private synchronized void broadcastCallback(ServiceCallbackWrapper wrapper) { - RemoteCallbackList callbackList = mCallbackList; - if (callbackList != null) { - // Call everyone on our callback list - int count = callbackList.beginBroadcast(); - try { - for (int i = 0; i < count; i++) { - try { - wrapper.call(callbackList.getBroadcastItem(i)); - } catch (RemoteException e) { - // Safe to ignore - } catch (RuntimeException e) { - // We don't want an exception in one call to prevent other calls, so - // we'll just log this and continue - Log.e("EmailServiceCallback", "Caught RuntimeException in broadcast", e); - } - } - } finally { - // No matter what, we need to finish the broadcast - callbackList.finishBroadcast(); - } - } - } - - @Override - public void loadAttachmentStatus(final long messageId, final long attachmentId, - final int status, final int progress) { - broadcastCallback(new ServiceCallbackWrapper() { - @Override - public void call(IEmailServiceCallback cb) throws RemoteException { - cb.loadAttachmentStatus(messageId, attachmentId, status, progress); - } - }); - } - - @Override - public void loadMessageStatus(final long messageId, final int status, final int progress) { - broadcastCallback(new ServiceCallbackWrapper() { - @Override - public void call(IEmailServiceCallback cb) throws RemoteException { - cb.loadMessageStatus(messageId, status, progress); - } - }); - } - - @Override - public void sendMessageStatus(final long messageId, final String subject, final int status, - final int progress) { - broadcastCallback(new ServiceCallbackWrapper() { - @Override - public void call(IEmailServiceCallback cb) throws RemoteException { - cb.sendMessageStatus(messageId, subject, status, progress); - } - }); - } - - @Override - public void syncMailboxListStatus(final long accountId, final int status, - final int progress) { - broadcastCallback(new ServiceCallbackWrapper() { - @Override - public void call(IEmailServiceCallback cb) throws RemoteException { - cb.syncMailboxListStatus(accountId, status, progress); - } - }); - } - - @Override - public void syncMailboxStatus(final long mailboxId, final int status, - final int progress) { - broadcastCallback(new ServiceCallbackWrapper() { - @Override - public void call(IEmailServiceCallback cb) throws RemoteException { - cb.syncMailboxStatus(mailboxId, status, progress); - } - }); - } - - private interface ServiceCallbackWrapper { - public void call(IEmailServiceCallback cb) throws RemoteException; - } -} diff --git a/emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java b/emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java index c7c77b897..d95642559 100644 --- a/emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java +++ b/emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java @@ -27,7 +27,6 @@ import com.android.emailcommon.Api; import com.android.emailcommon.Device; import com.android.emailcommon.TempDirectory; import com.android.emailcommon.mail.MessagingException; -import com.android.emailcommon.provider.Account; import com.android.emailcommon.provider.HostAuth; import com.android.emailcommon.provider.Policy; @@ -51,6 +50,10 @@ import java.io.IOException; public class EmailServiceProxy extends ServiceProxy implements IEmailService { private static final String TAG = "EmailServiceProxy"; + // Private intent that will be used to connect to an independent Exchange service + public static final String EXCHANGE_INTENT = "com.android.email.EXCHANGE_INTENT"; + public static final String IMAP_INTENT = "com.android.email.IMAP_INTENT"; + public static final String AUTO_DISCOVER_BUNDLE_ERROR_CODE = "autodiscover_error_code"; public static final String AUTO_DISCOVER_BUNDLE_HOST_AUTH = "autodiscover_host_auth"; @@ -411,7 +414,9 @@ public class EmailServiceProxy extends ServiceProxy implements IEmailService { }, "deleteAccountPIMData"); } + /** + * PRELIMINARY * Search for messages given a query string. The string is interpreted as the logical AND of * terms separated by white space. The search is performed on the specified mailbox in the * specified account (including subfolders, as specified by the includeSubfolders parameter). @@ -459,40 +464,6 @@ public class EmailServiceProxy extends ServiceProxy implements IEmailService { }, "sendMail"); } - @Override - public int getCapabilities(final Account acct) throws RemoteException { - setTask(new ProxyTask() { - @Override - public void run() throws RemoteException{ - if (mCallback != null) mService.setCallback(mCallback); - mReturn = mService.getCapabilities(acct); - } - }, "getCapabilities"); - waitForCompletion(); - if (mReturn == null) { - return 0; - } else { - return (Integer)mReturn; - } - } - /** - * Request that the account be updated for this service; this call is synchronous - * - * @param the email address of the account to be updated - */ - @Override - public void serviceUpdated(final String emailAddress) throws RemoteException { - setTask(new ProxyTask() { - @Override - public void run() throws RemoteException{ - if (mCallback != null) mService.setCallback(mCallback); - mService.serviceUpdated(emailAddress); - } - }, "settingsUpdate"); - waitForCompletion(); - } - - @Override public IBinder asBinder() { return null; diff --git a/emailcommon/src/com/android/emailcommon/service/IAccountService.aidl b/emailcommon/src/com/android/emailcommon/service/IAccountService.aidl index d456862b0..a29baf58c 100644 --- a/emailcommon/src/com/android/emailcommon/service/IAccountService.aidl +++ b/emailcommon/src/com/android/emailcommon/service/IAccountService.aidl @@ -19,7 +19,7 @@ package com.android.emailcommon.service; import android.os.Bundle; interface IAccountService { - oneway void notifyLoginFailed(long accountId, String reason); + oneway void notifyLoginFailed(long accountId); oneway void notifyLoginSucceeded(long accountId); void reconcileAccounts(String protocol, String accountManagerType); diff --git a/emailcommon/src/com/android/emailcommon/service/IEmailService.aidl b/emailcommon/src/com/android/emailcommon/service/IEmailService.aidl index 43c52eae7..cd5cd07c3 100644 --- a/emailcommon/src/com/android/emailcommon/service/IEmailService.aidl +++ b/emailcommon/src/com/android/emailcommon/service/IEmailService.aidl @@ -18,7 +18,6 @@ package com.android.emailcommon.service; import com.android.emailcommon.provider.HostAuth; -import com.android.emailcommon.provider.Account; import com.android.emailcommon.service.IEmailServiceCallback; import com.android.emailcommon.service.SearchParams; import android.os.Bundle; @@ -60,9 +59,4 @@ interface IEmailService { int searchMessages(long accountId, in SearchParams params, long destMailboxId); void sendMail(long accountId); - - // API level 3 - int getCapabilities(in Account acct); - - void serviceUpdated(String emailAddress); } diff --git a/emailcommon/src/com/android/emailcommon/service/PolicyServiceProxy.java b/emailcommon/src/com/android/emailcommon/service/PolicyServiceProxy.java index 78a354329..26e820dee 100755 --- a/emailcommon/src/com/android/emailcommon/service/PolicyServiceProxy.java +++ b/emailcommon/src/com/android/emailcommon/service/PolicyServiceProxy.java @@ -17,6 +17,7 @@ package com.android.emailcommon.service; import android.content.Context; +import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -28,11 +29,14 @@ public class PolicyServiceProxy extends ServiceProxy implements IPolicyService { private static final boolean DEBUG_PROXY = false; // DO NOT CHECK THIS IN SET TO TRUE private static final String TAG = "PolicyServiceProxy"; + // The intent used by sync adapter services to connect to the PolicyService + public static final String POLICY_INTENT = "com.android.email.POLICY_INTENT"; + private IPolicyService mService = null; private Object mReturn = null; public PolicyServiceProxy(Context _context) { - super(_context, getIntentForEmailPackage(_context, "POLICY_INTENT")); + super(_context, new Intent(POLICY_INTENT)); } @Override diff --git a/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java b/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java index e4ac3001a..8e3bcffb8 100644 --- a/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java +++ b/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java @@ -56,27 +56,6 @@ public abstract class ServiceProxy { private long mStartTime; private boolean mDead = false; - public static Intent getIntentForEmailPackage(Context context, String actionName) { - return new Intent(getIntentStringForEmailPackage(context, actionName)); - } - - /** - * Create Intent action based on the Email package name - * Package com.android.email + ACTION -> com.android.email.ACTION - * Package com.google.android.email + ACTION -> com.google.android.email.ACTION - * Package com.android.exchange + ACTION -> com.android.email.ACTION - * Package com.google.exchange + ACTION -> com.google.android.email.ACTION - * - * @param context the caller's context - * @param actionName the Intent action - * @return an Intent action based on the package name - */ - public static String getIntentStringForEmailPackage(Context context, String actionName) { - String packageName = context.getPackageName(); - int lastDot = packageName.lastIndexOf('.'); - return packageName.substring(0, lastDot + 1) + "email." + actionName; - } - public abstract void onConnected(IBinder binder); public ServiceProxy(Context _context, Intent _intent) { @@ -92,8 +71,7 @@ public abstract class ServiceProxy { public void onServiceConnected(ComponentName name, IBinder binder) { onConnected(binder); if (DEBUG_PROXY) { - Log.v(mTag, "Connected: " + name.getShortClassName() + " at " + - (System.currentTimeMillis() - mStartTime) + "ms"); + Log.v(mTag, "Connected: " + name.getShortClassName()); } // Run our task on a new thread new Thread(new Runnable() { @@ -108,8 +86,7 @@ public abstract class ServiceProxy { public void onServiceDisconnected(ComponentName name) { if (DEBUG_PROXY) { - Log.v(mTag, "Disconnected: " + name.getShortClassName() + " at " + - (System.currentTimeMillis() - mStartTime) + "ms"); + Log.v(mTag, "Disconnected: " + name.getShortClassName()); } } } @@ -192,9 +169,8 @@ public abstract class ServiceProxy { // Can be ignored safely } if (DEBUG_PROXY) { - Log.v(mTag, "Wait for " + mName + (mDead ? " finished in " : " timed out in ") + + Log.v(mTag, "Wait for " + mName + " finished in " + (System.currentTimeMillis() - time) + "ms"); - mDead = true; } } } diff --git a/emailcommon/src/com/android/emailcommon/service/SyncWindow.java b/emailcommon/src/com/android/emailcommon/service/SyncWindow.java index 3863e4f3f..52839b204 100644 --- a/emailcommon/src/com/android/emailcommon/service/SyncWindow.java +++ b/emailcommon/src/com/android/emailcommon/service/SyncWindow.java @@ -26,25 +26,4 @@ public class SyncWindow { public static final int SYNC_WINDOW_2_WEEKS = 4; public static final int SYNC_WINDOW_1_MONTH = 5; public static final int SYNC_WINDOW_ALL = 6; - - public static int toDays(int window) { - switch(window) { - case SYNC_WINDOW_1_DAY: - return 1; - case SYNC_WINDOW_3_DAYS: - return 3; - case SYNC_WINDOW_1_WEEK: - return 7; - case SYNC_WINDOW_2_WEEKS: - return 14; - case SYNC_WINDOW_1_MONTH: - return 30; - case SYNC_WINDOW_ALL: - return 365*10; - case SYNC_WINDOW_UNKNOWN: - case SYNC_WINDOW_AUTO: - default: - return 14; - } - } } diff --git a/emailcommon/src/com/android/emailcommon/utility/AttachmentUtilities.java b/emailcommon/src/com/android/emailcommon/utility/AttachmentUtilities.java index 7040eaa47..4dc21610a 100644 --- a/emailcommon/src/com/android/emailcommon/utility/AttachmentUtilities.java +++ b/emailcommon/src/com/android/emailcommon/utility/AttachmentUtilities.java @@ -32,8 +32,6 @@ import android.webkit.MimeTypeMap; import com.android.emailcommon.Logging; import com.android.emailcommon.provider.EmailContent.Attachment; import com.android.emailcommon.provider.EmailContent.AttachmentColumns; -import com.android.emailcommon.provider.EmailContent.Body; -import com.android.emailcommon.provider.EmailContent.BodyColumns; import com.android.emailcommon.provider.EmailContent.Message; import com.android.emailcommon.provider.EmailContent.MessageColumns; import com.android.mail.providers.UIProvider; @@ -44,9 +42,10 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; public class AttachmentUtilities { + public static final String AUTHORITY = "com.android.email.attachmentprovider"; + public static final Uri CONTENT_URI = Uri.parse( "content://" + AUTHORITY); public static final String FORMAT_RAW = "RAW"; public static final String FORMAT_THUMBNAIL = "THUMBNAIL"; @@ -135,16 +134,23 @@ public class AttachmentUtilities { */ public static final int MAX_ATTACHMENT_UPLOAD_SIZE = (5 * 1024 * 1024); - private static Uri sUri; public static Uri getAttachmentUri(long accountId, long id) { - if (sUri == null) { - sUri = Uri.parse(Attachment.ATTACHMENT_PROVIDER_URI_PREFIX); - } - return sUri.buildUpon() - .appendPath(Long.toString(accountId)) - .appendPath(Long.toString(id)) - .appendPath(FORMAT_RAW) - .build(); + return CONTENT_URI.buildUpon() + .appendPath(Long.toString(accountId)) + .appendPath(Long.toString(id)) + .appendPath(FORMAT_RAW) + .build(); + } + + public static Uri getAttachmentThumbnailUri(long accountId, long id, + int width, int height) { + return CONTENT_URI.buildUpon() + .appendPath(Long.toString(accountId)) + .appendPath(Long.toString(id)) + .appendPath(FORMAT_THUMBNAIL) + .appendPath(Integer.toString(width)) + .appendPath(Integer.toString(height)) + .build(); } /** @@ -229,6 +235,24 @@ public class AttachmentUtilities { return resultType.toLowerCase(); } + /** + * @return mime-type for a {@link Uri}. + * - Use {@link ContentResolver#getType} for a content: URI. + * - Use {@link #inferMimeType} for a file: URI. + * - Otherwise returns null. + */ + public static String inferMimeTypeForUri(Context context, Uri uri) { + final String scheme = uri.getScheme(); + if (ContentResolver.SCHEME_CONTENT.equals(scheme)) { + return context.getContentResolver().getType(uri); + } else if (ContentResolver.SCHEME_FILE.equals(scheme)) { + return inferMimeType(uri.getLastPathSegment(), ""); + } else { + Log.e(Logging.LOG_TAG, "Unable to determine MIME type for uri=" + uri, new Error()); + return null; + } + } + /** * Extract and return filename's extension, converted to lower case, and not including the "." * @@ -338,7 +362,8 @@ public class AttachmentUtilities { } } - private static long copyFile(InputStream in, OutputStream out) throws IOException { + private static long copyFile(InputStream in, File file) throws IOException { + FileOutputStream out = new FileOutputStream(file); long size = IOUtils.copy(in, out); in.close(); out.flush(); @@ -354,20 +379,24 @@ public class AttachmentUtilities { ContentValues cv = new ContentValues(); long attachmentId = attachment.mId; long accountId = attachment.mAccountKey; - String contentUri = null; + String contentUri; long size; try { - ContentResolver resolver = context.getContentResolver(); if (attachment.mUiDestination == UIProvider.AttachmentDestination.CACHE) { - Uri attUri = getAttachmentUri(accountId, attachmentId); - size = copyFile(in, resolver.openOutputStream(attUri)); - contentUri = attUri.toString(); + File saveIn = getAttachmentDirectory(context, accountId); + if (!saveIn.exists()) { + saveIn.mkdirs(); + } + File file = getAttachmentFilename(context, accountId, attachmentId); + file.createNewFile(); + size = copyFile(in, file); + contentUri = getAttachmentUri(accountId, attachmentId).toString(); } else if (Utility.isExternalStorageMounted()) { File downloads = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_DOWNLOADS); downloads.mkdirs(); File file = Utility.createUniqueFile(downloads, attachment.mFileName); - size = copyFile(in, new FileOutputStream(file)); + size = copyFile(in, file); String absolutePath = file.getAbsolutePath(); // Although the download manager can scan media files, scanning only happens @@ -400,20 +429,5 @@ public class AttachmentUtilities { } context.getContentResolver().update(uri, cv, null, null); - // If this is an inline attachment, update the body - if (contentUri != null && attachment.mContentId != null) { - Body body = Body.restoreBodyWithMessageId(context, attachment.mMessageKey); - if (body != null && body.mHtmlContent != null) { - cv.clear(); - String html = body.mHtmlContent; - String contentIdRe = - "\\s+(?i)src=\"cid(?-i):\\Q" + attachment.mContentId + "\\E\""; - String srcContentUri = " src=\"" + contentUri + "\""; - html = html.replaceAll(contentIdRe, srcContentUri); - cv.put(BodyColumns.HTML_CONTENT, html); - context.getContentResolver().update( - ContentUris.withAppendedId(Body.CONTENT_URI, body.mId), cv, null, null); - } - } } } diff --git a/emailcommon/src/com/android/emailcommon/utility/EmailClientConnectionManager.java b/emailcommon/src/com/android/emailcommon/utility/EmailClientConnectionManager.java index 868150714..15d1cca8a 100644 --- a/emailcommon/src/com/android/emailcommon/utility/EmailClientConnectionManager.java +++ b/emailcommon/src/com/android/emailcommon/utility/EmailClientConnectionManager.java @@ -42,10 +42,9 @@ import javax.net.ssl.KeyManager; */ public class EmailClientConnectionManager extends ThreadSafeClientConnManager { + private static final boolean LOG_ENABLED = false; private static final int STANDARD_PORT = 80; private static final int STANDARD_SSL_PORT = 443; - private static final boolean LOG_ENABLED = false; - /** * A {@link KeyManager} to track client certificate requests from servers. */ @@ -60,11 +59,9 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager { mTrackingKeyManager = keyManager; } - public static EmailClientConnectionManager newInstance(Context context, HttpParams params, - HostAuth hostAuth) { + public static EmailClientConnectionManager newInstance(HttpParams params, boolean ssl, + int port) { TrackingKeyManager keyManager = new TrackingKeyManager(); - boolean ssl = hostAuth.shouldUseSsl(); - int port = hostAuth.mPort; // Create a registry for our three schemes; http and https will use built-in factories SchemeRegistry registry = new SchemeRegistry(); @@ -72,11 +69,10 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager { ssl ? STANDARD_PORT : port)); // Register https with the secure factory registry.register(new Scheme("https", - SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, false), - ssl ? port : STANDARD_SSL_PORT)); + SSLUtils.getHttpSocketFactory(false, keyManager), ssl ? port : STANDARD_SSL_PORT)); // Register the httpts scheme with our insecure factory registry.register(new Scheme("httpts", - SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, true), + SSLUtils.getHttpSocketFactory(true /*insecure*/, keyManager), ssl ? port : STANDARD_SSL_PORT)); return new EmailClientConnectionManager(params, registry, keyManager); @@ -102,10 +98,11 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager { } KeyManager keyManager = KeyChainKeyManager.fromAlias(context, hostAuth.mClientCertAlias); - boolean insecure = hostAuth.shouldTrustAllServerCerts(); - SSLSocketFactory ssf = - SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, insecure); - registry.register(new Scheme(schemeName, ssf, hostAuth.mPort)); + SSLCertificateSocketFactory underlying = SSLUtils.getSSLSocketFactory( + hostAuth.shouldTrustAllServerCerts()); + underlying.setKeyManagers(new KeyManager[] { keyManager }); + registry.register( + new Scheme(schemeName, new SSLSocketFactory(underlying), hostAuth.mPort)); } } diff --git a/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java b/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java index a85049072..b21c68f33 100644 --- a/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java +++ b/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java @@ -16,139 +16,46 @@ package com.android.emailcommon.utility; -import android.content.ContentUris; -import android.content.ContentValues; import android.content.Context; -import android.database.Cursor; import android.net.SSLCertificateSocketFactory; import android.security.KeyChain; import android.security.KeyChainException; import android.util.Log; -import com.android.emailcommon.provider.EmailContent.HostAuthColumns; -import com.android.emailcommon.provider.HostAuth; import com.google.common.annotations.VisibleForTesting; -import java.io.ByteArrayInputStream; -import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.security.Principal; import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.cert.Certificate; import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Arrays; import javax.net.ssl.KeyManager; -import javax.net.ssl.TrustManager; import javax.net.ssl.X509ExtendedKeyManager; -import javax.net.ssl.X509TrustManager; public class SSLUtils { - // All secure factories are the same; all insecure factories are associated with HostAuth's + private static SSLCertificateSocketFactory sInsecureFactory; private static SSLCertificateSocketFactory sSecureFactory; private static final boolean LOG_ENABLED = false; private static final String TAG = "Email.Ssl"; - /** - * A trust manager specific to a particular HostAuth. The first time a server certificate is - * encountered for the HostAuth, its certificate is saved; subsequent checks determine whether - * the PublicKey of the certificate presented matches that of the saved certificate - * TODO: UI to ask user about changed certificates - */ - private static class SameCertificateCheckingTrustManager implements X509TrustManager { - private final HostAuth mHostAuth; - private final Context mContext; - // The public key associated with the HostAuth; we'll lazily initialize it - private PublicKey mPublicKey; - - SameCertificateCheckingTrustManager(Context context, HostAuth hostAuth) { - mContext = context; - mHostAuth = hostAuth; - // We must load the server cert manually (the ContentCache won't handle blobs - Cursor c = context.getContentResolver().query(HostAuth.CONTENT_URI, - new String[] {HostAuthColumns.SERVER_CERT}, HostAuth.ID + "=?", - new String[] {Long.toString(hostAuth.mId)}, null); - if (c != null) { - try { - if (c.moveToNext()) { - mHostAuth.mServerCert = c.getBlob(0); - } - } finally { - c.close(); - } - } - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - // We don't check client certificates - throw new CertificateException("We don't check client certificates"); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - if (chain.length == 0) { - throw new CertificateException("No certificates?"); - } else { - X509Certificate serverCert = chain[0]; - if (mHostAuth.mServerCert != null) { - // Compare with the current public key - if (mPublicKey == null) { - ByteArrayInputStream bais = new ByteArrayInputStream(mHostAuth.mServerCert); - Certificate storedCert = - CertificateFactory.getInstance("X509").generateCertificate(bais); - mPublicKey = storedCert.getPublicKey(); - try { - bais.close(); - } catch (IOException e) { - // Yeah, right. - } - } - if (!mPublicKey.equals(serverCert.getPublicKey())) { - throw new CertificateException( - "PublicKey has changed since initial connection!"); - } - } else { - // First time; save this away - byte[] encodedCert = serverCert.getEncoded(); - mHostAuth.mServerCert = encodedCert; - ContentValues values = new ContentValues(); - values.put(HostAuthColumns.SERVER_CERT, encodedCert); - mContext.getContentResolver().update( - ContentUris.withAppendedId(HostAuth.CONTENT_URI, mHostAuth.mId), - values, null, null); - } - } - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return null; - } - } - /** * Returns a {@link javax.net.ssl.SSLSocketFactory}. * Optionally bypass all SSL certificate checks. * * @param insecure if true, bypass all SSL certificate checks */ - public synchronized static SSLCertificateSocketFactory getSSLSocketFactory(Context context, - HostAuth hostAuth, boolean insecure) { + public synchronized static SSLCertificateSocketFactory getSSLSocketFactory( + boolean insecure) { if (insecure) { - SSLCertificateSocketFactory insecureFactory = (SSLCertificateSocketFactory) - SSLCertificateSocketFactory.getDefault(0, null); - insecureFactory.setTrustManagers( - new TrustManager[] { - new SameCertificateCheckingTrustManager(context, hostAuth)}); - return insecureFactory; + if (sInsecureFactory == null) { + sInsecureFactory = (SSLCertificateSocketFactory) + SSLCertificateSocketFactory.getInsecure(0, null); + } + return sInsecureFactory; } else { if (sSecureFactory == null) { sSecureFactory = (SSLCertificateSocketFactory) @@ -162,9 +69,8 @@ public class SSLUtils { * Returns a {@link org.apache.http.conn.ssl.SSLSocketFactory SSLSocketFactory} for use with the * Apache HTTP stack. */ - public static SSLSocketFactory getHttpSocketFactory(Context context, HostAuth hostAuth, - KeyManager keyManager, boolean insecure) { - SSLCertificateSocketFactory underlying = getSSLSocketFactory(context, hostAuth, insecure); + public static SSLSocketFactory getHttpSocketFactory(boolean insecure, KeyManager keyManager) { + SSLCertificateSocketFactory underlying = getSSLSocketFactory(insecure); if (keyManager != null) { underlying.setKeyManagers(new KeyManager[] { keyManager }); } diff --git a/emailcommon/src/com/android/emailcommon/utility/Utility.java b/emailcommon/src/com/android/emailcommon/utility/Utility.java index 28d0b41e9..d05760079 100644 --- a/emailcommon/src/com/android/emailcommon/utility/Utility.java +++ b/emailcommon/src/com/android/emailcommon/utility/Utility.java @@ -52,6 +52,7 @@ import com.android.emailcommon.provider.EmailContent.AttachmentColumns; import com.android.emailcommon.provider.EmailContent.HostAuthColumns; import com.android.emailcommon.provider.EmailContent.MailboxColumns; import com.android.emailcommon.provider.EmailContent.Message; +import com.android.emailcommon.provider.EmailContent.MessageColumns; import com.android.emailcommon.provider.HostAuth; import com.android.emailcommon.provider.Mailbox; import com.android.emailcommon.provider.ProviderUnavailableException; @@ -363,7 +364,6 @@ public class Utility { cal.setTimeZone(TimeZone.getTimeZone("GMT")); return cal; } - /** * Generate a time in milliseconds from an email date string that represents a date/time in GMT * @param date string in format 2010-02-23T16:00:00.000Z (ISO 8601, rfc3339) @@ -745,28 +745,25 @@ public class Utility { return false; } else if (attachment.mContentBytes != null) { return true; - } else { - String contentUri = attachment.getContentUri(); - if (TextUtils.isEmpty(contentUri)) { - return false; - } + } else if (TextUtils.isEmpty(attachment.mContentUri)) { + return false; + } + try { + Uri fileUri = Uri.parse(attachment.mContentUri); try { - Uri fileUri = Uri.parse(contentUri); + InputStream inStream = context.getContentResolver().openInputStream(fileUri); try { - InputStream inStream = context.getContentResolver().openInputStream(fileUri); - try { - inStream.close(); - } catch (IOException e) { - // Nothing to be done if can't close the stream - } - return true; - } catch (FileNotFoundException e) { - return false; + inStream.close(); + } catch (IOException e) { + // Nothing to be done if can't close the stream } - } catch (RuntimeException re) { - Log.w(Logging.LOG_TAG, "attachmentExists RuntimeException=" + re); + return true; + } catch (FileNotFoundException e) { return false; } + } catch (RuntimeException re) { + Log.w(Logging.LOG_TAG, "attachmentExists RuntimeException=" + re); + return false; } } @@ -792,18 +789,8 @@ public class Utility { Attachment.FLAG_DOWNLOAD_USER_REQUEST)) == 0) { Log.d(Logging.LOG_TAG, "Unloaded attachment isn't marked for download: " + att.mFileName + ", #" + att.mId); - Account acct = Account.restoreAccountWithId(context, msg.mAccountKey); - if (acct == null) return true; - // If smart forward is set and the message is a forward, we'll act as though - // the attachment has been loaded - // In Email1 this test wasn't necessary, as the UI handled it... - if ((msg.mFlags & Message.FLAG_TYPE_FORWARD) != 0) { - if ((acct.mFlags & Account.FLAGS_SUPPORTS_SMART_FORWARD) != 0) { - continue; - } - } Attachment.delete(context, Attachment.CONTENT_URI, att.mId); - } else if (att.getContentUri() != null) { + } else if (att.mContentUri != null) { // In this case, the attachment file is gone from the cache; let's clear the // contentUri; this should be a very unusual case ContentValues cv = new ContentValues(); @@ -1163,4 +1150,72 @@ public class Utility { sb.append(')'); return sb.toString(); } + + /** + * Updates the last seen message key in the mailbox data base for the INBOX of the currently + * shown account. If the account is {@link Account#ACCOUNT_ID_COMBINED_VIEW}, the INBOX for + * all accounts are updated. + * @return an {@link EmailAsyncTask} for test only. + */ + public static EmailAsyncTask updateLastSeenMessageKey(final Context context, + final long accountId) { + return EmailAsyncTask.runAsyncParallel(new Runnable() { + private void updateLastSeenMessageKeyForAccount(long accountId) { + ContentResolver resolver = context.getContentResolver(); + if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) { + Cursor c = resolver.query( + Account.CONTENT_URI, EmailContent.ID_PROJECTION, null, null, null); + if (c == null) throw new ProviderUnavailableException(); + try { + while (c.moveToNext()) { + final long id = c.getLong(EmailContent.ID_PROJECTION_COLUMN); + updateLastSeenMessageKeyForAccount(id); + } + } finally { + c.close(); + } + } else if (accountId > 0L) { + Mailbox mailbox = + Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_INBOX); + + // mailbox has been removed + if (mailbox == null) { + return; + } + // We use the highest _id for the account the mailbox table as the "last seen + // message key". We don't care if the message has been read or not. We only + // need a point at which we can compare against in the future. By setting this + // value, we are claiming that every message before this has potentially been + // seen by the user. + long messageId = Utility.getFirstRowLong( + context, + Message.CONTENT_URI, + EmailContent.ID_PROJECTION, + MessageColumns.MAILBOX_KEY + "=?", + new String[] { Long.toString(mailbox.mId) }, + MessageColumns.ID + " DESC", + EmailContent.ID_PROJECTION_COLUMN, 0L); + long oldLastSeenMessageId = Utility.getFirstRowLong( + context, ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailbox.mId), + new String[] { MailboxColumns.LAST_SEEN_MESSAGE_KEY }, + null, null, null, 0, 0L); + // Only update the db if the value has changed + if (messageId != oldLastSeenMessageId) { + ContentValues values = mailbox.toContentValues(); + values.put(MailboxColumns.LAST_SEEN_MESSAGE_KEY, messageId); + resolver.update( + Mailbox.CONTENT_URI, + values, + EmailContent.ID_SELECTION, + new String[] { Long.toString(mailbox.mId) }); + } + } + } + + @Override + public void run() { + updateLastSeenMessageKeyForAccount(accountId); + } + }); + } } diff --git a/emailsync/Android.mk b/emailsync/Android.mk deleted file mode 100644 index 0076c5d72..000000000 --- a/emailsync/Android.mk +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2012, The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -# Build the com.android.emailcommon static library. At the moment, this includes -# the emailcommon files themselves plus everything under src/org (apache code). All of our -# AIDL files are also compiled into the static library - -include $(CLEAR_VARS) - - -LOCAL_MODULE := com.android.emailsync -LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/emailsync) -LOCAL_STATIC_JAVA_LIBRARIES := com.android.emailcommon2 - -LOCAL_SDK_VERSION := 14 - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/emailsync/src/com/android/emailsync/AbstractSyncService.java b/emailsync/src/com/android/emailsync/AbstractSyncService.java deleted file mode 100644 index f64224feb..000000000 --- a/emailsync/src/com/android/emailsync/AbstractSyncService.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (C) 2008-2009 Marc Blank - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.emailsync; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Bundle; -import android.util.Log; - -import com.android.emailcommon.provider.Account; -import com.android.emailcommon.provider.HostAuth; -import com.android.emailcommon.provider.Mailbox; - -import java.util.concurrent.LinkedBlockingQueue; - -/** - * Base class for all protocol services SyncManager (extends Service, implements - * Runnable) instantiates subclasses to run a sync (either timed, or push, or - * mail placed in outbox, etc.) EasSyncService is currently implemented; my goal - * would be to move IMAP to this structure when it comes time to introduce push - * functionality. - */ -public abstract class AbstractSyncService implements Runnable { - - public String TAG = "AbstractSyncService"; - - public static final int SECONDS = 1000; - public static final int MINUTES = 60*SECONDS; - public static final int HOURS = 60*MINUTES; - public static final int DAYS = 24*HOURS; - - public static final int CONNECT_TIMEOUT = 30*SECONDS; - public static final int NETWORK_WAIT = 15*SECONDS; - - public static final int EXIT_DONE = 0; - public static final int EXIT_IO_ERROR = 1; - public static final int EXIT_LOGIN_FAILURE = 2; - public static final int EXIT_EXCEPTION = 3; - public static final int EXIT_SECURITY_FAILURE = 4; - public static final int EXIT_ACCESS_DENIED = 5; - - public Mailbox mMailbox; - protected long mMailboxId; - protected int mExitStatus = EXIT_EXCEPTION; - protected String mExitReason; - protected String mMailboxName; - public Account mAccount; - public Context mContext; - public int mChangeCount = 0; - public volatile int mSyncReason = 0; - protected volatile boolean mStop = false; - public volatile Thread mThread; - protected final Object mSynchronizer = new Object(); - // Whether or not the sync service is valid (usable) - public boolean mIsValid = true; - - public boolean mUserLog = true; // STOPSHIP - public boolean mFileLog = false; - - protected volatile long mRequestTime = 0; - protected LinkedBlockingQueue mRequestQueue = new LinkedBlockingQueue(); - - /** - * Sent by SyncManager to request that the service stop itself cleanly - */ - public abstract void stop(); - - /** - * Sent by SyncManager to indicate that an alarm has fired for this service, and that its - * pending (network) operation has timed out. The service is NOT automatically stopped, - * although the behavior is service dependent. - * - * @return true if the operation was stopped normally; false if the thread needed to be - * interrupted. - */ - public abstract boolean alarm(); - - /** - * Sent by SyncManager to request that the service reset itself cleanly; the meaning of this - * operation is service dependent. - */ - public abstract void reset(); - - /** - * Called to validate an account; abstract to allow each protocol to do what - * is necessary. For consistency with the Email app's original - * functionality, success is indicated by a failure to throw an Exception - * (ugh). Parameters are self-explanatory - * - * @param hostAuth - * @return a Bundle containing a result code and, depending on the result, a PolicySet or an - * error message - */ - public abstract Bundle validateAccount(HostAuth hostAuth, Context context); - - /** - * Called to clear the syncKey for the calendar associated with this service; this is necessary - * because changes to calendar sync state cause a reset of data. - */ - public abstract void resetCalendarSyncKey(); - - public AbstractSyncService(Context _context, Mailbox _mailbox) { - mContext = _context; - mMailbox = _mailbox; - mMailboxId = _mailbox.mId; - mMailboxName = _mailbox.mServerId; - mAccount = Account.restoreAccountWithId(_context, _mailbox.mAccountKey); - } - - // Will be required when subclasses are instantiated by name - public AbstractSyncService(String prefix) { - } - - /** - * The UI can call this static method to perform account validation. This method wraps each - * protocol's validateAccount method. Arguments are self-explanatory, except where noted. - * - * @param klass the protocol class (EasSyncService.class for example) - * @param hostAuth - * @param context - * @return a Bundle containing a result code and, depending on the result, a PolicySet or an - * error message - */ - public static Bundle validate(Class klass, - HostAuth hostAuth, Context context) { - AbstractSyncService svc; - try { - svc = klass.newInstance(); - return svc.validateAccount(hostAuth, context); - } catch (IllegalAccessException e) { - } catch (InstantiationException e) { - } - return null; - } - - public static class ValidationResult { - static final int NO_FAILURE = 0; - static final int CONNECTION_FAILURE = 1; - static final int VALIDATION_FAILURE = 2; - static final int EXCEPTION = 3; - - static final ValidationResult succeeded = new ValidationResult(true, NO_FAILURE, null); - boolean success; - int failure = NO_FAILURE; - String reason = null; - Exception exception = null; - - ValidationResult(boolean _success, int _failure, String _reason) { - success = _success; - failure = _failure; - reason = _reason; - } - - ValidationResult(boolean _success) { - success = _success; - } - - ValidationResult(Exception e) { - success = false; - failure = EXCEPTION; - exception = e; - } - - public boolean isSuccess() { - return success; - } - - public String getReason() { - return reason; - } - } - - public boolean isStopped() { - return mStop; - } - - public Object getSynchronizer() { - return mSynchronizer; - } - - /** - * Convenience methods to do user logging (i.e. connection activity). Saves a bunch of - * repetitive code. - */ - public void userLog(String string, int code, String string2) { - if (mUserLog) { - userLog(string + code + string2); - } - } - - public void userLog(String string, int code) { - if (mUserLog) { - userLog(string + code); - } - } - - public void userLog(String str, Exception e) { - if (mUserLog) { - Log.e(TAG, str, e); - } else { - Log.e(TAG, str + e); - } - if (mFileLog) { - FileLogger.log(e); - } - } - - /** - * Standard logging for EAS. - * If user logging is active, we concatenate any arguments and log them using Log.d - * We also check for file logging, and log appropriately - * @param strings strings to concatenate and log - */ - public void userLog(String ...strings) { - if (mUserLog) { - String logText; - if (strings.length == 1) { - logText = strings[0]; - } else { - StringBuilder sb = new StringBuilder(64); - for (String string: strings) { - sb.append(string); - } - logText = sb.toString(); - } - Log.d(TAG, logText); - if (mFileLog) { - FileLogger.log(TAG, logText); - } - } - } - - /** - * Error log is used for serious issues that should always be logged - * @param str the string to log - */ - public void errorLog(String str) { - Log.e(TAG, str); - if (mFileLog) { - FileLogger.log(TAG, str); - } - } - - /** - * Waits for up to 10 seconds for network connectivity; returns whether or not there is - * network connectivity. - * - * @return whether there is network connectivity - */ - public boolean hasConnectivity() { - ConnectivityManager cm = - (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - int tries = 0; - while (tries++ < 1) { - // Use the same test as in ExchangeService#waitForConnectivity - // TODO: Create common code for this test in emailcommon - NetworkInfo info = cm.getActiveNetworkInfo(); - if (info != null) { - return true; - } - try { - Thread.sleep(10*SECONDS); - } catch (InterruptedException e) { - } - } - return false; - } - - /** - * Request handling (common functionality) - * Can be overridden if desired - */ - - public void addRequest(Request req) { - if (!mRequestQueue.contains(req)) { - mRequestQueue.offer(req); - } - } - - public void removeRequest(Request req) { - mRequestQueue.remove(req); - } - - public boolean hasPendingRequests() { - return !mRequestQueue.isEmpty(); - } - - public void clearRequests() { - mRequestQueue.clear(); - } -} diff --git a/emailsync/src/com/android/emailsync/EmailSyncAlarmReceiver.java b/emailsync/src/com/android/emailsync/EmailSyncAlarmReceiver.java deleted file mode 100644 index 8ac0f0633..000000000 --- a/emailsync/src/com/android/emailsync/EmailSyncAlarmReceiver.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2008-2009 Marc Blank - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.emailsync; - -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.util.Log; - -import com.android.emailcommon.provider.EmailContent.Message; -import com.android.emailcommon.provider.EmailContent.MessageColumns; -import com.android.emailcommon.provider.ProviderUnavailableException; - -import java.util.ArrayList; - -/** - * EmailSyncAlarmReceiver (USAR) is used by the SyncManager to start up-syncs of user-modified data - * back to the Exchange server. - * - * Here's how this works for Email, for example: - * - * 1) User modifies or deletes an email from the UI. - * 2) SyncManager, which has a ContentObserver watching the Message class, is alerted to a change - * 3) SyncManager sets an alarm (to be received by USAR) for a few seconds in the - * future (currently 15), the delay preventing excess syncing (think of it as a debounce mechanism). - * 4) ESAR Receiver's onReceive method is called - * 5) ESAR goes through all change and deletion records and compiles a list of mailboxes which have - * changes to be uploaded. - * 6) ESAR calls SyncManager to start syncs of those mailboxes - * - * If EmailProvider isn't available, the upsyncs will happen the next time ExchangeService starts - * - */ -public class EmailSyncAlarmReceiver extends BroadcastReceiver { - final String[] MAILBOX_DATA_PROJECTION = {MessageColumns.MAILBOX_KEY}; - - @Override - public void onReceive(final Context context, Intent intent) { - new Thread(new Runnable() { - public void run() { - handleReceive(context); - } - }).start(); - } - - private void handleReceive(Context context) { - ArrayList mailboxesToNotify = new ArrayList(); - ContentResolver cr = context.getContentResolver(); - - // Get a selector for EAS accounts (we don't want to sync on changes to POP/IMAP messages) - String selector = SyncManager.getAccountSelector(); - - try { - // Find all of the deletions - Cursor c = cr.query(Message.DELETED_CONTENT_URI, MAILBOX_DATA_PROJECTION, selector, - null, null); - if (c == null) throw new ProviderUnavailableException(); - try { - // Keep track of which mailboxes to notify; we'll only notify each one once - while (c.moveToNext()) { - long mailboxId = c.getLong(0); - if (!mailboxesToNotify.contains(mailboxId)) { - mailboxesToNotify.add(mailboxId); - } - } - } finally { - c.close(); - } - - // Now, find changed messages - c = cr.query(Message.UPDATED_CONTENT_URI, MAILBOX_DATA_PROJECTION, selector, - null, null); - if (c == null) throw new ProviderUnavailableException(); - try { - // Keep track of which mailboxes to notify; we'll only notify each one once - while (c.moveToNext()) { - long mailboxId = c.getLong(0); - if (!mailboxesToNotify.contains(mailboxId)) { - mailboxesToNotify.add(mailboxId); - } - } - } finally { - c.close(); - } - - // Request service from the mailbox - for (Long mailboxId: mailboxesToNotify) { - SyncManager.serviceRequest(mailboxId, SyncManager.SYNC_UPSYNC); - } - } catch (ProviderUnavailableException e) { - Log.e("EmailSyncAlarmReceiver", "EmailProvider unavailable; aborting alarm receiver"); - } - } -} diff --git a/emailsync/src/com/android/emailsync/FileLogger.java b/emailsync/src/com/android/emailsync/FileLogger.java deleted file mode 100644 index db8b62605..000000000 --- a/emailsync/src/com/android/emailsync/FileLogger.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.emailsync; - -import android.content.Context; -import android.os.Environment; - -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Date; - -public class FileLogger { - private static FileLogger LOGGER = null; - private static FileWriter sLogWriter = null; - public static String LOG_FILE_NAME = - Environment.getExternalStorageDirectory() + "/emaillog.txt"; - - public synchronized static FileLogger getLogger (Context c) { - LOGGER = new FileLogger(); - return LOGGER; - } - - private FileLogger() { - try { - sLogWriter = new FileWriter(LOG_FILE_NAME, true); - } catch (IOException e) { - // Doesn't matter - } - } - - static public synchronized void close() { - if (sLogWriter != null) { - try { - sLogWriter.close(); - } catch (IOException e) { - // Doesn't matter - } - sLogWriter = null; - } - } - - static public synchronized void log(Exception e) { - if (sLogWriter != null) { - log("Exception", "Stack trace follows..."); - PrintWriter pw = new PrintWriter(sLogWriter); - e.printStackTrace(pw); - pw.flush(); - } - } - - @SuppressWarnings("deprecation") - static public synchronized void log(String prefix, String str) { - if (LOGGER == null) { - LOGGER = new FileLogger(); - log("Logger", "\r\n\r\n --- New Log ---"); - } - Date d = new Date(); - int hr = d.getHours(); - int min = d.getMinutes(); - int sec = d.getSeconds(); - - // I don't use DateFormat here because (in my experience), it's much slower - StringBuffer sb = new StringBuffer(256); - sb.append('['); - sb.append(hr); - sb.append(':'); - if (min < 10) - sb.append('0'); - sb.append(min); - sb.append(':'); - if (sec < 10) { - sb.append('0'); - } - sb.append(sec); - sb.append("] "); - if (prefix != null) { - sb.append(prefix); - sb.append("| "); - } - sb.append(str); - sb.append("\r\n"); - String s = sb.toString(); - - if (sLogWriter != null) { - try { - sLogWriter.write(s); - sLogWriter.flush(); - } catch (IOException e) { - // Something might have happened to the sdcard - if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { - // If the card is mounted and we can create the writer, retry - LOGGER = new FileLogger(); - if (sLogWriter != null) { - try { - log("FileLogger", "Exception writing log; recreating..."); - log(prefix, str); - } catch (Exception e1) { - // Nothing to do at this point - } - } - } - } - } - } -} diff --git a/emailsync/src/com/android/emailsync/MailboxAlarmReceiver.java b/emailsync/src/com/android/emailsync/MailboxAlarmReceiver.java deleted file mode 100644 index 810579924..000000000 --- a/emailsync/src/com/android/emailsync/MailboxAlarmReceiver.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2008-2009 Marc Blank - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.emailsync; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - - -/** - * MailboxAlarmReceiver is used to "wake up" the ExchangeService at the appropriate time(s). It may - * also be used for individual sync adapters, but this isn't implemented at the present time. - * - */ -public class MailboxAlarmReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - long mailboxId = intent.getLongExtra("mailbox", SyncManager.EXTRA_MAILBOX_ID); - // EXCHANGE_SERVICE_MAILBOX_ID tells us that the service is asking to be started - if (mailboxId == SyncManager.SYNC_SERVICE_MAILBOX_ID) { - context.startService(new Intent(context, SyncManager.class)); - } else { - SyncManager.alert(context, mailboxId); - } - } -} - diff --git a/emailsync/src/com/android/emailsync/MessageMoveRequest.java b/emailsync/src/com/android/emailsync/MessageMoveRequest.java deleted file mode 100644 index 3f783a40d..000000000 --- a/emailsync/src/com/android/emailsync/MessageMoveRequest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.emailsync; - -import com.android.emailsync.Request; - -/** - * MessageMoveRequest is the EAS wrapper for requesting a "move to folder" - */ -public class MessageMoveRequest extends Request { - public final long mMailboxId; - - public MessageMoveRequest(long messageId, long mailboxId) { - super(messageId); - mMailboxId = mailboxId; - } - - // MessageMoveRequests are unique by their message id (i.e. it's meaningless to have two - // separate message moves queued at the same time) - public boolean equals(Object o) { - if (!(o instanceof MessageMoveRequest)) return false; - return ((MessageMoveRequest)o).mMessageId == mMessageId; - } - - public int hashCode() { - return (int)mMessageId; - } -} diff --git a/emailsync/src/com/android/emailsync/PartRequest.java b/emailsync/src/com/android/emailsync/PartRequest.java deleted file mode 100644 index ce0070f18..000000000 --- a/emailsync/src/com/android/emailsync/PartRequest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2008-2009 Marc Blank - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.emailsync; - -import com.android.emailcommon.provider.EmailContent.Attachment; - -/** - * PartRequest is the wrapper for attachment loading requests. In addition to information about - * the attachment to be loaded, it also contains the callback to be used for status/progress - * updates to the UI. - */ -public class PartRequest extends Request { - public final Attachment mAttachment; - public final String mDestination; - public final String mContentUriString; - public final String mLocation; - - public PartRequest(Attachment _att, String _destination, String _contentUriString) { - super(_att.mMessageKey); - mAttachment = _att; - mLocation = mAttachment.mLocation; - mDestination = _destination; - mContentUriString = _contentUriString; - } - - // PartRequests are unique by their attachment id (i.e. multiple attachments might be queued - // for a particular message, but any individual attachment can only be loaded once) - public boolean equals(Object o) { - if (!(o instanceof PartRequest)) return false; - return ((PartRequest)o).mAttachment.mId == mAttachment.mId; - } - - public int hashCode() { - return (int)mAttachment.mId; - } -} diff --git a/emailsync/src/com/android/emailsync/Request.java b/emailsync/src/com/android/emailsync/Request.java deleted file mode 100644 index f686a36cc..000000000 --- a/emailsync/src/com/android/emailsync/Request.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.emailsync; - -/** - * Requests for mailbox actions are handled by subclasses of this abstract class. - * Three subclasses are now defined: PartRequest (attachment load), MeetingResponseRequest - * (respond to a meeting invitation), and MessageMoveRequest (move a message to another folder) - */ -public abstract class Request { - public final long mTimeStamp = System.currentTimeMillis(); - public final long mMessageId; - - public Request(long messageId) { - mMessageId = messageId; - } - - // Subclasses of Request may have different semantics regarding equality; therefore, - // we force them to implement the equals method - public abstract boolean equals(Object o); - public abstract int hashCode(); -} diff --git a/emailsync/src/com/android/emailsync/SyncManager.java b/emailsync/src/com/android/emailsync/SyncManager.java deleted file mode 100644 index 6ed7a7e01..000000000 --- a/emailsync/src/com/android/emailsync/SyncManager.java +++ /dev/null @@ -1,2326 +0,0 @@ -/* - * Copyright (C) 2008-2009 Marc Blank - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.emailsync; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.database.ContentObserver; -import android.database.Cursor; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.NetworkInfo.State; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.os.Process; -import android.os.RemoteException; -import android.provider.CalendarContract; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.provider.ContactsContract; -import android.util.Log; - -import com.android.emailcommon.TempDirectory; -import com.android.emailcommon.provider.Account; -import com.android.emailcommon.provider.EmailContent; -import com.android.emailcommon.provider.EmailContent.Body; -import com.android.emailcommon.provider.EmailContent.BodyColumns; -import com.android.emailcommon.provider.EmailContent.MailboxColumns; -import com.android.emailcommon.provider.EmailContent.Message; -import com.android.emailcommon.provider.EmailContent.MessageColumns; -import com.android.emailcommon.provider.EmailContent.SyncColumns; -import com.android.emailcommon.provider.HostAuth; -import com.android.emailcommon.provider.Mailbox; -import com.android.emailcommon.provider.Policy; -import com.android.emailcommon.provider.ProviderUnavailableException; -import com.android.emailcommon.service.AccountServiceProxy; -import com.android.emailcommon.service.EmailServiceProxy; -import com.android.emailcommon.service.EmailServiceStatus; -import com.android.emailcommon.service.IEmailServiceCallback.Stub; -import com.android.emailcommon.service.PolicyServiceProxy; -import com.android.emailcommon.utility.EmailAsyncTask; -import com.android.emailcommon.utility.EmailClientConnectionManager; -import com.android.emailcommon.utility.Utility; - -import org.apache.http.conn.params.ConnManagerPNames; -import org.apache.http.conn.params.ConnPerRoute; -import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpParams; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; - -/** - * The SyncServiceManager handles the lifecycle of various sync adapters used by services that - * cannot rely on the system SyncManager - * - * SyncServiceManager uses ContentObservers to detect changes to accounts, mailboxes, & messages in - * order to maintain proper 2-way syncing of data. (More documentation to follow) - * - */ -public abstract class SyncManager extends Service implements Runnable { - - private static String TAG = "SyncManager"; - - // The SyncServiceManager's mailbox "id" - public static final int EXTRA_MAILBOX_ID = -1; - public static final int SYNC_SERVICE_MAILBOX_ID = 0; - - private static final int SECONDS = 1000; - private static final int MINUTES = 60*SECONDS; - private static final int ONE_DAY_MINUTES = 1440; - - private static final int SYNC_SERVICE_HEARTBEAT_TIME = 15*MINUTES; - private static final int CONNECTIVITY_WAIT_TIME = 10*MINUTES; - - // Sync hold constants for services with transient errors - private static final int HOLD_DELAY_MAXIMUM = 4*MINUTES; - - // Reason codes when SyncServiceManager.kick is called (mainly for debugging) - // UI has changed data, requiring an upsync of changes - public static final int SYNC_UPSYNC = 0; - // A scheduled sync (when not using push) - public static final int SYNC_SCHEDULED = 1; - // Mailbox was marked push - public static final int SYNC_PUSH = 2; - // A ping (EAS push signal) was received - public static final int SYNC_PING = 3; - // Misc. - public static final int SYNC_KICK = 4; - // A part request (attachment load, for now) was sent to SyncServiceManager - public static final int SYNC_SERVICE_PART_REQUEST = 5; - - // Requests >= SYNC_CALLBACK_START generate callbacks to the UI - public static final int SYNC_CALLBACK_START = 6; - // startSync was requested of SyncServiceManager (other than due to user request) - public static final int SYNC_SERVICE_START_SYNC = SYNC_CALLBACK_START + 0; - // startSync was requested of SyncServiceManager (due to user request) - public static final int SYNC_UI_REQUEST = SYNC_CALLBACK_START + 1; - - protected static final String WHERE_IN_ACCOUNT_AND_PUSHABLE = - MailboxColumns.ACCOUNT_KEY + "=? and type in (" + Mailbox.TYPE_INBOX + ',' - + Mailbox.TYPE_EAS_ACCOUNT_MAILBOX + ',' + Mailbox.TYPE_CONTACTS + ',' - + Mailbox.TYPE_CALENDAR + ')'; - protected static final String WHERE_IN_ACCOUNT_AND_TYPE_INBOX = - MailboxColumns.ACCOUNT_KEY + "=? and type = " + Mailbox.TYPE_INBOX ; - private static final String WHERE_MAILBOX_KEY = Message.MAILBOX_KEY + "=?"; - private static final String WHERE_NOT_INTERVAL_NEVER_AND_ACCOUNT_KEY_IN = - "(" + MailboxColumns.TYPE + '=' + Mailbox.TYPE_OUTBOX - + " or " + MailboxColumns.SYNC_INTERVAL + "!=" + Mailbox.CHECK_INTERVAL_NEVER + ')' - + " and " + MailboxColumns.ACCOUNT_KEY + " in ("; - - public static final int SEND_FAILED = 1; - public static final String MAILBOX_KEY_AND_NOT_SEND_FAILED = - MessageColumns.MAILBOX_KEY + "=? and (" + SyncColumns.SERVER_ID + " is null or " + - SyncColumns.SERVER_ID + "!=" + SEND_FAILED + ')'; - - public static final String CALENDAR_SELECTION = - Calendars.ACCOUNT_NAME + "=? AND " + Calendars.ACCOUNT_TYPE + "=?"; - private static final String WHERE_CALENDAR_ID = Events.CALENDAR_ID + "=?"; - - // Offsets into the syncStatus data for EAS that indicate type, exit status, and change count - // The format is S:: - public static final int STATUS_TYPE_CHAR = 1; - public static final int STATUS_EXIT_CHAR = 3; - public static final int STATUS_CHANGE_COUNT_OFFSET = 5; - - // Ready for ping - public static final int PING_STATUS_OK = 0; - // Service already running (can't ping) - public static final int PING_STATUS_RUNNING = 1; - // Service waiting after I/O error (can't ping) - public static final int PING_STATUS_WAITING = 2; - // Service had a fatal error; can't run - public static final int PING_STATUS_UNABLE = 3; - // Service is disabled by user (checkbox) - public static final int PING_STATUS_DISABLED = 4; - - private static final int MAX_CLIENT_CONNECTION_MANAGER_SHUTDOWNS = 1; - - // We synchronize on this for all actions affecting the service and error maps - private static final Object sSyncLock = new Object(); - // All threads can use this lock to wait for connectivity - public static final Object sConnectivityLock = new Object(); - public static boolean sConnectivityHold = false; - - // Keeps track of running services (by mailbox id) - public final HashMap mServiceMap = - new HashMap(); - // Keeps track of services whose last sync ended with an error (by mailbox id) - /*package*/ public ConcurrentHashMap mSyncErrorMap = - new ConcurrentHashMap(); - // Keeps track of which services require a wake lock (by mailbox id) - private final HashMap mWakeLocks = new HashMap(); - // Keeps track of which services have held a wake lock (by mailbox id) - private final HashMap mWakeLocksHistory = new HashMap(); - // Keeps track of PendingIntents for mailbox alarms (by mailbox id) - private final HashMap mPendingIntents = new HashMap(); - // The actual WakeLock obtained by SyncServiceManager - private WakeLock mWakeLock = null; - // Keep our cached list of active Accounts here - public final AccountList mAccountList = new AccountList(); - // Keep track of when we started up - private long mServiceStartTime; - - // Observers that we use to look for changed mail-related data - private final Handler mHandler = new Handler(); - private AccountObserver mAccountObserver; - private MailboxObserver mMailboxObserver; - private SyncedMessageObserver mSyncedMessageObserver; - - // Concurrent because CalendarSyncAdapter can modify the map during a wipe - private final ConcurrentHashMap mCalendarObservers = - new ConcurrentHashMap(); - - public ContentResolver mResolver; - - // The singleton SyncServiceManager object, with its thread and stop flag - protected static SyncManager INSTANCE; - protected static Thread sServiceThread = null; - // Cached unique device id - protected static String sDeviceId = null; - // HashMap of ConnectionManagers that all EAS threads can use (by HostAuth id) - private static HashMap sClientConnectionManagers = - new HashMap(); - // Count of ClientConnectionManager shutdowns - private static volatile int sClientConnectionManagerShutdownCount = 0; - - private static volatile boolean sStartingUp = false; - private static volatile boolean sStop = false; - - // The reason for SyncServiceManager's next wakeup call - private String mNextWaitReason; - // Whether we have an unsatisfied "kick" pending - private boolean mKicked = false; - - // Receiver of connectivity broadcasts - private ConnectivityReceiver mConnectivityReceiver = null; - private ConnectivityReceiver mBackgroundDataSettingReceiver = null; - private volatile boolean mBackgroundData = true; - // The most current NetworkInfo (from ConnectivityManager) - private NetworkInfo mNetworkInfo; - - // For sync logging - protected static boolean sUserLog = false; - protected static boolean sFileLog = false; - - /** - * Return an AccountObserver for this manager; the subclass must implement the newAccount() - * method, which is called whenever the observer discovers that a new account has been created. - * The subclass should do any housekeeping necessary - * @param handler a Handler - * @return the AccountObserver - */ - public abstract AccountObserver getAccountObserver(Handler handler); - - /** - * Perform any housekeeping necessary upon startup of the manager - */ - public abstract void onStartup(); - - /** - * Returns a String that can be used as a WHERE clause in SQLite that selects accounts whose - * syncs are managed by this manager - * @return the account selector String - */ - public abstract String getAccountsSelector(); - - /** - * Returns an appropriate sync service for the passed in mailbox - * @param context the caller's context - * @param mailbox the Mailbox to be synced - * @return a service that will sync the Mailbox - */ - public abstract AbstractSyncService getServiceForMailbox(Context context, Mailbox mailbox); - - /** - * Return a list of all Accounts in EmailProvider. Because the result of this call may be used - * in account reconciliation, an exception is thrown if the result cannot be guaranteed accurate - * @param context the caller's context - * @param accounts a list that Accounts will be added into - * @return the list of Accounts - * @throws ProviderUnavailableException if the list of Accounts cannot be guaranteed valid - */ - public abstract AccountList collectAccounts(Context context, AccountList accounts); - - /** - * Returns the AccountManager type (e.g. com.android.exchange) for this sync service - */ - public abstract String getAccountManagerType(); - - /** - * Returns the intent used for this sync service - */ - public abstract Intent getServiceIntent(); - - /** - * Returns the callback proxy used for communicating back with the Email app - */ - public abstract Stub getCallbackProxy(); - - /** - * Called when a sync service has started (in case any action is needed). This method must - * not perform any long-lived actions (db access, network access, etc) - */ - public abstract void onStartService(Mailbox mailbox); - - public class AccountList extends ArrayList { - private static final long serialVersionUID = 1L; - - @Override - public boolean add(Account account) { - // Cache the account manager account - account.mAmAccount = new android.accounts.Account( - account.mEmailAddress, getAccountManagerType()); - super.add(account); - return true; - } - - public boolean contains(long id) { - for (Account account : this) { - if (account.mId == id) { - return true; - } - } - return false; - } - - public Account getById(long id) { - for (Account account : this) { - if (account.mId == id) { - return account; - } - } - return null; - } - - public Account getByName(String accountName) { - for (Account account : this) { - if (account.mEmailAddress.equalsIgnoreCase(accountName)) { - return account; - } - } - return null; - } - } - - public static void setUserDebug(int state) { - sUserLog = (state & EmailServiceProxy.DEBUG_BIT) != 0; - sFileLog = (state & EmailServiceProxy.DEBUG_FILE_BIT) != 0; - if (sFileLog) { - sUserLog = true; - } - Log.d("Sync Debug", "Logging: " + (sUserLog ? "User " : "") + (sFileLog ? "File" : "")); - } - - private boolean onSecurityHold(Account account) { - return (account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0; - } - - public static String getAccountSelector() { - SyncManager ssm = INSTANCE; - if (ssm == null) return ""; - return ssm.getAccountsSelector(); - } - - public abstract class AccountObserver extends ContentObserver { - String mSyncableMailboxSelector = null; - String mAccountSelector = null; - - // Runs when SyncServiceManager first starts - @SuppressWarnings("deprecation") - public AccountObserver(Handler handler) { - super(handler); - // At startup, we want to see what EAS accounts exist and cache them - // TODO: Move database work out of UI thread - Context context = getContext(); - synchronized (mAccountList) { - try { - collectAccounts(context, mAccountList); - } catch (ProviderUnavailableException e) { - // Just leave if EmailProvider is unavailable - return; - } - // Create an account mailbox for any account without one - for (Account account : mAccountList) { - int cnt = Mailbox.count(context, Mailbox.CONTENT_URI, "accountKey=" - + account.mId, null); - if (cnt == 0) { - // This case handles a newly created account - newAccount(account.mId); - } - } - } - // Run through accounts and update account hold information - Utility.runAsync(new Runnable() { - @Override - public void run() { - synchronized (mAccountList) { - for (Account account : mAccountList) { - if (onSecurityHold(account)) { - // If we're in a security hold, and our policies are active, release - // the hold - if (PolicyServiceProxy.isActive(SyncManager.this, null)) { - PolicyServiceProxy.setAccountHoldFlag(SyncManager.this, - account, false); - log("isActive true; release hold for " + account.mDisplayName); - } - } - } - } - }}); - } - - /** - * Returns a String suitable for appending to a where clause that selects for all syncable - * mailboxes in all eas accounts - * @return a complex selection string that is not to be cached - */ - public String getSyncableMailboxWhere() { - if (mSyncableMailboxSelector == null) { - StringBuilder sb = new StringBuilder(WHERE_NOT_INTERVAL_NEVER_AND_ACCOUNT_KEY_IN); - boolean first = true; - synchronized (mAccountList) { - for (Account account : mAccountList) { - if (!first) { - sb.append(','); - } else { - first = false; - } - sb.append(account.mId); - } - } - sb.append(')'); - mSyncableMailboxSelector = sb.toString(); - } - return mSyncableMailboxSelector; - } - - private void onAccountChanged() { - try { - maybeStartSyncServiceManagerThread(); - Context context = getContext(); - - // A change to the list requires us to scan for deletions (stop running syncs) - // At startup, we want to see what accounts exist and cache them - AccountList currentAccounts = new AccountList(); - try { - collectAccounts(context, currentAccounts); - } catch (ProviderUnavailableException e) { - // Just leave if EmailProvider is unavailable - return; - } - synchronized (mAccountList) { - for (Account account : mAccountList) { - boolean accountIncomplete = - (account.mFlags & Account.FLAGS_INCOMPLETE) != 0; - // If the current list doesn't include this account and the account wasn't - // incomplete, then this is a deletion - if (!currentAccounts.contains(account.mId) && !accountIncomplete) { - // The implication is that the account has been deleted; let's find out - alwaysLog("Observer found deleted account: " + account.mDisplayName); - // Run the reconciler (the reconciliation itself runs in the Email app) - runAccountReconcilerSync(SyncManager.this); - // See if the account is still around - Account deletedAccount = - Account.restoreAccountWithId(context, account.mId); - if (deletedAccount != null) { - // It is; add it to our account list - alwaysLog("Account still in provider: " + account.mDisplayName); - currentAccounts.add(account); - } else { - // It isn't; stop syncs and clear our selectors - alwaysLog("Account deletion confirmed: " + account.mDisplayName); - stopAccountSyncs(account.mId, true); - mSyncableMailboxSelector = null; - mAccountSelector = null; - } - } else { - // Get the newest version of this account - Account updatedAccount = - Account.restoreAccountWithId(context, account.mId); - if (updatedAccount == null) continue; - if (account.mSyncInterval != updatedAccount.mSyncInterval - || account.mSyncLookback != updatedAccount.mSyncLookback) { - // Set the inbox interval to the interval of the Account - // This setting should NOT affect other boxes - ContentValues cv = new ContentValues(); - cv.put(MailboxColumns.SYNC_INTERVAL, updatedAccount.mSyncInterval); - getContentResolver().update(Mailbox.CONTENT_URI, cv, - WHERE_IN_ACCOUNT_AND_TYPE_INBOX, new String[] { - Long.toString(account.mId) - }); - // Stop all current syncs; the appropriate ones will restart - log("Account " + account.mDisplayName + " changed; stop syncs"); - stopAccountSyncs(account.mId, true); - } - - // See if this account is no longer on security hold - if (onSecurityHold(account) && !onSecurityHold(updatedAccount)) { - releaseSyncHolds(SyncManager.this, - AbstractSyncService.EXIT_SECURITY_FAILURE, account); - } - - // Put current values into our cached account - account.mSyncInterval = updatedAccount.mSyncInterval; - account.mSyncLookback = updatedAccount.mSyncLookback; - account.mFlags = updatedAccount.mFlags; - } - } - // Look for new accounts - for (Account account : currentAccounts) { - if (!mAccountList.contains(account.mId)) { - // Don't forget to cache the HostAuth - HostAuth ha = HostAuth.restoreHostAuthWithId(getContext(), - account.mHostAuthKeyRecv); - if (ha == null) continue; - account.mHostAuthRecv = ha; - // This is an addition; create our magic hidden mailbox... - log("Account observer found new account: " + account.mDisplayName); - newAccount(account.mId); - mAccountList.add(account); - mSyncableMailboxSelector = null; - mAccountSelector = null; - } - } - // Finally, make sure our account list is up to date - mAccountList.clear(); - mAccountList.addAll(currentAccounts); - } - - // See if there's anything to do... - kick("account changed"); - } catch (ProviderUnavailableException e) { - alwaysLog("Observer failed; provider unavailable"); - } - } - - @Override - public void onChange(boolean selfChange) { - new Thread(new Runnable() { - @Override - public void run() { - onAccountChanged(); - }}, "Account Observer").start(); - } - - public abstract void newAccount(long acctId); - } - - /** - * Register a specific Calendar's data observer; we need to recognize when the SYNC_EVENTS - * column has changed (when sync has turned off or on) - * @param account the Account whose Calendar we're observing - */ - private void registerCalendarObserver(Account account) { - // Get a new observer - CalendarObserver observer = new CalendarObserver(mHandler, account); - if (observer.mCalendarId != 0) { - // If we find the Calendar (and we'd better) register it and store it in the map - mCalendarObservers.put(account.mId, observer); - mResolver.registerContentObserver( - ContentUris.withAppendedId(Calendars.CONTENT_URI, observer.mCalendarId), false, - observer); - } - } - - /** - * Unregister all CalendarObserver's - */ - static public void unregisterCalendarObservers() { - SyncManager ssm = INSTANCE; - if (ssm == null) return; - ContentResolver resolver = ssm.mResolver; - for (CalendarObserver observer: ssm.mCalendarObservers.values()) { - resolver.unregisterContentObserver(observer); - } - ssm.mCalendarObservers.clear(); - } - - public static Uri asSyncAdapter(Uri uri, String account, String accountType) { - return uri.buildUpon().appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true") - .appendQueryParameter(Calendars.ACCOUNT_NAME, account) - .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build(); - } - - /** - * Return the syncable state of an account's calendar, as determined by the sync_events column - * of our Calendar (from CalendarProvider2) - * Note that the current state of sync_events is cached in our CalendarObserver - * @param accountId the id of the account whose calendar we are checking - * @return whether or not syncing of events is enabled - */ - private boolean isCalendarEnabled(long accountId) { - CalendarObserver observer = mCalendarObservers.get(accountId); - if (observer != null) { - return (observer.mSyncEvents == 1); - } - // If there's no observer, there's no Calendar in CalendarProvider2, so we return true - // to allow Calendar creation - return true; - } - - private class CalendarObserver extends ContentObserver { - final long mAccountId; - final String mAccountName; - long mCalendarId; - long mSyncEvents; - - public CalendarObserver(Handler handler, Account account) { - super(handler); - mAccountId = account.mId; - mAccountName = account.mEmailAddress; - // Find the Calendar for this account - Cursor c = mResolver.query(Calendars.CONTENT_URI, - new String[] {Calendars._ID, Calendars.SYNC_EVENTS}, - CALENDAR_SELECTION, - new String[] {account.mEmailAddress, getAccountManagerType()}, - null); - if (c != null) { - // Save its id and its sync events status - try { - if (c.moveToFirst()) { - mCalendarId = c.getLong(0); - mSyncEvents = c.getLong(1); - } - } finally { - c.close(); - } - } - } - - @Override - public synchronized void onChange(boolean selfChange) { - // See if the user has changed syncing of our calendar - if (!selfChange) { - new Thread(new Runnable() { - @Override - public void run() { - try { - Cursor c = mResolver.query(Calendars.CONTENT_URI, - new String[] {Calendars.SYNC_EVENTS}, Calendars._ID + "=?", - new String[] {Long.toString(mCalendarId)}, null); - if (c == null) return; - // Get its sync events; if it's changed, we've got work to do - try { - if (c.moveToFirst()) { - long newSyncEvents = c.getLong(0); - if (newSyncEvents != mSyncEvents) { - log("_sync_events changed for calendar in " + mAccountName); - Mailbox mailbox = Mailbox.restoreMailboxOfType(INSTANCE, - mAccountId, Mailbox.TYPE_CALENDAR); - // Sanity check for mailbox deletion - if (mailbox == null) return; - ContentValues cv = new ContentValues(); - if (newSyncEvents == 0) { - // When sync is disabled, we're supposed to delete - // all events in the calendar - log("Deleting events and setting syncKey to 0 for " + - mAccountName); - // First, stop any sync that's ongoing - stopManualSync(mailbox.mId); - // Set the syncKey to 0 (reset) - AbstractSyncService service = getServiceForMailbox( - INSTANCE, mailbox); - service.resetCalendarSyncKey(); - // Reset the sync key locally and stop syncing - cv.put(Mailbox.SYNC_KEY, "0"); - cv.put(Mailbox.SYNC_INTERVAL, - Mailbox.CHECK_INTERVAL_NEVER); - mResolver.update(ContentUris.withAppendedId( - Mailbox.CONTENT_URI, mailbox.mId), cv, null, - null); - // Delete all events using the sync adapter - // parameter so that the deletion is only local - Uri eventsAsSyncAdapter = - asSyncAdapter( - Events.CONTENT_URI, - mAccountName, - getAccountManagerType()); - mResolver.delete(eventsAsSyncAdapter, WHERE_CALENDAR_ID, - new String[] {Long.toString(mCalendarId)}); - } else { - // Make this a push mailbox and kick; this will start - // a resync of the Calendar; the account mailbox will - // ping on this during the next cycle of the ping loop - cv.put(Mailbox.SYNC_INTERVAL, - Mailbox.CHECK_INTERVAL_PUSH); - mResolver.update(ContentUris.withAppendedId( - Mailbox.CONTENT_URI, mailbox.mId), cv, null, - null); - kick("calendar sync changed"); - } - - // Save away the new value - mSyncEvents = newSyncEvents; - } - } - } finally { - c.close(); - } - } catch (ProviderUnavailableException e) { - Log.w(TAG, "Observer failed; provider unavailable"); - } - }}, "Calendar Observer").start(); - } - } - } - - private class MailboxObserver extends ContentObserver { - public MailboxObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - // See if there's anything to do... - if (!selfChange) { - kick("mailbox changed"); - } - } - } - - private class SyncedMessageObserver extends ContentObserver { - Intent syncAlarmIntent = new Intent(INSTANCE, EmailSyncAlarmReceiver.class); - PendingIntent syncAlarmPendingIntent = - PendingIntent.getBroadcast(INSTANCE, 0, syncAlarmIntent, 0); - AlarmManager alarmManager = (AlarmManager)INSTANCE.getSystemService(Context.ALARM_SERVICE); - - public SyncedMessageObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - alarmManager.set(AlarmManager.RTC_WAKEUP, - System.currentTimeMillis() + 10*SECONDS, syncAlarmPendingIntent); - } - } - - static public Account getAccountById(long accountId) { - SyncManager ssm = INSTANCE; - if (ssm != null) { - AccountList accountList = ssm.mAccountList; - synchronized (accountList) { - return accountList.getById(accountId); - } - } - return null; - } - - static public Account getAccountByName(String accountName) { - SyncManager ssm = INSTANCE; - if (ssm != null) { - AccountList accountList = ssm.mAccountList; - synchronized (accountList) { - return accountList.getByName(accountName); - } - } - return null; - } - - public class SyncStatus { - static public final int NOT_RUNNING = 0; - static public final int DIED = 1; - static public final int SYNC = 2; - static public final int IDLE = 3; - } - - /*package*/ public class SyncError { - int reason; - public boolean fatal = false; - long holdDelay = 15*SECONDS; - public long holdEndTime = System.currentTimeMillis() + holdDelay; - - public SyncError(int _reason, boolean _fatal) { - reason = _reason; - fatal = _fatal; - } - - /** - * We double the holdDelay from 15 seconds through 8 mins - */ - void escalate() { - if (holdDelay <= HOLD_DELAY_MAXIMUM) { - holdDelay *= 2; - } - holdEndTime = System.currentTimeMillis() + holdDelay; - } - } - - private void logSyncHolds() { - if (sUserLog) { - log("Sync holds:"); - long time = System.currentTimeMillis(); - for (long mailboxId : mSyncErrorMap.keySet()) { - Mailbox m = Mailbox.restoreMailboxWithId(this, mailboxId); - if (m == null) { - log("Mailbox " + mailboxId + " no longer exists"); - } else { - SyncError error = mSyncErrorMap.get(mailboxId); - if (error != null) { - log("Mailbox " + m.mDisplayName + ", error = " + error.reason - + ", fatal = " + error.fatal); - if (error.holdEndTime > 0) { - log("Hold ends in " + ((error.holdEndTime - time) / 1000) + "s"); - } - } - } - } - } - } - - /** - * Release security holds for the specified account - * @param account the account whose Mailboxes should be released from security hold - */ - static public void releaseSecurityHold(Account account) { - SyncManager ssm = INSTANCE; - if (ssm != null) { - ssm.releaseSyncHolds(INSTANCE, AbstractSyncService.EXIT_SECURITY_FAILURE, - account); - } - } - - /** - * Release a specific type of hold (the reason) for the specified Account; if the account - * is null, mailboxes from all accounts with the specified hold will be released - * @param reason the reason for the SyncError (AbstractSyncService.EXIT_XXX) - * @param account an Account whose mailboxes should be released (or all if null) - * @return whether or not any mailboxes were released - */ - public /*package*/ boolean releaseSyncHolds(Context context, int reason, Account account) { - boolean holdWasReleased = releaseSyncHoldsImpl(context, reason, account); - kick("security release"); - return holdWasReleased; - } - - private boolean releaseSyncHoldsImpl(Context context, int reason, Account account) { - boolean holdWasReleased = false; - for (long mailboxId: mSyncErrorMap.keySet()) { - if (account != null) { - Mailbox m = Mailbox.restoreMailboxWithId(context, mailboxId); - if (m == null) { - mSyncErrorMap.remove(mailboxId); - } else if (m.mAccountKey != account.mId) { - continue; - } - } - SyncError error = mSyncErrorMap.get(mailboxId); - if (error != null && error.reason == reason) { - mSyncErrorMap.remove(mailboxId); - holdWasReleased = true; - } - } - return holdWasReleased; - } - - public static void log(String str) { - log(TAG, str); - } - - public static void log(String tag, String str) { - if (sUserLog) { - Log.d(tag, str); - if (sFileLog) { - FileLogger.log(tag, str); - } - } - } - - public static void alwaysLog(String str) { - if (!sUserLog) { - Log.d(TAG, str); - } else { - log(str); - } - } - - /** - * EAS requires a unique device id, so that sync is possible from a variety of different - * devices (e.g. the syncKey is specific to a device) If we're on an emulator or some other - * device that doesn't provide one, we can create it as "device". - * This would work on a real device as well, but it would be better to use the "real" id if - * it's available - */ - static public String getDeviceId(Context context) throws IOException { - if (sDeviceId == null) { - sDeviceId = new AccountServiceProxy(context).getDeviceId(); - alwaysLog("Received deviceId from Email app: " + sDeviceId); - } - return sDeviceId; - } - - static public ConnPerRoute sConnPerRoute = new ConnPerRoute() { - @Override - public int getMaxForRoute(HttpRoute route) { - return 8; - } - }; - - static public synchronized EmailClientConnectionManager getClientConnectionManager( - Context context, HostAuth hostAuth) { - // We'll use a different connection manager for each HostAuth - EmailClientConnectionManager mgr = null; - // We don't save managers for validation/autodiscover - if (hostAuth.mId != HostAuth.NOT_SAVED) { - mgr = sClientConnectionManagers.get(hostAuth.mId); - } - if (mgr == null) { - // After two tries, kill the process. Most likely, this will happen in the background - // The service will restart itself after about 5 seconds - if (sClientConnectionManagerShutdownCount > MAX_CLIENT_CONNECTION_MANAGER_SHUTDOWNS) { - alwaysLog("Shutting down process to unblock threads"); - Process.killProcess(Process.myPid()); - } - HttpParams params = new BasicHttpParams(); - params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 25); - params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, sConnPerRoute); - boolean ssl = hostAuth.shouldUseSsl(); - int port = hostAuth.mPort; - mgr = EmailClientConnectionManager.newInstance(context, params, hostAuth); - log("Creating connection manager for port " + port + ", ssl: " + ssl); - sClientConnectionManagers.put(hostAuth.mId, mgr); - } - // Null is a valid return result if we get an exception - return mgr; - } - - static private synchronized void shutdownConnectionManager() { - log("Shutting down ClientConnectionManagers"); - for (EmailClientConnectionManager mgr: sClientConnectionManagers.values()) { - mgr.shutdown(); - } - sClientConnectionManagers.clear(); - } - - public static void stopAccountSyncs(long acctId) { - SyncManager ssm = INSTANCE; - if (ssm != null) { - ssm.stopAccountSyncs(acctId, true); - } - } - - public void stopAccountSyncs(long acctId, boolean includeAccountMailbox) { - synchronized (sSyncLock) { - List deletedBoxes = new ArrayList(); - for (Long mid : mServiceMap.keySet()) { - Mailbox box = Mailbox.restoreMailboxWithId(this, mid); - if (box != null) { - if (box.mAccountKey == acctId) { - if (!includeAccountMailbox && - box.mType == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) { - AbstractSyncService svc = mServiceMap.get(mid); - if (svc != null) { - svc.stop(); - } - continue; - } - AbstractSyncService svc = mServiceMap.get(mid); - if (svc != null) { - svc.stop(); - Thread t = svc.mThread; - if (t != null) { - t.interrupt(); - } - } - deletedBoxes.add(mid); - } - } - } - for (Long mid : deletedBoxes) { - releaseMailbox(mid); - } - } - } - - /** - * Informs SyncServiceManager that an account has a new folder list; as a result, any existing - * folder might have become invalid. Therefore, we act as if the account has been deleted, and - * then we reinitialize it. - * - * @param acctId - */ - static public void stopNonAccountMailboxSyncsForAccount(long acctId) { - SyncManager ssm = INSTANCE; - if (ssm != null) { - ssm.stopAccountSyncs(acctId, false); - kick("reload folder list"); - } - } - - private void acquireWakeLock(long id) { - synchronized (mWakeLocks) { - Long lock = mWakeLocks.get(id); - if (lock == null) { - if (mWakeLock == null) { - PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MAIL_SERVICE"); - mWakeLock.acquire(); - // STOPSHIP Remove - log("+WAKE LOCK ACQUIRED"); - } - mWakeLocks.put(id, System.currentTimeMillis()); - } - } - } - - private void releaseWakeLock(long id) { - synchronized (mWakeLocks) { - Long lock = mWakeLocks.get(id); - if (lock != null) { - Long startTime = mWakeLocks.remove(id); - Long historicalTime = mWakeLocksHistory.get(id); - if (historicalTime == null) { - historicalTime = 0L; - } - mWakeLocksHistory.put(id, - historicalTime + (System.currentTimeMillis() - startTime)); - if (mWakeLocks.isEmpty()) { - if (mWakeLock != null) { - mWakeLock.release(); - } - mWakeLock = null; - // STOPSHIP Remove - log("+WAKE LOCK RELEASED"); - } else { - log("Release request for lock not held: " + id); - } - } - } - } - - static public String alarmOwner(long id) { - if (id == EXTRA_MAILBOX_ID) { - return TAG; - } else { - String name = Long.toString(id); - if (sUserLog && INSTANCE != null) { - Mailbox m = Mailbox.restoreMailboxWithId(INSTANCE, id); - if (m != null) { - name = m.mDisplayName + '(' + m.mAccountKey + ')'; - } - } - return "Mailbox " + name; - } - } - - private void clearAlarm(long id) { - synchronized (mPendingIntents) { - PendingIntent pi = mPendingIntents.get(id); - if (pi != null) { - AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE); - alarmManager.cancel(pi); - //log("+Alarm cleared for " + alarmOwner(id)); - mPendingIntents.remove(id); - } - } - } - - private void setAlarm(long id, long millis) { - synchronized (mPendingIntents) { - PendingIntent pi = mPendingIntents.get(id); - if (pi == null) { - Intent i = new Intent(this, MailboxAlarmReceiver.class); - i.putExtra("mailbox", id); - i.setData(Uri.parse("Box" + id)); - pi = PendingIntent.getBroadcast(this, 0, i, 0); - mPendingIntents.put(id, pi); - - AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE); - alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + millis, pi); - //log("+Alarm set for " + alarmOwner(id) + ", " + millis/1000 + "s"); - } - } - } - - private void clearAlarms() { - AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE); - synchronized (mPendingIntents) { - for (PendingIntent pi : mPendingIntents.values()) { - alarmManager.cancel(pi); - } - mPendingIntents.clear(); - } - } - - static public void runAwake(long id) { - SyncManager ssm = INSTANCE; - if (ssm != null) { - ssm.acquireWakeLock(id); - ssm.clearAlarm(id); - } - } - - static public void runAsleep(long id, long millis) { - SyncManager ssm = INSTANCE; - if (ssm != null) { - ssm.setAlarm(id, millis); - ssm.releaseWakeLock(id); - } - } - - static public void clearWatchdogAlarm(long id) { - SyncManager ssm = INSTANCE; - if (ssm != null) { - ssm.clearAlarm(id); - } - } - - static public void setWatchdogAlarm(long id, long millis) { - SyncManager ssm = INSTANCE; - if (ssm != null) { - ssm.setAlarm(id, millis); - } - } - - static public void alert(Context context, final long id) { - final SyncManager ssm = INSTANCE; - checkSyncManagerRunning(); - if (id < 0) { - log("SyncServiceManager alert"); - kick("ping SyncServiceManager"); - } else if (ssm == null) { - context.startService(new Intent(context, SyncManager.class)); - } else { - final AbstractSyncService service = ssm.getRunningService(id); - if (service != null) { - // Handle alerts in a background thread, as we are typically called from a - // broadcast receiver, and are therefore running in the UI thread - String threadName = "SyncServiceManager Alert: "; - if (service.mMailbox != null) { - threadName += service.mMailbox.mDisplayName; - } - new Thread(new Runnable() { - @Override - public void run() { - Mailbox m = Mailbox.restoreMailboxWithId(ssm, id); - if (m != null) { - // We ignore drafts completely (doesn't sync). Changes in Outbox are - // handled in the checkMailboxes loop, so we can ignore these pings. - if (sUserLog) { - Log.d(TAG, "Alert for mailbox " + id + " (" + m.mDisplayName + ")"); - } - if (m.mType == Mailbox.TYPE_DRAFTS || m.mType == Mailbox.TYPE_OUTBOX) { - String[] args = new String[] {Long.toString(m.mId)}; - ContentResolver resolver = INSTANCE.mResolver; - resolver.delete(Message.DELETED_CONTENT_URI, WHERE_MAILBOX_KEY, - args); - resolver.delete(Message.UPDATED_CONTENT_URI, WHERE_MAILBOX_KEY, - args); - return; - } - service.mAccount = Account.restoreAccountWithId(INSTANCE, m.mAccountKey); - service.mMailbox = m; - // Send the alarm to the sync service - if (!service.alarm()) { - // A false return means that we were forced to interrupt the thread - // In this case, we release the mailbox so that we can start another - // thread to do the work - log("Alarm failed; releasing mailbox"); - synchronized(sSyncLock) { - ssm.releaseMailbox(id); - } - // Shutdown the connection manager; this should close all of our - // sockets and generate IOExceptions all around. - SyncManager.shutdownConnectionManager(); - } - } - }}, threadName).start(); - } - } - } - - public class ConnectivityReceiver extends BroadcastReceiver { - @SuppressWarnings("deprecation") - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - Bundle b = intent.getExtras(); - if (b != null) { - NetworkInfo a = (NetworkInfo)b.get(ConnectivityManager.EXTRA_NETWORK_INFO); - String info = "Connectivity alert for " + a.getTypeName(); - State state = a.getState(); - if (state == State.CONNECTED) { - info += " CONNECTED"; - log(info); - synchronized (sConnectivityLock) { - sConnectivityLock.notifyAll(); - } - kick("connected"); - } else if (state == State.DISCONNECTED) { - info += " DISCONNECTED"; - log(info); - kick("disconnected"); - } - } - } else if (intent.getAction().equals( - ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED)) { - ConnectivityManager cm = - (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); - mBackgroundData = cm.getBackgroundDataSetting(); - // If background data is now on, we want to kick SyncServiceManager - if (mBackgroundData) { - kick("background data on"); - log("Background data on; restart syncs"); - // Otherwise, stop all syncs - } else { - log("Background data off: stop all syncs"); - EmailAsyncTask.runAsyncParallel(new Runnable() { - @Override - public void run() { - synchronized (mAccountList) { - for (Account account : mAccountList) - SyncManager.stopAccountSyncs(account.mId); - } - }}); - } - } - } - } - - /** - * Starts a service thread and enters it into the service map - * This is the point of instantiation of all sync threads - * @param service the service to start - * @param m the Mailbox on which the service will operate - */ - private void startServiceThread(AbstractSyncService service) { - final Mailbox mailbox = service.mMailbox; - synchronized (sSyncLock) { - String mailboxName = mailbox.mDisplayName; - String accountName = service.mAccount.mDisplayName; - Thread thread = new Thread(service, mailboxName + "[" + accountName + "]"); - log("Starting thread for " + mailboxName + " in account " + accountName); - thread.start(); - mServiceMap.put(mailbox.mId, service); - runAwake(mailbox.mId); - } - onStartService(mailbox); - } - - private void requestSync(Mailbox m, int reason, Request req) { - int syncStatus = EmailContent.SYNC_STATUS_BACKGROUND; - // Don't sync if there's no connectivity - if (sConnectivityHold || (m == null) || sStop) { - if (reason >= SYNC_CALLBACK_START) { - try { - Stub proxy = getCallbackProxy(); - if (proxy != null) { - proxy.syncMailboxStatus(m.mId, EmailServiceStatus.CONNECTION_ERROR, 0); - } - } catch (RemoteException e) { - // We tried... - } - } - return; - } - synchronized (sSyncLock) { - Account acct = Account.restoreAccountWithId(this, m.mAccountKey); - if (acct != null) { - // Always make sure there's not a running instance of this service - AbstractSyncService service = mServiceMap.get(m.mId); - if (service == null) { - service = getServiceForMailbox(this, m); - if (!service.mIsValid) return; - service.mSyncReason = reason; - if (req != null) { - service.addRequest(req); - } - startServiceThread(service); - if (reason >= SYNC_CALLBACK_START) { - syncStatus = EmailContent.SYNC_STATUS_USER; - } - setMailboxSyncStatus(m.mId, syncStatus); - } - } - } - } - - public void setMailboxSyncStatus(long id, int status) { - ContentValues values = new ContentValues(); - values.put(Mailbox.UI_SYNC_STATUS, status); - mResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, id), values, null, null); - } - - public void setMailboxLastSyncResult(long id, int result) { - ContentValues values = new ContentValues(); - values.put(Mailbox.UI_LAST_SYNC_RESULT, result); - mResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, id), values, null, null); - } - - private void stopServiceThreads() { - synchronized (sSyncLock) { - ArrayList toStop = new ArrayList(); - - // Keep track of which services to stop - for (Long mailboxId : mServiceMap.keySet()) { - toStop.add(mailboxId); - } - - // Shut down all of those running services - for (Long mailboxId : toStop) { - AbstractSyncService svc = mServiceMap.get(mailboxId); - if (svc != null) { - log("Stopping " + svc.mAccount.mDisplayName + '/' + svc.mMailbox.mDisplayName); - svc.stop(); - if (svc.mThread != null) { - svc.mThread.interrupt(); - } - } - releaseWakeLock(mailboxId); - } - } - } - - private void waitForConnectivity() { - boolean waiting = false; - ConnectivityManager cm = - (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); - while (!sStop) { - NetworkInfo info = cm.getActiveNetworkInfo(); - if (info != null) { - mNetworkInfo = info; - // We're done if there's an active network - if (waiting) { - // If we've been waiting, release any I/O error holds - releaseSyncHolds(this, AbstractSyncService.EXIT_IO_ERROR, null); - // And log what's still being held - logSyncHolds(); - } - return; - } else { - // If this is our first time through the loop, shut down running service threads - if (!waiting) { - waiting = true; - stopServiceThreads(); - } - // Wait until a network is connected (or 10 mins), but let the device sleep - // We'll set an alarm just in case we don't get notified (bugs happen) - synchronized (sConnectivityLock) { - runAsleep(EXTRA_MAILBOX_ID, CONNECTIVITY_WAIT_TIME+5*SECONDS); - try { - log("Connectivity lock..."); - sConnectivityHold = true; - sConnectivityLock.wait(CONNECTIVITY_WAIT_TIME); - log("Connectivity lock released..."); - } catch (InterruptedException e) { - // This is fine; we just go around the loop again - } finally { - sConnectivityHold = false; - } - runAwake(EXTRA_MAILBOX_ID); - } - } - } - } - - /** - * Note that there are two ways the EAS SyncServiceManager service can be created: - * - * 1) as a background service instantiated via startService (which happens on boot, when the - * first EAS account is created, etc), in which case the service thread is spun up, mailboxes - * sync, etc. and - * 2) to execute an RPC call from the UI, in which case the background service will already be - * running most of the time (unless we're creating a first EAS account) - * - * If the running background service detects that there are no EAS accounts (on boot, if none - * were created, or afterward if the last remaining EAS account is deleted), it will call - * stopSelf() to terminate operation. - * - * The goal is to ensure that the background service is running at all times when there is at - * least one EAS account in existence - * - * Because there are edge cases in which our process can crash (typically, this has been seen - * in UI crashes, ANR's, etc.), it's possible for the UI to start up again without the - * background service having been started. We explicitly try to start the service in Welcome - * (to handle the case of the app having been reloaded). We also start the service on any - * startSync call (if it isn't already running) - */ - @SuppressWarnings("deprecation") - @Override - public void onCreate() { - TAG = getClass().getSimpleName(); - EmailContent.init(this); - Utility.runAsync(new Runnable() { - @Override - public void run() { - // Quick checks first, before getting the lock - if (sStartingUp) return; - synchronized (sSyncLock) { - alwaysLog("!!! onCreate"); - // Try to start up properly; we might be coming back from a crash that the Email - // application isn't aware of. - startService(getServiceIntent()); - if (sStop) { - return; - } - } - }}); - } - - @SuppressWarnings("deprecation") - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - alwaysLog("!!! onStartCommand, startingUp = " + sStartingUp + ", running = " + - (INSTANCE != null)); - if (!sStartingUp && INSTANCE == null) { - sStartingUp = true; - Utility.runAsync(new Runnable() { - @Override - public void run() { - try { - synchronized (sSyncLock) { - // SyncServiceManager cannot start unless we connect to AccountService - if (!new AccountServiceProxy(SyncManager.this).test()) { - alwaysLog("!!! Email application not found; stopping self"); - stopSelf(); - } - if (sDeviceId == null) { - try { - String deviceId = getDeviceId(SyncManager.this); - if (deviceId != null) { - sDeviceId = deviceId; - } - } catch (IOException e) { - } - if (sDeviceId == null) { - alwaysLog("!!! deviceId unknown; stopping self and retrying"); - stopSelf(); - // Try to restart ourselves in a few seconds - Utility.runAsync(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - } - startService(getServiceIntent()); - }}); - return; - } - } - // Run the reconciler and clean up mismatched accounts - if we weren't - // running when accounts were deleted, it won't have been called. - runAccountReconcilerSync(SyncManager.this); - // Update other services depending on final account configuration - maybeStartSyncServiceManagerThread(); - if (sServiceThread == null) { - log("!!! EAS SyncServiceManager, stopping self"); - stopSelf(); - } else if (sStop) { - // If we were trying to stop, attempt a restart in 5 secs - setAlarm(SYNC_SERVICE_MAILBOX_ID, 5*SECONDS); - } else { - mServiceStartTime = System.currentTimeMillis(); - } - } - } finally { - sStartingUp = false; - } - }}); - } - return Service.START_STICKY; - } - - public static void reconcileAccounts(Context context) { - SyncManager ssm = INSTANCE; - if (ssm != null) { - ssm.runAccountReconcilerSync(context); - } - } - - protected abstract void runAccountReconcilerSync(Context context); - - @SuppressWarnings("deprecation") - @Override - public void onDestroy() { - log("!!! onDestroy"); - // Handle shutting down off the UI thread - Utility.runAsync(new Runnable() { - @Override - public void run() { - // Quick checks first, before getting the lock - if (INSTANCE == null || sServiceThread == null) return; - synchronized(sSyncLock) { - // Stop the sync manager thread and return - if (sServiceThread != null) { - sStop = true; - sServiceThread.interrupt(); - } - } - }}); - } - - void maybeStartSyncServiceManagerThread() { - // Start our thread... - // See if there are any EAS accounts; otherwise, just go away - if (sServiceThread == null || !sServiceThread.isAlive()) { - AccountList currentAccounts = new AccountList(); - try { - collectAccounts(this, currentAccounts); - } catch (ProviderUnavailableException e) { - // Just leave if EmailProvider is unavailable - return; - } - if (!currentAccounts.isEmpty()) { - log(sServiceThread == null ? "Starting thread..." : "Restarting thread..."); - sServiceThread = new Thread(this, TAG); - INSTANCE = this; - sServiceThread.start(); - } - } - } - - /** - * Start up the SyncManager service if it's not already running - * This is a stopgap for cases in which SyncServiceManager died (due to a crash somewhere in - * com.android.email) and hasn't been restarted. See the comment for onCreate for details - */ - static void checkSyncManagerRunning() { - SyncManager ssm = INSTANCE; - if (ssm == null) return; - if (sServiceThread == null) { - log("!!! checkSyncServiceManagerServiceRunning; starting service..."); - ssm.startService(new Intent(ssm, SyncManager.class)); - } - } - - @SuppressWarnings("deprecation") - @Override - public void run() { - sStop = false; - alwaysLog("Service thread running"); - - TempDirectory.setTempDirectory(this); - - // Synchronize here to prevent a shutdown from happening while we initialize our observers - // and receivers - synchronized (sSyncLock) { - if (INSTANCE != null) { - mResolver = getContentResolver(); - - // Set up our observers; we need them to know when to start/stop various syncs based - // on the insert/delete/update of mailboxes and accounts - // We also observe synced messages to trigger upsyncs at the appropriate time - mAccountObserver = getAccountObserver(mHandler); - mResolver.registerContentObserver(Account.NOTIFIER_URI, true, mAccountObserver); - mMailboxObserver = new MailboxObserver(mHandler); - mResolver.registerContentObserver(Mailbox.CONTENT_URI, false, mMailboxObserver); - mSyncedMessageObserver = new SyncedMessageObserver(mHandler); - mResolver.registerContentObserver(Message.SYNCED_CONTENT_URI, true, - mSyncedMessageObserver); - - // Set up receivers for connectivity and background data setting - mConnectivityReceiver = new ConnectivityReceiver(); - registerReceiver(mConnectivityReceiver, new IntentFilter( - ConnectivityManager.CONNECTIVITY_ACTION)); - - mBackgroundDataSettingReceiver = new ConnectivityReceiver(); - registerReceiver(mBackgroundDataSettingReceiver, new IntentFilter( - ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED)); - // Save away the current background data setting; we'll keep track of it with the - // receiver we just registered - ConnectivityManager cm = (ConnectivityManager)getSystemService( - Context.CONNECTIVITY_SERVICE); - mBackgroundData = cm.getBackgroundDataSetting(); - - onStartup(); - } - } - - try { - // Loop indefinitely until we're shut down - while (!sStop) { - runAwake(EXTRA_MAILBOX_ID); - waitForConnectivity(); - mNextWaitReason = null; - long nextWait = checkMailboxes(); - try { - synchronized (this) { - if (!mKicked) { - if (nextWait < 0) { - log("Negative wait? Setting to 1s"); - nextWait = 1*SECONDS; - } - if (nextWait > 10*SECONDS) { - if (mNextWaitReason != null) { - log("Next awake " + nextWait / 1000 + "s: " + mNextWaitReason); - } - runAsleep(EXTRA_MAILBOX_ID, nextWait + (3*SECONDS)); - } - wait(nextWait); - } - } - } catch (InterruptedException e) { - // Needs to be caught, but causes no problem - log("SyncServiceManager interrupted"); - } finally { - synchronized (this) { - if (mKicked) { - //log("Wait deferred due to kick"); - mKicked = false; - } - } - } - } - log("Shutdown requested"); - } catch (ProviderUnavailableException pue) { - // Shutdown cleanly in this case - // NOTE: Sync adapters will also crash with this error, but that is already handled - // in the adapters themselves, i.e. they return cleanly via done(). When the Email - // process starts running again, remote processes will be started again in due course - Log.e(TAG, "EmailProvider unavailable; shutting down"); - // Ask for our service to be restarted; this should kick-start the Email process as well - startService(new Intent(this, SyncManager.class)); - } catch (RuntimeException e) { - // Crash; this is a completely unexpected runtime error - Log.e(TAG, "RuntimeException", e); - throw e; - } finally { - shutdown(); - } - } - - private void shutdown() { - synchronized (sSyncLock) { - // If INSTANCE is null, we've already been shut down - if (INSTANCE != null) { - log("Shutting down..."); - - // Stop our running syncs - stopServiceThreads(); - - // Stop receivers - if (mConnectivityReceiver != null) { - unregisterReceiver(mConnectivityReceiver); - } - if (mBackgroundDataSettingReceiver != null) { - unregisterReceiver(mBackgroundDataSettingReceiver); - } - - // Unregister observers - ContentResolver resolver = getContentResolver(); - if (mSyncedMessageObserver != null) { - resolver.unregisterContentObserver(mSyncedMessageObserver); - mSyncedMessageObserver = null; - } - if (mAccountObserver != null) { - resolver.unregisterContentObserver(mAccountObserver); - mAccountObserver = null; - } - if (mMailboxObserver != null) { - resolver.unregisterContentObserver(mMailboxObserver); - mMailboxObserver = null; - } - unregisterCalendarObservers(); - - // Clear pending alarms and associated Intents - clearAlarms(); - - // Release our wake lock, if we have one - synchronized (mWakeLocks) { - if (mWakeLock != null) { - mWakeLock.release(); - mWakeLock = null; - } - } - - INSTANCE = null; - sServiceThread = null; - sStop = false; - log("Goodbye"); - } - } - } - - /** - * Release a mailbox from the service map and release its wake lock. - * NOTE: This method MUST be called while holding sSyncLock! - * - * @param mailboxId the id of the mailbox to be released - */ - public void releaseMailbox(long mailboxId) { - mServiceMap.remove(mailboxId); - releaseWakeLock(mailboxId); - } - - /** - * Retrieve a running sync service for the passed-in mailbox id in a threadsafe manner - * - * @param mailboxId the id of the mailbox whose service is to be found - * @return the running service (a subclass of AbstractSyncService) or null if none - */ - public AbstractSyncService getRunningService(long mailboxId) { - synchronized(sSyncLock) { - return mServiceMap.get(mailboxId); - } - } - - /** - * Check whether an Outbox (referenced by a Cursor) has any messages that can be sent - * @param c the cursor to an Outbox - * @return true if there is mail to be sent - */ - private boolean hasSendableMessages(Cursor outboxCursor) { - Cursor c = mResolver.query(Message.CONTENT_URI, Message.ID_COLUMN_PROJECTION, - MAILBOX_KEY_AND_NOT_SEND_FAILED, - new String[] {Long.toString(outboxCursor.getLong(Mailbox.CONTENT_ID_COLUMN))}, - null); - try { - while (c.moveToNext()) { - if (!Utility.hasUnloadedAttachments(this, c.getLong(Message.CONTENT_ID_COLUMN))) { - return true; - } - } - } finally { - if (c != null) { - c.close(); - } - } - return false; - } - - /** - * Taken from ConnectivityManager using public constants - */ - public static boolean isNetworkTypeMobile(int networkType) { - switch (networkType) { - case ConnectivityManager.TYPE_MOBILE: - case ConnectivityManager.TYPE_MOBILE_MMS: - case ConnectivityManager.TYPE_MOBILE_SUPL: - case ConnectivityManager.TYPE_MOBILE_DUN: - case ConnectivityManager.TYPE_MOBILE_HIPRI: - return true; - default: - return false; - } - } - - /** - * Determine whether the account is allowed to sync automatically, as opposed to manually, based - * on whether the "require manual sync when roaming" policy is in force and applicable - * @param account the account - * @return whether or not the account can sync automatically - */ - /*package*/ public static boolean canAutoSync(Account account) { - SyncManager ssm = INSTANCE; - if (ssm == null) { - return false; - } - NetworkInfo networkInfo = ssm.mNetworkInfo; - - // Enforce manual sync only while roaming here - long policyKey = account.mPolicyKey; - // Quick exit from this check - if ((policyKey != 0) && (networkInfo != null) && - isNetworkTypeMobile(networkInfo.getType())) { - // We'll cache the Policy data here - Policy policy = account.mPolicy; - if (policy == null) { - policy = Policy.restorePolicyWithId(INSTANCE, policyKey); - account.mPolicy = policy; - if (!PolicyServiceProxy.isActive(ssm, policy)) { - PolicyServiceProxy.setAccountHoldFlag(ssm, account, true); - log("canAutoSync; policies not active, set hold flag"); - return false; - } - } - if (policy != null && policy.mRequireManualSyncWhenRoaming && networkInfo.isRoaming()) { - return false; - } - } - return true; - } - - /** - * Convenience method to determine whether Email sync is enabled for a given account - * @param account the Account in question - * @return whether Email sync is enabled - */ - private boolean canSyncEmail(android.accounts.Account account) { - return ContentResolver.getSyncAutomatically(account, EmailContent.AUTHORITY); - } - - /** - * Determine whether a mailbox of a given type in a given account can be synced automatically - * by SyncServiceManager. This is an increasingly complex determination, taking into account - * security policies and user settings (both within the Email application and in the Settings - * application) - * - * @param account the Account that the mailbox is in - * @param type the type of the Mailbox - * @return whether or not to start a sync - */ - private boolean isMailboxSyncable(Account account, int type) { - // This 'if' statement performs checks to see whether or not a mailbox is a - // candidate for syncing based on policies, user settings, & other restrictions - if (type == Mailbox.TYPE_OUTBOX) { - // Outbox is always syncable - return true; - } else if (type == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) { - // Always sync EAS mailbox unless master sync is off - return ContentResolver.getMasterSyncAutomatically(); - } else if (type == Mailbox.TYPE_CONTACTS || type == Mailbox.TYPE_CALENDAR) { - // Contacts/Calendar obey this setting from ContentResolver - if (!ContentResolver.getMasterSyncAutomatically()) { - return false; - } - // Get the right authority for the mailbox - String authority; - if (type == Mailbox.TYPE_CONTACTS) { - authority = ContactsContract.AUTHORITY; - } else { - authority = CalendarContract.AUTHORITY; - if (!mCalendarObservers.containsKey(account.mId)){ - // Make sure we have an observer for this Calendar, as - // we need to be able to detect sync state changes, sigh - registerCalendarObserver(account); - } - } - // See if "sync automatically" is set; if not, punt - if (!ContentResolver.getSyncAutomatically(account.mAmAccount, authority)) { - return false; - // See if the calendar is enabled from the Calendar app UI; if not, punt - } else if ((type == Mailbox.TYPE_CALENDAR) && !isCalendarEnabled(account.mId)) { - return false; - } - // Never automatically sync trash - } else if (type == Mailbox.TYPE_TRASH) { - return false; - // For non-outbox, non-account mail, we do three checks: - // 1) are we restricted by policy (i.e. manual sync only), - // 2) has the user checked the "Sync Email" box in Account Settings, and - // 3) does the user have the master "background data" box checked in Settings - } else if (!canAutoSync(account) || !canSyncEmail(account.mAmAccount) || !mBackgroundData) { - return false; - } - return true; - } - - private long checkMailboxes () { - // First, see if any running mailboxes have been deleted - ArrayList deletedMailboxes = new ArrayList(); - synchronized (sSyncLock) { - for (long mailboxId: mServiceMap.keySet()) { - Mailbox m = Mailbox.restoreMailboxWithId(this, mailboxId); - if (m == null) { - deletedMailboxes.add(mailboxId); - } - } - // If so, stop them or remove them from the map - for (Long mailboxId: deletedMailboxes) { - AbstractSyncService svc = mServiceMap.get(mailboxId); - if (svc == null || svc.mThread == null) { - releaseMailbox(mailboxId); - continue; - } else { - boolean alive = svc.mThread.isAlive(); - log("Deleted mailbox: " + svc.mMailboxName); - if (alive) { - stopManualSync(mailboxId); - } else { - log("Removing from serviceMap"); - releaseMailbox(mailboxId); - } - } - } - } - - long nextWait = SYNC_SERVICE_HEARTBEAT_TIME; - long now = System.currentTimeMillis(); - - // Start up threads that need it; use a query which finds eas mailboxes where the - // the sync interval is not "never". This is the set of mailboxes that we control - if (mAccountObserver == null) { - log("mAccountObserver null; service died??"); - return nextWait; - } - - Cursor c = getContentResolver().query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION, - mAccountObserver.getSyncableMailboxWhere(), null, null); - if (c == null) throw new ProviderUnavailableException(); - try { - while (c.moveToNext()) { - long mailboxId = c.getLong(Mailbox.CONTENT_ID_COLUMN); - AbstractSyncService service = getRunningService(mailboxId); - if (service == null) { - // Get the cached account - Account account = getAccountById(c.getInt(Mailbox.CONTENT_ACCOUNT_KEY_COLUMN)); - if (account == null) continue; - - // We handle a few types of mailboxes specially - int mailboxType = c.getInt(Mailbox.CONTENT_TYPE_COLUMN); - if (!isMailboxSyncable(account, mailboxType)) { - continue; - } - - // Check whether we're in a hold (temporary or permanent) - SyncError syncError = mSyncErrorMap.get(mailboxId); - if (syncError != null) { - // Nothing we can do about fatal errors - if (syncError.fatal) continue; - if (now < syncError.holdEndTime) { - // If release time is earlier than next wait time, - // move next wait time up to the release time - if (syncError.holdEndTime < now + nextWait) { - nextWait = syncError.holdEndTime - now; - mNextWaitReason = "Release hold"; - } - continue; - } else { - // Keep the error around, but clear the end time - syncError.holdEndTime = 0; - } - } - - // Otherwise, we use the sync interval - long syncInterval = c.getInt(Mailbox.CONTENT_SYNC_INTERVAL_COLUMN); - if (syncInterval == Mailbox.CHECK_INTERVAL_PUSH) { - Mailbox m = EmailContent.getContent(c, Mailbox.class); - requestSync(m, SYNC_PUSH, null); - } else if (mailboxType == Mailbox.TYPE_OUTBOX) { - if (hasSendableMessages(c)) { - Mailbox m = EmailContent.getContent(c, Mailbox.class); - startServiceThread(getServiceForMailbox(this, m)); - } - } else if (syncInterval > 0 && syncInterval <= ONE_DAY_MINUTES) { - long lastSync = c.getLong(Mailbox.CONTENT_SYNC_TIME_COLUMN); - long sinceLastSync = now - lastSync; - long toNextSync = syncInterval*MINUTES - sinceLastSync; - String name = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN); - if (toNextSync <= 0) { - Mailbox m = EmailContent.getContent(c, Mailbox.class); - requestSync(m, SYNC_SCHEDULED, null); - } else if (toNextSync < nextWait) { - nextWait = toNextSync; - if (sUserLog) { - log("Next sync for " + name + " in " + nextWait/1000 + "s"); - } - mNextWaitReason = "Scheduled sync, " + name; - } else if (sUserLog) { - log("Next sync for " + name + " in " + toNextSync/1000 + "s"); - } - } - } else { - Thread thread = service.mThread; - // Look for threads that have died and remove them from the map - if (thread != null && !thread.isAlive()) { - if (sUserLog) { - log("Dead thread, mailbox released: " + - c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN)); - } - synchronized (sSyncLock) { - releaseMailbox(mailboxId); - } - // Restart this if necessary - if (nextWait > 3*SECONDS) { - nextWait = 3*SECONDS; - mNextWaitReason = "Clean up dead thread(s)"; - } - } else { - long requestTime = service.mRequestTime; - if (requestTime > 0) { - long timeToRequest = requestTime - now; - if (timeToRequest <= 0) { - service.mRequestTime = 0; - service.alarm(); - } else if (requestTime > 0 && timeToRequest < nextWait) { - if (timeToRequest < 11*MINUTES) { - nextWait = timeToRequest < 250 ? 250 : timeToRequest; - mNextWaitReason = "Sync data change"; - } else { - log("Illegal timeToRequest: " + timeToRequest); - } - } - } - } - } - } - } finally { - c.close(); - } - return nextWait; - } - - static public void serviceRequest(long mailboxId, int reason) { - serviceRequest(mailboxId, 5*SECONDS, reason); - } - - /** - * Return a boolean indicating whether the mailbox can be synced - * @param m the mailbox - * @return whether or not the mailbox can be synced - */ - public static boolean isSyncable(Mailbox m) { - return m.mType != Mailbox.TYPE_DRAFTS - && m.mType != Mailbox.TYPE_OUTBOX - && m.mType != Mailbox.TYPE_SEARCH - && m.mType < Mailbox.TYPE_NOT_SYNCABLE; - } - - static public void serviceRequest(long mailboxId, long ms, int reason) { - SyncManager ssm = INSTANCE; - if (ssm == null) return; - Mailbox m = Mailbox.restoreMailboxWithId(ssm, mailboxId); - if (m == null || !isSyncable(m)) return; - try { - AbstractSyncService service = ssm.getRunningService(mailboxId); - if (service != null) { - service.mRequestTime = System.currentTimeMillis() + ms; - kick("service request"); - } else { - startManualSync(mailboxId, reason, null); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - static public void serviceRequestImmediate(long mailboxId) { - SyncManager ssm = INSTANCE; - if (ssm == null) return; - AbstractSyncService service = ssm.getRunningService(mailboxId); - if (service != null) { - service.mRequestTime = System.currentTimeMillis(); - Mailbox m = Mailbox.restoreMailboxWithId(ssm, mailboxId); - if (m != null) { - service.mAccount = Account.restoreAccountWithId(ssm, m.mAccountKey); - service.mMailbox = m; - kick("service request immediate"); - } - } - } - - static public void sendMessageRequest(Request req) { - SyncManager ssm = INSTANCE; - if (ssm == null) return; - Message msg = Message.restoreMessageWithId(ssm, req.mMessageId); - if (msg == null) return; - long mailboxId = msg.mMailboxKey; - Mailbox mailbox = Mailbox.restoreMailboxWithId(ssm, mailboxId); - if (mailbox == null) return; - - // If we're loading an attachment for Outbox, we want to look at the source message - // to find the loading mailbox - if (mailbox.mType == Mailbox.TYPE_OUTBOX) { - long sourceId = Utility.getFirstRowLong(ssm, Body.CONTENT_URI, - new String[] {BodyColumns.SOURCE_MESSAGE_KEY}, - BodyColumns.MESSAGE_KEY + "=?", - new String[] {Long.toString(msg.mId)}, null, 0, -1L); - if (sourceId != -1L) { - EmailContent.Message sourceMsg = - EmailContent.Message.restoreMessageWithId(ssm, sourceId); - if (sourceMsg != null) { - mailboxId = sourceMsg.mMailboxKey; - } - } - } - sendRequest(mailboxId, req); - } - - static public void sendRequest(long mailboxId, Request req) { - SyncManager ssm = INSTANCE; - if (ssm == null) return; - AbstractSyncService service = ssm.getRunningService(mailboxId); - if (service == null) { - startManualSync(mailboxId, SYNC_SERVICE_PART_REQUEST, req); - kick("part request"); - } else { - service.addRequest(req); - } - } - - /** - * Determine whether a given Mailbox can be synced, i.e. is not already syncing and is not in - * an error state - * - * @param mailboxId - * @return whether or not the Mailbox is available for syncing (i.e. is a valid push target) - */ - static public int pingStatus(long mailboxId) { - SyncManager ssm = INSTANCE; - if (ssm == null) return PING_STATUS_OK; - // Already syncing... - if (ssm.getRunningService(mailboxId) != null) { - return PING_STATUS_RUNNING; - } - // No errors or a transient error, don't ping... - SyncError error = ssm.mSyncErrorMap.get(mailboxId); - if (error != null) { - if (error.fatal) { - return PING_STATUS_UNABLE; - } else if (error.holdEndTime > 0) { - return PING_STATUS_WAITING; - } - } - return PING_STATUS_OK; - } - - static public void startManualSync(long mailboxId, int reason, Request req) { - SyncManager ssm = INSTANCE; - if (ssm == null) return; - synchronized (sSyncLock) { - AbstractSyncService svc = ssm.mServiceMap.get(mailboxId); - if (svc == null) { - if (ssm.mSyncErrorMap.containsKey(mailboxId) && reason == SyncManager.SYNC_UPSYNC) { - return; - } else if (reason != SyncManager.SYNC_UPSYNC) { - ssm.mSyncErrorMap.remove(mailboxId); - } - Mailbox m = Mailbox.restoreMailboxWithId(ssm, mailboxId); - if (m != null) { - log("Starting sync for " + m.mDisplayName); - ssm.requestSync(m, reason, req); - } - } else { - // If this is a ui request, set the sync reason for the service - if (reason >= SYNC_CALLBACK_START) { - svc.mSyncReason = reason; - } - } - } - } - - // DO NOT CALL THIS IN A LOOP ON THE SERVICEMAP - static public void stopManualSync(long mailboxId) { - SyncManager ssm = INSTANCE; - if (ssm == null) return; - synchronized (sSyncLock) { - AbstractSyncService svc = ssm.mServiceMap.get(mailboxId); - if (svc != null) { - log("Stopping sync for " + svc.mMailboxName); - svc.stop(); - svc.mThread.interrupt(); - ssm.releaseWakeLock(mailboxId); - } - } - } - - /** - * Wake up SyncServiceManager to check for mailboxes needing service - */ - static public void kick(String reason) { - SyncManager ssm = INSTANCE; - if (ssm != null) { - synchronized (ssm) { - //INSTANCE.log("Kick: " + reason); - ssm.mKicked = true; - ssm.notify(); - } - } - if (sConnectivityLock != null) { - synchronized (sConnectivityLock) { - sConnectivityLock.notify(); - } - } - } - - /** - * Tell SyncServiceManager to remove the mailbox from the map of mailboxes with sync errors - * @param mailboxId the id of the mailbox - */ - static public void removeFromSyncErrorMap(long mailboxId) { - SyncManager ssm = INSTANCE; - if (ssm != null) { - ssm.mSyncErrorMap.remove(mailboxId); - } - } - - private boolean isRunningInServiceThread(long mailboxId) { - AbstractSyncService syncService = getRunningService(mailboxId); - Thread thisThread = Thread.currentThread(); - return syncService != null && syncService.mThread != null && - thisThread == syncService.mThread; - } - - /** - * Sent by services indicating that their thread is finished; action depends on the exitStatus - * of the service. - * - * @param svc the service that is finished - */ - static public void done(AbstractSyncService svc) { - SyncManager ssm = INSTANCE; - if (ssm == null) return; - synchronized(sSyncLock) { - long mailboxId = svc.mMailboxId; - // If we're no longer the syncing thread for the mailbox, just return - if (!ssm.isRunningInServiceThread(mailboxId)) { - return; - } - ssm.releaseMailbox(mailboxId); - ssm.setMailboxSyncStatus(mailboxId, EmailContent.SYNC_STATUS_NONE); - - ConcurrentHashMap errorMap = ssm.mSyncErrorMap; - SyncError syncError = errorMap.get(mailboxId); - - int exitStatus = svc.mExitStatus; - Mailbox m = Mailbox.restoreMailboxWithId(ssm, mailboxId); - if (m == null) return; - - if (exitStatus != AbstractSyncService.EXIT_LOGIN_FAILURE) { - long accountId = m.mAccountKey; - Account account = Account.restoreAccountWithId(ssm, accountId); - if (account == null) return; - if (ssm.releaseSyncHolds(ssm, - AbstractSyncService.EXIT_LOGIN_FAILURE, account)) { - new AccountServiceProxy(ssm).notifyLoginSucceeded(accountId); - } - } - - int lastResult = EmailContent.LAST_SYNC_RESULT_SUCCESS; - // For error states, whether the error is fatal (won't automatically be retried) - boolean errorIsFatal = true; - try { - switch (exitStatus) { - case AbstractSyncService.EXIT_DONE: - if (svc.hasPendingRequests()) { - // TODO Handle this case - } - errorMap.remove(mailboxId); - // If we've had a successful sync, clear the shutdown count - synchronized (SyncManager.class) { - sClientConnectionManagerShutdownCount = 0; - } - // Leave now; other statuses are errors - return; - // I/O errors get retried at increasing intervals - case AbstractSyncService.EXIT_IO_ERROR: - if (syncError != null) { - syncError.escalate(); - log(m.mDisplayName + " held for " + (syncError.holdDelay/ 1000) + "s"); - return; - } else { - log(m.mDisplayName + " added to syncErrorMap, hold for 15s"); - } - lastResult = EmailContent.LAST_SYNC_RESULT_CONNECTION_ERROR; - errorIsFatal = false; - break; - // These errors are not retried automatically - case AbstractSyncService.EXIT_LOGIN_FAILURE: - new AccountServiceProxy(ssm).notifyLoginFailed(m.mAccountKey, svc.mExitReason); - lastResult = EmailContent.LAST_SYNC_RESULT_AUTH_ERROR; - break; - case AbstractSyncService.EXIT_SECURITY_FAILURE: - case AbstractSyncService.EXIT_ACCESS_DENIED: - lastResult = EmailContent.LAST_SYNC_RESULT_SECURITY_ERROR; - break; - case AbstractSyncService.EXIT_EXCEPTION: - lastResult = EmailContent.LAST_SYNC_RESULT_INTERNAL_ERROR; - break; - } - // Add this box to the error map - errorMap.put(mailboxId, ssm.new SyncError(exitStatus, errorIsFatal)); - } finally { - // Always set the last result - ssm.setMailboxLastSyncResult(mailboxId, lastResult); - kick("sync completed"); - } - } - } - - /** - * Given the status string from a Mailbox, return the type code for the last sync - * @param status the syncStatus column of a Mailbox - * @return - */ - static public int getStatusType(String status) { - if (status == null) { - return -1; - } else { - return status.charAt(STATUS_TYPE_CHAR) - '0'; - } - } - - /** - * Given the status string from a Mailbox, return the change count for the last sync - * The change count is the number of adds + deletes + changes in the last sync - * @param status the syncStatus column of a Mailbox - * @return - */ - static public int getStatusChangeCount(String status) { - try { - String s = status.substring(STATUS_CHANGE_COUNT_OFFSET); - return Integer.parseInt(s); - } catch (RuntimeException e) { - return -1; - } - } - - static public Context getContext() { - return INSTANCE; - } - - private void writeWakeLockTimes(PrintWriter pw, HashMap map, boolean historical) { - long now = System.currentTimeMillis(); - for (long mailboxId: map.keySet()) { - Long time = map.get(mailboxId); - if (time == null) { - // Just in case... - continue; - } - Mailbox mailbox = Mailbox.restoreMailboxWithId(this, mailboxId); - StringBuilder sb = new StringBuilder(); - if (mailboxId == EXTRA_MAILBOX_ID) { - sb.append(" SyncManager"); - } else if (mailbox == null) { - sb.append(" Mailbox " + mailboxId + " (deleted?)"); - } else { - String protocol = Account.getProtocol(this, mailbox.mAccountKey); - sb.append(" Mailbox " + mailboxId + " (" + protocol + ", type " + - mailbox.mType + ")"); - } - long logTime = historical ? time : (now - time); - sb.append(" held for " + (logTime / 1000) + "s"); - pw.println(sb.toString()); - } - } - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - long uptime = System.currentTimeMillis() - mServiceStartTime; - pw.println("SyncManager: " + TAG + " up for " + (uptime / 1000 / 60) + " m"); - if (mWakeLock != null) { - pw.println(" Holding WakeLock"); - writeWakeLockTimes(pw, mWakeLocks, false); - } else { - pw.println(" Not holding WakeLock"); - } - if (!mWakeLocksHistory.isEmpty()) { - pw.println(" Historical times"); - writeWakeLockTimes(pw, mWakeLocksHistory, true); - } - } -} diff --git a/proguard.flags b/proguard.flags index 4ead936ec..57e7b0704 100644 --- a/proguard.flags +++ b/proguard.flags @@ -215,3 +215,8 @@ *** toByteArray(java.io.Reader, java.lang.String); *** toByteArray(java.lang.String); } + +-keepclasseswithmembers class com.android.email.activity.ThreePaneLayout { + *** setMessageListWidthAnim(...); + *** setMailboxListLeftAnim(...); +} diff --git a/remove-exchange-support.sh b/remove-exchange-support.sh new file mode 100755 index 000000000..2c08f02b6 --- /dev/null +++ b/remove-exchange-support.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# You can remove exchange by running this script. +# + +set -e # fail fast + +# Step 0. Make sure we're in the right directory, and the user really wants it. + +if [[ ! -d src/com/android/email/ ]] ; then + echo "Run the script in the root of the email source tree." 1>&2 + exit 1 +fi + +echo "" +echo -n "Do you wish to remove exchange support from the email app? (y/N):" + +read answer +if [[ "$answer" != y ]] ; then + echo "Aborted." 1>&2 + exit 1 +fi + + +# Step 1. Remove all Exchange related packages. + +rm -fr src/com/android/exchange/ \ + tests/src/com/android/exchange/ + + +# Step 2. Remove lines surrounded by START-EXCHANGE and END-EXCHANGE + +find . \( -name '*.java' -o -name '*.xml' -o -name 'Android.mk' \) -print0 | + xargs -0 sed -i "" -e '/EXCHANGE-REMOVE-SECTION-START/,/EXCHANGE-REMOVE-SECTION-END/d' + + +# Step 3. Remove all imports from com.android.exchange (and its subpackages). + +find . -name '*.java' -print0 | + xargs -0 sed -i "" -e '/^import com\.android\.exchange/d' + + +echo "" +echo "Exchange support has been successfully removed." + +exit 0 diff --git a/res/drawable-hdpi/attachment_bg_holo.9.png b/res/drawable-hdpi/attachment_bg_holo.9.png new file mode 100644 index 0000000000000000000000000000000000000000..43983c5fe7a9178e653961341a98b5f7e26c3ee3 GIT binary patch literal 288 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%EX7WqAsj$Z!;#Vf4nJ zaCd?*qxs3xYk`7?JY5_^JUZV_bL2W?AmDo6MAY@*x;;_rZ|vSFys{(Uxa%YNFOp`csf#D0o)<%<=iea@ZxpRs`ua{1r^Dg(c z*^jg>T;)#x@|7#N8d$H&pRNzLWp$0n>z$X~>=t_8n`=b_5M5)@?RqM5AwI=%b(z@W zha7VnY&WRxz06p_VE%w7M%m*7^C>?0{VNxwH{AUgJmJ;`;d`>n+7`@;WpoJ@oS5mT e$N7Wt6KmH;^&pYjdl!JdVeoYIb6Mw<&;$TvyKH9w literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/btn_check_off_normal_holo_light.png b/res/drawable-hdpi/btn_check_off_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..351a4f0887600f7610556cd6c569033b161eca80 GIT binary patch literal 119 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjcAhSdAs)w*fBgS%&#Y_6d{>8| z$s{XjjhKnKxpxrr#Oih&R$* RAPzK%!PC{xWt~$(698Y4Bewtm literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/btn_check_on_normal_holo_light.png b/res/drawable-hdpi/btn_check_on_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..4b4364c92910461df237669173bb510ea6915417 GIT binary patch literal 374 zcmV-+0g3*JP)F4<7gc&HDHs z0k&XR0Hk@z!MAVUcKi45-xnmq{{V3URV=uB_b$lg-(V&KE&2(I=sD*R)%< zY0mf{a9AL@p(VUSZHGc2kFSf+-K?HnU*jE;4;|{w5jl0JRZ7$}Y3hc6Lme*jmTu|f z*YlXz5VFu?eYf1Hz2E0lHwu;4m_I*v=6lV4*M zZ}{x@y3k_Y{^aHf6=$-Fiz-#sYaTC;klN}vBYSaszR}TOtK{z*JclICM}?YQU;Jy1 zsfOXjl(%9tES=tcO8oPE8_%H%{cPsueVeWIZzb;9IxU-J@3v!&(PkX{6}G%*6|Eob zv~tYZW;yqSX7r<_I*Sd>E!NNKoz(xfqyGBshDS>eeLt3Yl%<%C;*a_mikvy&I7|BGBJf4VO|A?w%YB-Z8r zf}NFHd2by|{JQa2iNT6re7|=3s9s;dpv-u}QQ+Fm31($S_pfT(eL5s4B=6IQOTVu= zWNbKjY0Jjz>v$fP%=B7!uvX#wC8pQGKPUM=dT#lcIaO;Xo3m6(@#9~AX0P#$e1G)s z&M9+NZw`86BoUtcuYcK__1Pzi>p>bnthHZ!?DgbJ&ri-$lsVVXrLvYM@S@I^YZfbx z$1*R^lG>7TT$|UgLWa9$*7q`-XET09FjQr=zvvK(I>fWsA;T;9jmKHhBmXWe_f1;+ z){;qgp7~|TxrX(st5sk0idKfys_Z_n>B1c44L5Z<)AZZsJeF7%6}T+ma>9Nksa&p` zIXmjS{Z{#?a&+<~2X5KF&QWGlFz4HOCnf$&vpfGw-T2#*R*jpItN(p74t3qQ-yo3t z7FTcUyQG^Q3!^8q)!tjb@5!N}^PU^j%R*m2buI2OzZCU+MefH%yEtr8|9<#(Ytq{H zMXwe;3V67nRV`tcI#)Q?ezELn+ZUen4)X~3;J>fN_=~g5{C=NltS~)1GpSef zSd(4!=U2O;b?bIMT5$N++~Ttlm$%Fc54&=|?&IuPXRZeR{6D4n;bv6Z2@u7SCgfq`@f<5Uz4x%nxXX_e?246O{!AR11D zCoKSKkObKfoS#-wo>-L1;Fyx1l&avFo0y&&l$w}QS$Hzl2B?U^)78&qol`;+0DPq+ A1poj5 literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/btn_no_off.png.png b/res/drawable-hdpi/btn_no_off.png.png new file mode 100644 index 0000000000000000000000000000000000000000..f88b5a7256959fc71bcee25272c7bba6cd54462a GIT binary patch literal 1020 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfizez!?|}o;S3DSES@foArXh)UO$~5>MC>KE9lvmQYt1$V|{=}pn>*-ojD>%KI#BQ}3O1b+?xfay5X@j_v z=Aus<48QMqaOKNB`O=&B^WObA_uabu*_ku%Hwa2^ym#WtWRa@3TW^;|=N$E|-|?tv zW<~AYrVC0Ep9@;wu}uXND*mAWY z(R%BWzrlV->ZOEQ&fdGkALcW=$NbcjoX>mynl*acb!>kn1{B}O{O8H!9WkjwzxQNm z%w1LfIDV<*+Elq)PjdN{3NLM}SbmG?NnZN?t^Vv*ylMMSKJ7M+I%QLOK~Kf5Yx$yA zf>E+tb#r+tcA6}95%{*myV|HOv{`RU)!pC+=>}3|*9BU{+7I^onKt~tQ+-bPvB!xJ z(TZbyf8IvOPTt%#zhimwqh#$9%l3zg*sw2Kz1LFphrWW|Y_`XtEDOW!KdEIuy03Ah z*;t6D<=v_O2@!0?AHUD~q3-?R(Wc6jH*0kyST>)1aFAz-^!hal+ zE7z+kN9V6-+FuV8e{gT*{czF$ zsZ%$8Z))<5(m2sLSM`Va>qFk_SHz?qw5eFb^4}%4LXK5joBL9)(5_hr+w+<|xXiN} z74&wth$Z{>%qx`C5ef4?y8LCNfmD^$(RMlIfSLd??tPP;cVBmkcK*>5z9sqM&O`GS zdD~A%k8{X@bM>m9xSasoxNzwexKMWr_<{{?C|>{dFtHLRUvK*ZwD$H{W$ykV_v44 z?b65B{f;j+vWz_@d!f(G+npnqPs&%ia{Aebg3I%T-d25;Hk)EMcZSi$gpaemY49E|3fO}lsX=)@W2*LIfHH2hiq(lF-&^RAKy)1=q!dQr3XRi?7p zqyL@tjQYmkeHAnEe1MrlwZt`|BqgyV)hf9t6-Y4{85kPs8d&HWnT8k|TNxQ!8Jg=F zm|GbbNOv$!MbVI(pOTqYiLSxW%Fqm=;Y4`S0-y#-kPX54X(i=}MX3yqDfvmM3ZA)% b>8U}fi7AzZCsS>JiWody{an^LB{Ts52>Hb` literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/btn_star_off_convo_holo_light.png b/res/drawable-hdpi/btn_star_off_convo_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..8d0c6c29ed983247b971ecbc1e3c573bbe2544cc GIT binary patch literal 705 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfizez!?|}o;S3CnCp}#pLn02po#yR-*g&Ag{&wY#3zwQ$nEd|y zmp{rXAi{Y>SvOZPl5fhSCB1JKOyQn8XVTYq6>8sZZ8*$V-`lbzwkglE;Mh+yPPQdW zc(%D0bOaP^(PippRam-Rwn>nw!GO!ZE6OKzgGz(ltVP+Z_D>xxUff&6d_0<+MNmxB zRu=DpvrAM(j+?RP_p&E?23#+Eo9^v#a2C12 zYrAEhV&3E}#$n>}#){F_r<1}(r@Q{XaY^cuM&8MaWUu{Yc29KboDHeK^)_?MAn7T^q+B+7Cbpc~owZt`| zBqgyV)hf9t6-Y4{85kPs8d&HWnT8k|S(zGJ8JOrAm|Gbb2&G@2fTAHcKP5A*5~0C3 z#L(2rz|_jr6r$li+f7%X21$?&!TD(=<%vb942~)JNvR5+xryniL8*x;m4zo$ZGegx NJYD@<);T3K0RV0E5rqH% literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/btn_star_off_normal_email_holo_light.png b/res/drawable-hdpi/btn_star_off_normal_email_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..5076fbf0df5d7aa5e82866ab1b7edf2f7e95d4ea GIT binary patch literal 325 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 Jmvv4FO#sx?4`u)W literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/divider_horizontal_holo_light.9.png b/res/drawable-hdpi/divider_horizontal_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..064cf07dee3ce3994b47cb2fb03b464d1d3ddf11 GIT binary patch literal 105 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1h!3HF`e}{MjDFaUz#}JO|$q5b({}UelujhFG z_4V~%20d$>xHz4s+}l;^E#4;B_V<52&taYnhCD%rNu4U!vOE^Y0`)R@y85}Sb4q9e E0Q46jy#N3J literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/expander_close_holo_light.9.png b/res/drawable-hdpi/expander_close_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..2dd454837eaeda94b3a8803ea7888bfc3b76ea5f GIT binary patch literal 425 zcmeAS@N?(olHy`uVBq!ia0vp^h9Jzr1|*B;ILZJimSQK*5Dp-y;YjHK@;M7UB8wRq zxI00Z(fs7;wG0f5-kvUwAsLNtZ|wG#brfm%ct7sK0`t}i!6WPunh`5p7yYjn4yXya zCT7;9S<_HtXr!6u((Tf@ySw=C&sle8Mt+-y%FjIJa<6!!==aJysbgXVOfB7>Dzb?!8p*6F3XU6?#x8pw^E>&}zkvY>nd)kw#AK_9tNeff|{C~t^{O!h7 z0imr($WeghV71R5xvid>nX}CO^Rg!V$!^kA-OqTL!Np_olaS-Uux9Xd^>bP0l+XkK Da>laX literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/expander_open_holo_light.9.png b/res/drawable-hdpi/expander_open_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..7cec83ad514f515ced2d4e9ef4dd44b9d2f38e36 GIT binary patch literal 428 zcmeAS@N?(olHy`uVBq!ia0vp^h9Jzr1|*B;ILZJimSQK*5Dp-y;YjHK@;M7UB8wRq zxI00Z(fs7;wG0f5ex5FlAsLNtZ|wD!a};R!c;D4|O2Z_z8$TGc6j>aZfBkRvZEC&I zu`Me-Ab?40Pqv}D;79KFeQ&fshiqrBHa>s$-J!l9E|JHcnV;pO8&y0fr4*c6{(r8c z{1O*kRCKyrXJ7Fnlt(PW zyqbaB5G3!U2N1uO0F9UmrZI9}V&=W$4& zcHVA&&j^*R-}lY``up42(<&c*bZ2gB*lggNv++iv?b6jpV|1To8HRW7j5;G4ty31^ z{}Rf(nzZgl(#FG8Ug2*x%)8|3sbKl7y`o8FpXN4Ey<4%jKLg#s;OXk;vd$@?2>|ut BP+b53 literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/header_bg_email_widget_holo.9.png b/res/drawable-hdpi/header_bg_email_widget_holo.9.png new file mode 100644 index 0000000000000000000000000000000000000000..6216ba3c3607b77def57e8027359d662da273d7e GIT binary patch literal 395 zcmeAS@N?(olHy`uVBq!ia0vp^ULefD1|(%J94Z7-EX7WqAsj$Z!;#Vf4nJ zaCd?*qxs3xYZ(|Abv<1iLn`LHy|Z!cAp;S&hxZkQQv{@>wly!!n9-zqDIkS4*g<8% z$(L_Do=;ZVd&z3o^#jgITd$QYtFw(=v2~fs$xS-fkEOr7W#T*i z$g=)i#lEv@!fN)P`C=ymCM;BNc5G|nVg0#R?uiSSCCOzZlA-~WdcRmf9ma+z`!kE{ z6R-M{S5S4ILivjpLG@S8bgpY@1lk=ip4nJ zaCd?*qxs3xYk`9Lo-U3d5>u1^{QqyytgQUFp6yM;#EFfK5A6@1F}|_%vaiI9f)_2# h2NyI5dOiN4#&G;I2k%xB&%;2i44$rjF6*2UngD!bD}(?5 literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_badge_attachment.png b/res/drawable-hdpi/ic_badge_attachment.png new file mode 100644 index 0000000000000000000000000000000000000000..a3db7ef5d0cf01e7bb24c58178921ff661fc1167 GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gj+dN$yLp+Wr|M>sko>|xM@Cw$& zVgbjQl9Q7qzA-+sQ<&$;_>Luse?sL8u_MfdngZR*ksf^0SRR!rY;)b_Al?z(IAgNY z;lv2}j@J`(8Ft=b@@$k8D4#5#^Fpja#)|c3lc&R(?j;M@JGN?mXjI$9uf||#K8tl% zij*ENZ{8G(6HRQ~J`+u^2o(hhYBb%w5Ij*wz?{+0daBY)#%Lqfn^8Ppm3(y?md;b- mGLkIgcH`b1$j!E$mEpkdThjKLdtu;ab literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_badge_reply_forward_holo_light.png b/res/drawable-hdpi/ic_badge_reply_forward_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..e89b301145b183cad30b04527371069b4589ee22 GIT binary patch literal 312 zcmV-80muG{P)f*X3kYFsK_m^tH%PQVf)op|r65AC1;v>PDHg!;z%e2%0LecG zVgphvfE5vYiL?NOKLD`}DHeb*ClGHW(gG0v2E^W^SOCIoNbK!E{27{GUg3!RpFkW- ziUlxi2{oV#h;xWYW%(!;;KE22?1ct_G?BTzjuZ==pqeHCaX1l%XJP~q5k4R$l0o4g zg*B3?U;!xj{BR~y3M~MoEPK2ejRFfm$<>%(Q9*$P>O>WGw8Q|5cgk?fPLRw10000< KMNUMnLSTaNpm0+F literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_badge_reply_holo_light.png b/res/drawable-hdpi/ic_badge_reply_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..1c0e8228818275def8e53abf1403ec0895d3e372 GIT binary patch literal 287 zcmV+)0pR|LP)biUpQH{Eb!?m;vzzApTDa3yhHrr@95YK>QZTaH?9M z1;nq=45yj}>OlM)i{VtUKm~}O;4mB;eh9>Sfw&s#FuWGXK{fm*6aN9?`9REr%>rp4 zzDI%K$oLWvi_+2p5Z;9=h^T2vAht**6c5O1If2+4h!5jXy9HM=Rm5Wnw&chRG7O8_ z?f5d9B97#WE6vM6Q_+8D?oS}5sK90kzKnGhnoNaADeTZQ9s>bikX2l(BUwVT3I|p` l(aZt{Y-J7677XcD1pw)Eo*}B3{*V9w002ovPDHLkV1nl$a%%tp literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_forward_holo_dark.png b/res/drawable-hdpi/ic_forward_holo_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..1f1349df81a462574e369a4204a31cff98b66765 GIT binary patch literal 417 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezr3(FlKnVIEG~0dwavt-NjLc;X`Vt zs*;kDmFxV({f_ffI{my$UR^P@D($)v?BcTA&wF_X2)x*`+;6{Lam3I`}pPf9X` zhYI*I91!5PWPZTb4J5?8frQ-Ydd7@)L8*S?D+a-SrZd-WV`Pr#`N##IT$it)NEA+xXPTcT0_${aX-Ja(^G)gwf zo0iDTv-iC5^L1vu?yMaXc@KL{d+PV+wf$u^#xyHw6IO%IFV}E3I8Jr0yW8-1m1LI{ zqi2yzDdVKnm#-M!JfAc3_F2oVH}qeAWzuMXpcOStYZlo)+q?ft8ZhV?JYD@<);T3K F0RTInu_yol literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_list_combined_inbox.png b/res/drawable-hdpi/ic_list_combined_inbox.png new file mode 100644 index 0000000000000000000000000000000000000000..a70ad8eed5961bafc9747663a5de8c1f3b08e501 GIT binary patch literal 786 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfizez!?|}o;XrLto-U3d5r^MiJL`2PK;qcPct4S-g3uSCJBoLS z7N%Xv&e&deVQEOlLh*oGtxIR?xy0_s)OVEK7;wv4K(?AUHf+Tr&s{Sl)!J5kX4Jh@ zsi2~we%}6l^}RjM4K8^o->r9@+r9OgrdV5vkLreh`br!@rk~FpVcw;xW+KCN$&V#@ z=W)kX@0NN$W@ggs^^R^hw|3*939G*u&REwYeqWC%sfSm_@%Y22s8hu=zvj-_x@!3f z8-;`0WIK2Gdz`kJc4On+vMlXW{n5&kW^Q@5lHF)!L9NTm%IqsU&tH@6PWf4{pf$t3 z?3uaR-Vbm5Jg)zl{Jrnvg_il19(JyVVYTME78_S2-TvA8^3{^3ceZu8syAicU$e{X z;!)2&AImMPwKAeU9SP%HlD+R~r>d{x)pgEOleVOWPMKMr;O~23%bFx(_LsBrS4CVC zx$?%+AU;eiwZTKlczI%cY(bVPP-?w}=#{tGGbVeUdbaA1eFtl&PptFd7-^%_!>!!X z%hp(kZl8ZWRcPU!=H8v2Yp1wmW$mkYeu4jd?U9KdYo1Nj3c8tl_T#sA_q$X=x4u8n zbYr6OMppK3p-au{f8G@oQJ!h~dbN2)?z4CL?`0>npP6wj>$UC7?|d(unBK-+U0lUp zqVOyFeOv6K|I7|QWxnj6_my);_C!ghg2wpTYhmHxayxfO7PM}+^I-2&5S}BQ_W1Jt zuG#k=e!j0aVgI(DvYAV&7ue4=S$o52nrj>|$*7jNMwFx^mZVxG7o`Fz1|tJQLtO(y zT_e*FLvt$wGb>XIT>}d%1A~cSujEiPDSr1<%~X^wgl##FWaylc_d9MGT&2>S z4={E+nQaFWEGuwK2hw1@49XUHUO-Nor;B5V#`)H>hJ1${1eg!p6t#aOeckoR|NRb& z9%u+DI^0>OEv{^>Q`CCEWRpTm=8a7XFEel0E>blA{&=gRQq`x;U*^{=6#eOt{Yg*M zGTXOfo`9`l^&!`eSu)A1TSs4+9F=JSWhQv!Nl!A8&ykOBT7;dOH!?pi&B9U zgOP!up{{|Yu7P2Qp`n$bxs`#5t^trVI9On^4@E<6eoAIqCAtPvD`PW=hP5ZWz5+E! vf@}!RPb(=;EJ|f?Ovz75Rq)JBOiv9;O-!jQJeg_(RK(!v>gTe~DWM4f(w1$T literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_menu_compose_normal_holo_light.png b/res/drawable-hdpi/ic_menu_compose_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..b590822e4e612d747ebe96ac4d9ab97f7db30a37 GIT binary patch literal 300 zcmV+{0n`48P)425OVB+cqTcWx5kY6@2oWKjAVe3Q);0-YYU${V%D4Ep`aUqu3~2H^H&Y%X zfB*srAbhw!k7w*?flcL4bcw*?%5p!Bfa0FbKi z;QfB8;0eeXg|kZVujhmY{Vw$Y>pviSqjCVcR7AOX%5kaY#5 zOSrRxtbi;_fh*u*5$^mXu7h+9_jZ*?OOUSN-tR)`dkMw5%n2ZXJ>b_&0gwSt0R(Ui y7}K-|xSqKHIxRH;w1fl@K!E83^p8vc&+`Jf^vMf5k2$;m00004q17=X*z5E%{+V>hq6>?A!(5wUpkHYzf9-H#Y}5=2DtAdca$t#9BBgph_bO_R92 zhv#DY@jS(Zh9+fMCbvxTX9LnSb-_Jw=i*wv10H}0umd(ghB46xh~gKCZIh6;1GxBJ zAuhw>#&tv53gF@g194Tz1h3&efZa5=6t@a-70_`gE_2X@G!1A|TosbbA+`Zr{OCcv zDx_l@FbgCu!yINd0hgF)8ju8n0w^G4Kvew8fOp_A0(s_8*v^3yI~nK&5bxo=PcA00 zZO98!S9}4SU9Li$vth^pSPSQ^@i*N$ePAhzyABXT78N1giY?_1$7+eU6(EMNn?DIc zndbOR68{8#e7p;pgcK+?)f_j)V~oXp1u%Yb6Ee_@KjV}4{x)jL2%m7F7@Ol*9&^A} znAk($3mFRk`(w-je93%OtTt~It5b9VoNwR*C+sTpj_l^QloUV#6hHx<1L!Ly1yBG5 ePyhu`fCsM4|Bu)=uNrLt0000RA?}IlS;d-R8o+rHF zf!w{zy`LuEebH1^W$syq00bZa0SG_<0>lWAB*}M7A^-sh@OOZvsEi=IW>~fXP<3ty zQetn-VA=xMAge$iCHCsr8QK73qQ`(B&fv(9UGfks$fS1BZUYcq_X1@Nf_H0sOzd_4 zQH4?u$kFl@qG8QvFjiQ;qbGQJ%`&?4fP9GhqMqmpfmP2K)49DT_%7r*@GcrVzG+qN z@aMmH$GFzb4l><*WCfafyi?0JY7#u;N&JRjYma9Q&ufgW#2NSX!sA)M^A&3^G)KU~ zG53kNdCbk+LG&ORh_?7u&iFjQ+#&XEG)1|rSDU`8zQ@{5pF|tc4BB9f?*jxN009Va nCjk6&mjDDH009UlCK?0V#U5sn)WW8tmc0wmOOeRcSV#>Sx~m@s&@4A<*Zt_*1TT3MOaO^ zCth6qT1t*$@x9v**GQ|r=-d=q$CAzIKOxf1?tVSrVix-|KOel`)s*AH?EP5X^1)K| zZ&pb!>!VM&s4VimI*W5#<-^9584m>YESH*_?q0TV!p;|sISVw7+sdrA%V^U0e7Us6 z>Y#ad-)-GfX*ZB`A&=5JcJ%J?4^Vrk^z6&`2H*!*N&l>8+<#{mYc$O>S$f)>2Q{7A?5%3V;+jF z?*6yrSoU5~dtcVgr+i79_$4m4@^UJc&%TX}BI=e3EfkNI_e zPEo#6bY` zTNzqfnHcLDSXdbtT%8>F9YsTKeoAIqC2kE%&b{;oYLEok5S*V@Ql40p%HWuipOmWL dnVXoN8kCxtQdxL1)dr}D!PC{xWt~$(69AJ0U%dbT literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_menu_refresh_holo_light.png b/res/drawable-hdpi/ic_menu_refresh_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..8591b6973fb052abe38f53a19484828a88ca4648 GIT binary patch literal 566 zcmV-60?GY}P)(Faw4_ z7q|y%fUi7Xk>4c_Py*V(WX0iatp@}^8JMnoo(Mo5c!=QiHnw+wK#7=B08L=N=HRj9 zKvk~R?=$DX1#ly|U=5%d;&?-om^$;71Wyd0t~qEfQS}Wf)vJv+a0+}YhkQvcn3UPR ztsHpKME=si`K**oD@k0{9MITzSB?|lS#f>{c&-2xHPNpmF&D)Jpq9;1QiEMw08a|Q zt!n_EHm;8e%qD=D0&wgSKvq|u6EIKhIZ**TO91}@=h{wuq5w=>15i8vC>B{IL4cM5 z&~XXC)0FOOmjD9I)LU=`z|(AM-N=2w0)TE^4dMpab|L8)j@&hkP|(|{6Yl`flV)Uc z?{({H!3IFzzG<|(Ht_sTm<5L?EAn5fGKbWTp5T}eDb80Cgu;$`F|)<0L@(KXD3>0vx9O0IqFnZ6em5A^-pY07*qoM6N<$ Ef)9J_LI3~& literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_menu_search_holo_light.png b/res/drawable-hdpi/ic_menu_search_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..dae6979ae8d468634448deb54d70326664088387 GIT binary patch literal 845 zcmV-T1G4;yP)N)7>VFkv9aRLZ50Jk z{DZo1rF_7Z|0p5_D=6r$VCh1us3l$auTV z%pGSV_P))`o8k98=ic8r=e?B9uK)AWxeA3+q3GNhop_jD zR^0zr@}*L;(Zk;Kqu-r!(l=glhYgZ)=sdde$vBP(Ez!MN!XdXWA%+8#l1qIgv@AI2 zY4^F!L!S4gmX@&7xHJT!D}S-Cg|N@{ZK3+N$`*%&@Rp+Et2{s z{3+bER%A7NyYPjEq{snG`%BoemOpC1R^hO*96;Mce+c)llR+hUQTRla3)tX$VcR-q zkR%@nueFP9a2J)5SB1~od)trb9`WT)3XkQvs2%m)Lb$b)gVpO`n=o%$Qs&mdxeg1T zJHJzE`sJ^2LfDS_@gr-Fc!f!UM=D9%-mFqldkB@!jL%TiNHl{*h zN1a7eIBqrr(KA|#E^e%6ze|;5#^3cOwh^0#K_pd=QOmAlD#<;ybO?(>Ao`w8m)Apb zm0xS=CB$rYWvtn?Zgip#aYI)7Tes$yBbvewF3f1)>ug;^vbhJ*66Q^1HNb%pO)U$; zJ6ThDK*UYmERLEP$r-GY#ZgV~)n%AQ1X%X4Kj|`MR@=))bY5CUDc{$-MlXMM{TJ3h X;AC&a2VI#s00000NkvXXu0mjf`HPib literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_menu_send_disabled_holo_light.png b/res/drawable-hdpi/ic_menu_send_disabled_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..24232e4009ee5426aaaf364be50063d10a958f87 GIT binary patch literal 409 zcmV;K0cQS*P)+hz>yXc>|#! zb7<&0V0Q;#4WbKpYXD%#!URMY@Z1t$45ACjIs#PR^4}Y}20TEgo(Kh5$}5@^fT+>{ zsE&Oh6o4%O07F*L*e0ML1ayW_044?iDzU!a|ASBfMg{?YA*l&~N__c~Oxzz{WGngO zq7PZ$g@1#jt*+7pz0Ad8lTLLa703$SzDH_NUO`$!(6#5-FSjYY(jsOs9X%(m&fU&>&NNL)jq)0mXd5I#+&*M$0`e-k5z&` z*h*XuoceIr`k5&8@w)YaQvkw#!NKpy7{CAqFyLN4ORS+UBv{(D00000NkvXXu0mjf Dd1S11 literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_menu_send_holo_light.png b/res/drawable-hdpi/ic_menu_send_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..95fc4fc2ae0bf2708ca722c3c5498379fd4eff07 GIT binary patch literal 432 zcmV;h0Z;ykP)?!N z&Mr*xZ3PU^5?nj+y7f6*k2$H_fp{OUO6$L?N7jwsi@7MBsM~kQsCu1E50y!#x3OAAmkt z$OT!*glwT#+(MPZ7W!Q{=N9S(xQPGk{nT!wgVY{mjrV^}z%2DwiArMOvAj;Nq+h2C zsdIqTk&^8sCUvOKQyZ&zn?~qhYsY9Y$ilgYyTNCooa1$osWb=REa^Wu^gl8MC_n)U a*wzmw(z~iW^@SXh=Wuk{ zDBzmp(cvZLWU{t*Y46pW5&4;kn&r<-(kE&KHoNd@eqv`?%M{Misqs+g&_os&oh>I+ zU&X#lJC>CGEv-CVZ}weg-aF-8cW1W$+%y0E^WU}eDsObo{c+#@#s0S;|IX)gD_pbl zb+M}5k$Y;vBv+;BzVkfiy6V2)^<o+Gni#oQA% zk8|D6R9u>5t$HM4LSpx#Gl|omEJ`@4Ie&NT*4Gbtq}f<@+U8Ywg*m+Yb*O3fW5>2# zD}#+gh1<4Z@&8J z?-$y1I{V)DJ+J>s$QtDYZa=erN0Qe)^SE1EUr8U=?Xle-k&$3_}1jal`yK~g{JoAsg_DdCsXj_V)oo*jPa_go>vx3<&g zZjDzH-W}B}owDNcdzGO4nOnmo->>VxyVoYjzuHG{*ZZc(aF(ANJkRdVoWFXm(fv!8 zj;xw&w?VI~=?cGHNu@+keqim9hh3p+Qm?ko(zQyvmX&XF!l!!Ar+dp>U+c~IoV?d{ zZB#eYy^<4dcPrv^EYi>UAN}*W?_kkEK1gtLU#)*EnA2 zZfTy86_L?t`uxn!U0W-H*&N@oR=3Za{^M7t*)>++a7$Ycx2kJ=m@*(PHVr&53heb`YM)M5&JAz z{jb@~ioLEgr*+#Et=6*pck!dW<)6ISK%p5|rp>+4>8|sSwe!r6t+kih6oBbkwZt`| zBqgyV)hf9t6-Y4{85kPs8d&HWnuQpeS{WEynE<)wRt5%}k~c3z(U6;;l9^VCTf<>H zZdsrPNstY}`DrEPiAAXljw$&`sS2LCiRr09sfj6-g(p*OfQlGAUHx3vIVCg!0DH!$ AdjJ3c literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_menu_star_holo_light.png b/res/drawable-hdpi/ic_menu_star_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..3051ead55aa0d8f3b11f7debbfdecdd2f6a022a9 GIT binary patch literal 501 zcmVSr!E?z@&JV41qc@0$f!Ey1)<8 zQBlAR2{@_=tcJn|EUP4N6(ex2iohJ;#Rz-?h6)0^G2sKYRSz(M5e%)K;T)HzzSgjn=%BB2?dU1 z2n>iv2B89t9J{iIhnGI?u30BFL!`h~Mj&>nwG^qBVa9JZMGDv%tDbaMn)YwlA_d~k z2~K3y)4G>iw4G84G$Sv5FL~dy1AIj4K*%h#9=d5e!{Kh4TRb~7Q?*41w6JeCh(3Y@ rU5!0+kxW2;Bhvy}KnrMre#>IijpuQq>u;-@{3@($g z3dYvG0B{RqH^^89W0igfh{4cj)B!Ob#`q^ww4VXCVdzPlYv;JO)z1JTWiGV2woFp1 zc>s1(h-scFi&R*|&ORvAgn@S!id&<@JjZ0c3~UJM0NjvDYE|6$$qoRM_>J9p%Q^tV z*r_kwV!xx(TV|f;|S9|CgR0u#fi@H7p*dV5RXQ?3Sy+d z5^-nIpz%j!og>7dV_9eud0bT4Q3nklW4gxNeD`yv(&!9|6kq5C?(WujUa?;>XA^N? zv<~oQ?Si}4U#k*+hVVSJt>hqPOjSiCl#Y5-i>dEGPxunQv!8UUhi z+Hn&=^ggp10CTwa(Jg=}I|e8uGFr?HfC3gjR4mH7&pkXM3yR;IV2->S0HP^dl*R|7 z^hhhKJjarS4%P@jgjD6AwCgpJ9=8yzqPMx1yb&aCYT;f2tYYCyd=E*x&inp^6fX}7 zZ&VbXGzxHtMRRC<>(Z{P&Pu5_YA9g8#gY+#25IW(@nOP3=9!X?9m`g-nUes+QgFU# zp7+ouPQ*b^dZ9UT(zcK0PPYyW;n5L2Rk9ZVapdiYgYN?o%sFlh!2gfT2k-%W03YB_ a0(=7!_#H6*PQ*$800000alX|Fv zNHJ6?Rf#cTJUE9(Xf-Bzn?BqxJd)kX4Ex=g*?F_64TnQjMqR~`31kBQJAs9NRcJPw ze-huq&A*stP zp=F#YjMs4lCkng2SCFq_8#~i_W>NwxXySYxGF~ry|Ad!#hSwZxM2ESvR|yLFg6(9a<>X99z&=RhxeAm>0nMspI6;%iC*of(tp;YCUUUEGVSkZG|Q zwkEorn5Hq`#e--qFW_SIx3!R;E^N@x=yzclee^58FLkH#WG(+wp9y3FnZR7}3nnjY U9hA92)&Kwi07*qoM6N<$f;|G|7ytkO literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_newer_arrow_disabled_holo_light.png b/res/drawable-hdpi/ic_newer_arrow_disabled_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..dc73d2e99cacdad8e95a4eda6c203f37da40fbed GIT binary patch literal 844 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfizez!?|}o;XrL6o-U3d5r^MikM$RJ6gl?s{Z#Oe+p*8*7M>!}YuF)_tduo%LcrANWxM0HTv_X3o&DA8+}(X=KF?1-cV>>H z-(!Xq`(t+47cTPr-N zd0szopr0M$xkm;e9( literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_newer_arrow_holo_light.png b/res/drawable-hdpi/ic_newer_arrow_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..065bdd6e765e49c1b6c5c9cb2eed5c68d3c2134b GIT binary patch literal 937 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfizez!?|}o;XrL0JzX3_A`ZX3Zr>5>D#G^R{@iNb)vg^aD?EHu z90kITDy_)jh}f|#z`04MX~oenC5;o#Z*i>opvrZkd*(fB!ve7VS z;UYoKj4S2oI`{6r*8cSE+}E{Jt55y>TwA=a_}LkmvfS%wTNmB`bnx0bmi1|GIR10( zyJ>zSvPsNthH$awCQhjpUYl}WTr+gt?lIFa1SPAF4gGⅆca zdTG~Xx5uB)OEe$6w(7-Hv47nDlAM+wWLa{vuFd8>=bw>S|=)PyQ zz*If!%oQQV$tyF0i=WIeZ+Z4kPkPm-hs%Yw+`7(Jec8^G>wI*udHO8 zJ}{{q6qUD5-LGQ#F5G$QjrOFhjk7D%`YrS4M+H<|vHsiT8fR+zG$z_t{!8TitSGy>Ag zAG-F$$A!%aO-=nWZ-!+uF`iWR^s^uM>Z)Wp`&9kWgg|O>(_Mh_WSA5y4YHz~- zZ!@3g-QSxzq!hE3uD5yFm7QbtHp!3i{DcgrWlKJ-m6t8LG~F`(ew^$%m--FI-$yTh zzv#tPC*Kka*LR;k&;OpW`U*4rb9nl)=E&kHCG!GbF4)62%~oRF|2x-f&!72Q zu&h2V@PzB+o&VQPYVhLx{X#*<<*LK)jQ8JHuDrbZ!S>o!w?i|3YH?}J_++vQs_eJ!#XYKB|+^KQ7 zi(9W-b=I!hX>G?uEU*eZU7y*R7G z)aQkk)XfE4d?6cG0m>x^6o|FT{qrvjTP(fTFIhdzG-DD^Db-U*->YY zRyZt7o)xZj$?R>r)~#bbO20nukYf+Q2+~K_~@cX$x zf&L|&4)0#qobNDuCAMe6-b?~@a5z_Yfw?opHRsr`b+xZm8s^kqb{6gQc(QYA((ey)EcmL+)xVfcUyQARp46{|& zY&XZ6%u;2L&Aky4x-t3K`3ql+TXpxp7URvDu`TM{z7H*%^K0L44Y+3afMcq&M!tNB z+iBT_Ubp|ZIzO2CkNGcCoY4Ve(Kgpfz~rS`;u=wsl30>zm0Xkxq!^403=MS+EOd=b zLkvxDSr e1<%~X^wgl##FWaylc_d9MGT&_ROa}{>wBNCy5htmv*LoQ ziPJ>|rAC7ZLQnXGoD_9*c$~O|ZrOAzxCpe|I^*|;Sxek9?9hdbFWv^C!5QH0piQo zx9&guZz5OQ%gt-1?FzdXbkOOS!ptw5Od{?t?Gih1Cc)Pv_1+rWNz0@cxh|61nzG%0 z&B6C^ADR`WabLanglqD(9okX3*Cr=TFKhp}UVqlPtm!r>t97c6Y>C`{#YlWQ{|nu) zkG;!trl`EOc^n*hw1jcd-X_*LZ+em`yieqbkPCN0$BMfjx87Atxhb|i_JYUT zj;6UhlOBZsjuD?w^Pu#v(CISE?xu`(OKsPKy82f4^KOQ9x}}_wvfueiRYm))(fjS& z;zStRI+helob9SE&VHPx%hD4fSd%|DUG?T-Gs~s+=QbT}P-NP-=_p&IQRZIb zW~pgzB@8_g+uqNKm2=(_`Ph2vo|47hxmHr11$X22ueiGC@V$vGOW#)6H|%$loBysChhMd_M}YYozuYh>3v=`GxUiBI&)zdw&!=Kk7rZ2P^a z2`7za@0qYqP_{52B=gS;)9vhPd#@)3a!4$DUwt`{^I+)u9cnyJc7~VOzS%Z=^QzN{ z;dT|Q7pF`P>(tuwcF(fE-{WsJM}66GS#A?r$ z^R6LUCp~|Ey*OjDd)l1xF4b9*43qfZUJuWVDRT&#x>SFrZ}XGb_d8b$#e7^PoqO#K zg9OLJF6l6iyj#zo=Pge5oSc1fioVaS*FH1LyBL+L6PkU~cHbBH_m$h@V4$H(NbHvP z3=#KKKREv3f5bmU?pDk#>(@%aw5wX;8c~vxSdwa$T$Bo=7>o=I4RsAHbd5|y3=ORe zEv!sTbq&m|3=EF0G)qO%kei>9nN|taV4`be7Ghv&Wo!yW5Dl{Hlq6;)LdyOdsUa#?U;jpPX~ zLHqPs&#z1mT6I8gp1b0s>v~MUE9n}%}Q(<6G z#GjTa6Zlj2RL8M6b}#DNpJ~^(=)h`=8EI~AUr#CkpU@_r>-&nfryo`PQvESLG%={?VAmF!+N*9*+@n-ZGRcG`ddplwUzZ`ik9b6vHQr~!d{R@?jn3|=E(?!nJ3U1y0dI#(LkoVnT ztu>1^jna;W+O7Zod+i#F#NJoGaxxEGUUH+Pe%l)l&Y6P39=dCPoI7#hh)Tiz-29Zxv`X9> zmYjR(4b&hBvLQG>t)x7$D3!r6B|j-u!8128JvAsbF{QHbWU38N5re0zpUXO@geCwo C98Mbm literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_reply.png b/res/drawable-hdpi/ic_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..0816a5129b80cbfdf58f17e0101a1c7abce33fc9 GIT binary patch literal 1540 zcmV+f2K)JmP)Vok_f-F8ko;A>{iSDk@Gx>PM6_*)Uxgaym zLP4IO(xi%A-B+Gc6QsC_5(?Nvb83`ge1eRi(xfK4cF*En5ya22tNe`eAp!uMq%51t zydJz5?`>j1p6u8&{6sz;033j}^$dU@y~#mCmXhMg_;1$5wv6nG>Xyx{ao-q7LZrfa%nAfp;Av)!w%xsNbW15bK<+zI03`?DA`snp z`i*mbN(y5LM6trP11HFW!tOp0*<2d$%4KFE0gxg!QkI7f_U()b@SFz|5U8Z!x?2Be zLhsxk+rUk#qUj(3rM+6b~r(M?cbY-sh{65AbaM@&y8BBG#Cp_I}aydpQLu#E20i z1puX6L<&pB8{c{kENtw}>m;)R4;YvTTVtx)cewet(h4rP$xUwZB!KNB?v5eG&1)Nv zuQH5nHLen5UNi&(0G$xcFcJ&)AMTVRj~BEFg^Ty!X%Z$=X+j-bdb&NdMbx?<2$Z(} z`wboAtQP(&+CfIBkr&NTCnEL!Bdx#Y?ScS+>P3BGO?-8H{p8;8p&veZx8YpcgSm7d zOGVb*_6B@sm0Xf?{yPdEicx3OHu}G6`=uyR3}E=LGXv2-Z(f-a>!$lYtGk@mGcia2 zjwY551VC#rO4Pu;3lWi{wsGv6wWsnG2mp}Y*Xfn3aa zQ1kTvtYV4+HMCMoP!#Rz4voA17i!+a5#1jaa6`T8e`D{Ddq4gMiVS9~?ZG6^0000b zbVXQnWMOn=I%9HWVRU5xGB7bQEigDOGBs2&F*-3gIy5ycFgH3dFvY1hWdHyGC3Hnt zbYx+4WjbwdWNBu305UK!F)c7OEiyJ#FgZFiH99jjEigAaFfbjiBR>ED02y>eSaefw qW^{L9a%BKPWN%_+AW3auXJt}lVPtu6$z?nM0000hMn)H>s|fIuq{M`awOwF4AO7?ZIfB#;dGxC!|@_k5q#hkI{wPd-d+ zAMAtcyxf_+_wT>gx@)h!Ht@N9)A^Ifp}5sbk^mq7LMqh8-oq*Ght_xbx#JXLkyR zi3v+PWiuhld7Q*6szupVnaT61RW2KJ8MbovA@&ePr zQQug;H;ljbQuBcgZ~q{KCjc}Mi@}rvj95_Y$Ni*(a=@4hZqUsRtR~{T1O-Hlh*7I6 zN0#qxuu7$!$0vJUZho$A7CvXv&V`y3OEqHSW&LY||3RnJeeSfRcaTpV>TWljs~Asw6Ed!KzcM zsfSYg9xuS_rCqNzKYRB=_}TeM3^1|cM`G#wk3IblH%gsupGJ}anp|15;*Y7{*E_}1 z$tI?+m0kJs@1ELx`nz&r{JfGB1X`fhoOtz^6OsRtV@#BFb#75tz%=WkT-=DI@D#Ic z`=bwNrgxVgB?jcv8KOJHy8lvzh0m2)VF&?qy zuZTQ#*jTo~NEAxgM&+sMro-t4Bos@y!WFLYz+OC1rB^5+Jge;bdN8NZ|VzALdmt4oA+-#{e5YL%7jqN-!|Q5W~5|Wd|dRm6&LOuSsh9U4z!}aQ_Xs2 z;G%2)h0 ze*64ot8Wc)p$|^N>-uuq$jC+@E8%NtqoOub+Ht8UG_5W@ZD z`$_xb53lu_1NMkRGVxIRIniG&vVmgaKyefCtJ}KFKu|8jnBeG_4CfpDKiwZ2_M;R4 zEY+#eFEwp__W0(*exxvM2>^K;{oCDgqqxia$crC%E7|2mD4>9)t4Bw+zS#Hy;>jtI z&$FCFo8jyj_*qMA*vm?gaHWs<^R4$D%1MMS9D)jkA~(nS74OcAASPE=r#2mVWzW7d z6&AXcK{1ocGhRG2@bmq8+KW;EVj_S?^ygZ$UeN3dhXqu)!$9qICi2&`?%RFv&q)5#&w5V`f~#LL);%~#Ezi^&m8t`I*B)9`T_T>1L*hPKB14!g9=TK)Cl4vtB52PDg;)7Nc0YhLYE!wTf3n%K1da=2U z7g^|xwB0fQR!EtCJ<#mlv_QhsFB_RH>Bm}by#Xca0V&}Piw;+N>CJnNO-ckb-xFy1 z8AN#&=2)nGU#pZ%x9{Y`W~~yd6-E@@xRP(*=Tn;R_k0qny+VzN8@QPr@o1bArJT|- z+)M8N#S=;_x@>Nv`zc!A|M407DRe%!&(Qt@EzfJ>FO+hX0000bbVXQnWMOn=I%9HW zVRU5xGB7bQEigDOGBs2&F*-3gIy5ycFgH3dFvY1hWdHyGC3HntbYx+4WjbwdWNBu3 z05UK!F)c7OEiyJ#FgZFiH99jjEigAaFfbjiBR>ED02y>eSaefwW^{L9a%BKPWN%_+ eAW3auXJt}lVPtu6$z?nM0000#gc3PrSr#*Fzt`VIg_78}NB_d*3 zmL)zNdRJ3s-uD^v?#!h3%X@pg^ZktXeSW+i;Qi(0IRFRX033h=Z~zHcOBmP4BS4)m znc~XAxKsg7_?9m25ctR&K!u-@n9mKsZ&M_UOBKKmF%@R)Kp2-Qz#cFrToUt{0_*@I zq9rk(8Njx|d@Epnj9#e&Yyks}p|cp~QwP{En7<5MY>D|)0oH&wjgf0$CZ_o)^?@F6 z1DvY+mIz=4c-4IH4k%bMAEkGo1I)(-SO#7+8~4h-Pit8UjU08eJEr(JUZ3 z(n7XrC}iEhv?YbmHHud;QfwhkD=`%Ojf$m8>>K*7iApw`xu%Lcw$w`aagLF+WbYbM zrzeDve(zp$d}=C*#2#q&g#%610zf4s`?P_%q$8ztRIDciSW@w0RfUT-hx#MouwGBt zPInbZrq3#kyuC^US50WFnOA_|A2!9c^9nFAXEV+{uR8z--~b$e1Nc7xpToclT0<3g QmH+?%07*qoM6N<$f~)4)sQ>@~ literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_reply_holo_dark.png b/res/drawable-hdpi/ic_reply_holo_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..41acf9d008c465b91afe6e5b604de2578569b10b GIT binary patch literal 428 zcmV;d0aN~oP)H(pl>;X9-J~>o9pa{edf%wBv^nf}LKlu*Yka0t_h+7y$9B|EOTdc)$#Z-~Pu0L&5_#K>YbXb};BY-~`0q{^JIN+5;X096zW% z;0MG%{}Tp-(gPtxIvyW555#AHcoEb*etLNz0f_(nrwv#Ktw(6)fwcd$2cW>248)w& z_CU^m`hsJmv}6e>aQ#0p;1aPhL5wda50EDoQ8N#84Ui{XsaXoO41fpDP`e(e9sp06 zQM)ECCDLMSH8Bq~S6mw1sr>G{^r#2L{w> z*&m3);|XFLb9Gqs{)FZ?W_t9Em#9Cr#UBd~6VaTqXUybg_NvEQ>BC$Sn@Cr(%f>8do+ii%KO`=zm3f6RVe z+=@d+QCBnx4K`6WE}b;su!#d8==cyD+N6!C8XprH<&yO@7*flj5{UBg+%$zlwF8#y z_x|twf4~2?U+M08dQKW@QYGXrMLT5UjqT79m5SR$7v~Lbup5!N)6<&$#gJX`N zS5)v&lBqk@oA0L6wu|T}3nCg20WCrn3T?4iY!A%?2sprVkdMHaB=8ad^y7Lk%n8DmG{xJsqMRs_~|2RWEXBmxeej}VQBH)>(kk67N027`h;%{3fs*cKfy zssr{2PBO&PdI+YI&#w_%-fE&q$~a$jILHFdG=sQSv^`uv|GKfRwpSc=5LZB+J>qJl z9;F=(Fv;EB4F!tC8)?65kfNx=imi>9$ig`#$&edXH*^WeaR6bg4a9gL#vzQ$qLKhY zmX88nj0<9eV?AsXq++7Li$VrwAk2U`6th`DR1`U#hOv0Ifz4SSRxJ%R>>9-GDmHpo ztdw?qMXNB+Jat)05-3cVE7m`8CYWe}DRD?dEKHX6asO;|muqe?H?kbF~fg z+-LHoxt4FA`0ejS`4+k|HFd1heES4)cfWD#G__$@XDGb#c7iV8?@!$XX@QWQi_q{e*j(i<@ zRoyJ^s@BBc<{T)WxAC2QvEOe1Czp}p8x;= literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_spam_normal_holo_light.png b/res/drawable-hdpi/ic_spam_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..21a0fa639eb2735059587cd9d9b9d9575407ab56 GIT binary patch literal 260 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpUt)1EGlAsP4H-gM+TtRTV?@cxNv z+XU{asA#X9Bw4_qZ}Y!vhS=JUjFkT(g?=d8XOS;uK!h22J~( z07j)#{E8wCCq7N@$T*;=@&DvPHjUH&U0oQBPW^XrVK4}MRL#6hP={gi?h{P6s&ly* z9vOEq>=EEuaF^i%j{(R~g98my4G;W3;B?^I@t1G)O>~+#7wLbE2(+tO)AZ(3*FiC( z)2rROPiU=|d8f2^Pt_j2(n`|?*9BVg7JuB*8W`Cm3Z}AaHt1~hnss)EoQmXl0NB_&Ozxv$##`VtSNXB2i zoCO?;Y%!huOeYOKtMN3e7-d+#WZ~R?$NYd^x=#Vyy04!ye(l@7?)5XF!zR8{zwe9e zUbQV({;dP~9wss5u2RHm=_!Ek!{AJ_f5T5(6FywE<9 zdykntVRzS4fda<91Dax%3)og2>OXTpcEaSAV!bkrr=c1jS>|0e-tCyLdlTqx22WQ% Jmvv4FO#t`wW|sf} literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/btn_check_off_normal_holo_light.png b/res/drawable-mdpi/btn_check_off_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..f1b9c7ace38508d8619af83ec2c40057e5f79bc7 GIT binary patch literal 127 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzH%}MGkch)?uLbflDDXHR41LC< zz{Pw*N3J0yrPB1m@9b1-EoGp(8vY3;d0aW?#T!a*%m?y;z(LyKuFU5S28Im&8(eN{ U59aUO^BW}Y>FVdQ&MBb@0O`jhYybcN literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/btn_check_on_normal_holo_light.png b/res/drawable-mdpi/btn_check_on_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..aede5316a231c5a9200ba579e5e5c59590fcb052 GIT binary patch literal 322 zcmV-I0lof-P)jR#o(5oI(5nvEp1TW0obw{ zC?v30PCEx6I|7g8v~>UofB5jB5|lXclnu0X01J0J)qf U(v0{{R3 literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/btn_maybe_off.png b/res/drawable-mdpi/btn_maybe_off.png new file mode 100644 index 0000000000000000000000000000000000000000..2a22a27ce9cc9d962c4c75d85b0eeb9acf41b0d9 GIT binary patch literal 817 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE>N4Lr;B5V$MLsU&Uc>;lsNwJ`dsUm%N9yH&wPD+ zK?={hWyLp7^>0cM@!Gg)(o)?`AJlI|Dmt}II=UiKbEP%=#)MPvO~OqMXIyeEjyzLZ zU#oxeR%Gq@=QihmSMHl%T=ipr(AuXLRM-25u`lbb&N8X{x>zH_dQ$MW4m>UmO{zt*@Zh_UCezSTX-rnf8&v&j@0P_ zPUV}L_n7r(T{~*1{>Ve7Ki`I>So>FY@~tP68|-XWY&ZGWU~t@R%GDm}qqE{?e_J)@ z=;|Y@UlbfM;yLag`cZP43gc(Pt5fC``taxNj8yjzTQr*?HMg^Trz^kS5o_Ko=a93_ znI&e7RX{~{(SM6KH5bke(=u!6>)2=ZJBiiQJG;T~FWa5m9qrGf)-v{})-PO;MokUo%gy%Z;j51jkWr{6_`$356>UV6&U zQq_6?&Pfx>BTmIme`L;)xmWE`$ktze%@)$`^~?%6+rv*)o8OsVd+O@s)^Dd(*}aOE zJe8f|7wRz}NA)#tBQZ)XwM#dGV+dFQQMH<$SPr>+sV4b8B<_Fun_>A|&i zQ3lNWKLJydYKdz^NlIc#s#S7PDv)9@GB7mMHL%b%G7T{_wlXrdGBnpUFt;)=knUid zilQMmKP5A*5?zC#m7y6#!-?>u1wajwARB`7(@M${i&7aJQ}UBi6+Ckj(^G>|6H_V+ TPo~-c6)||a`njxgN@xNAiD6e~ literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/btn_no_off.png.png b/res/drawable-mdpi/btn_no_off.png.png new file mode 100644 index 0000000000000000000000000000000000000000..4e8590e46825571575c73ee9bc6e43a88ede0279 GIT binary patch literal 715 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~JOWltB!5Rc<;r|kDub`)vY->cwglF@OBPbq4p zrc&ahXIkCNuRD4dH2psm$$jm5)!w6`?Q$_Wx;eTghmY<(rtxF(Ns%lO{VbhLaT|BK zl$#a)XINhIdEfiJGw)V@*tq7w1|Nl)N0#yb=zcb%X&0yFgu-VhB&_w?->f*bn`vh1 zM2%9Ju9yP<7Kz6`lMgLARv!97W5WEI#}6e&$Nns9i%jSeuUobE`bWLRR!@E|l>0mP zC41l_;kzoW*LI2a@3H)?Bv!d~bg9N7CRG&q3m7|);Cjn$E)8 literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/btn_star_off_convo_holo_light.png b/res/drawable-mdpi/btn_star_off_convo_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..af65e65c6ad4f1a22609f1eb119dc41493342a4f GIT binary patch literal 538 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~IjlBbJfh{y4_mjnF|IS8;kcpnv|rE&2{%m4qM zJ6wbe6_{-0R|Gv+uxy`|@2uDFN{n{T{nW1)@-9z!6JO)Xm+>raUaf`ojmNL;ZFk7i z>2hQH%c7>#q4t+W?#Q#M7s>%z_2*h2nnwMwwAFlSw6o#DE18O|9V@nl-;R)xY8Feq zb7sxVJ2x*oS{Mtw>6`uUSd_^pX%_DF)0t{++~MN zg>LF9?e+R+uPi@m;~1ko>1OoIc(KchvTE%fzr0vi`utMLc76SfWAfw@8DT-z*Y_<} zMBQDwT})l@#c5fVR@RFn+ zG~~wb3yrUStzW%IXk+~!-uPL+UE2P!C8<`)MX5lF!N|bSP}jgh z*T^))(8$Wv*vh~}*TCG$z(6Sd`UDgWx%nxXX_W{K#vz8LRtBb4rlt@L_t|c`0yRj2 uYzWRzD=AMbN@Z|N$xljE@XSq2PYp^2>S z4={E+nQaFWEGuwK2hw1@3^B*n9tLt&d%8G=Xq=yV@uVNKqX6@T>$a;G2~1G>_n)7+ zQy^u=Qp=P{FQ2iLZdt2!`>6f$i&GrFcH7;O)R7JmZ;I1@*&{8&eb907*E!Bl+paa4 zwKtvX-m8!>O(4W$694 zqV0p6_W_-)TH+c}l9E`GYL#4+3Zxi}3=9o*4J>qxOhXKftxPPe3@mjG%&iOzrh9ll zLeY?$pOTqYiO^siVrXJzXl`X>4AHRepRpWJgCxj?;QX|b^2DN42FH~Aq*MjZ+{Ezopr07(v&F8}}l literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/btn_star_on_convo_holo_light.png b/res/drawable-mdpi/btn_star_on_convo_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..1c2997a1d2e49ccac1aa72fcc3f7c386458bc92d GIT binary patch literal 1026 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v_d#0*}aI zAngIhZYQ(tK!Rljj_E*J0gT&!&6&%Vm! zL`)!u)%)^7wx!084>@gcux@JfJ^$|B`#aU=?&KHQFX%D4*4VE+_qu&=$=zEb9~{UAw z*;pDawSHSBDt_~pdH>nyxY-#8R|gAUfg?#i0xV3751+J2Trk}lCvx*|Yh~b*s1o11 zAJ_kVq2#2)AlbmuaC*6==c7I@rp-O?KkiUG{YNx5Sf2UEME~=QGZ-guxi2c#eks7Z zh_|%#l=3m(w`!U}1s7E^X0dqwTR2gLvrEY#P@DgpN$!kgtfx0IFicBTp1yg)=|}@# z?cdq=WO|MUr8~1%w};JkXj;Z$CLqAFhOz!kAj68OeYPj_+0t)+d$LOSgummuyL#Vq zZ>znF{k3>4`!-%5pC$3f9<6kK=uxt2MdKb$MTsx>ZpSIlFWRk{uC+R$?w*UyysB9q zQt5{#t_ZA%`yzaQ&%T;{>%#Uu{Jb%Hm(bxKmcK2{YS*s(dG7613)S>LKJPQutq+%D z)2{XP%RjQM(Z%Sqo|yMsJAqyQS4mtC^o|YQYc0j}c#OX?cW!X~xaGe&^Q0s4Wo!0D9G3m5&sO3a zT5<1c&88||+k*bS3}SJ8sLY+;m>D(W&#X#2Gqd7-rgwgqajs2Y zuz&ITWo}ggv8wN)E9^y%3NwC4i4}ejab1pK#=LXO+S`PlKl&Jw-Dts}P_m)CprmA3 zX@*5%S(er8ZMh9+uZuaKwEd8{<8#`%Ej{n=Z!SMPF=LmAJVS}EsA%hckKifw>B;9s z)lE~UY|Q+@yCna2|MaQ9t$D$F*2(P5NcSe90u2L0?aGf!3v2%REygc|u+uTjR}avO75Mu&!e`eL&WAV%nW43~{^rz6$?f=4oJlF#W^d+o!)@UHxfuR_L+_9fdCL zo$+UlIu^C4c)nyy?(07;&s8|*-BHh1p<0hxQy3f@f-)PuTva-Q_4_W{CRTmUjSaVZ zx$9EIrK{YoeSK{uyB4P{H?B&WQMQ>OVMduT$FbS}-v9fSBJ^~U`ecs#@AKIYR$MlC ztJd)0{`c!);p^XDTlFcXG0z~a{(gPNjBnTXNQ7&p8eTsr5zKD+Hpzi+!fd(S14Oxx2&tO3tC-|)0Alx-`N zzG12EAN*=}Uut{epU6pF*BkVo--zGMc051y{SjMW0#Ge+jVMV;EJ?LWE=mPb3`Pcq zhPnn8x<;lUhQ?MV7FGt9x(4P}1_skTydR-x$jwj5Oshm_Fb**^u`)EbGBSo}SohCZ y4yZvAWJ7R%T1k0gQ7VIDN`6wRf@f}GdTLN=VoGJ<$y6JlA_h-aKbLh*2~7YkBRE0; literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/btn_yes_off.png.png b/res/drawable-mdpi/btn_yes_off.png.png new file mode 100644 index 0000000000000000000000000000000000000000..0bad6697109523be0fe917ec657888849489adeb GIT binary patch literal 807 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE>N4kr;B5V$MLsQ&SwWZ3LLlh)N;vKY>^7L(S!Si!3S8x9O*TCA^ctbj4{q{FU)!G(FYXmBIyVd@8Z+P=m>p4f&9n(u| zW(Y7VsG4&Ir#5fT3084w5mu2`j2aZu#aHEWY@1Zi;WKOy4^_iv@QSboa<;$dBiAxHz4s+}l;^E#4;B_V<52&taYnhCD%rNu4U!vOE^Y0`)R@y85}Sb4q9e E0Q46jy#N3J literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/expander_close_holo_light.9.png b/res/drawable-mdpi/expander_close_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..2ea96680937faffc1faec5f13bfd67e8f96b577c GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^iXhCv1|-9u9Lfh$EX7WqAsj$Z!;#Vf4nJ zaCd?*qxs3xYk`87JY5_^B3j>G+vw%&DB$++y;qOpTb8Zd6GJuzp#UjTi_;OXk;vd$@?2>?%CdpH09 literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/expander_open_holo_light.9.png b/res/drawable-mdpi/expander_open_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..28b9ec76f4741fc4a843286d57cd766f7bfa0dc9 GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^iXhCv1|-9u9Lfh$EX7WqAsj$Z!;#Vf4nJ zaCd?*qxs3xYk`87JY5_^B3j>G+vvsYDA4fm{geesi3`h?3BFO8n( zbh-}NIhrLVWoJriakDpb%n^PAbc4L`P(HD>R5EVx>?*ljJ#Th9C~ z{-1B1JY#TFi2uim6W`T2Z>8(B-Y@78w^=Pesfs@|e8%HV?tS$$-}qI%lRsK9bM2q* xDH=;$jH1~7o6YXHV6xxra_q^!8e)QWjC=p9KQPex#t-xzgQu&X%Q~loCID!Cc+~&^ literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/gradient_bg_email_widget_holo.9.png b/res/drawable-mdpi/gradient_bg_email_widget_holo.9.png new file mode 100644 index 0000000000000000000000000000000000000000..8d319d1d7fb02fee1042a07b75790f07869e95ea GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%EX7WqAsj$Z!;#Vf4nJ zaCd?*qxs3xYk`8{o-U3d9-VKeS#ljP;5o2n(tmry_`vBY{oI<05eXlXTKW$zbQ5x4 zBc5S$;9hl@S*th0#k5Vc(|0EZ$j;SYb#dy8`oP$`KNsD)nEp=sZmsM>wh8}~j92Wr XEh{4aGeTxN&g%6=$EHcczTlq;~m+#A2SF|YGT-ngBtmEQ#<3f>& zUzw7Ua!!kK&WYH-lol7a2oA-Z49>t?b;;M09NkVdz5mQLd(X4;Zm9=<+{}5@;o~O6 wU~_N&?-y?ClqL(Vxy|=SuH85C^|r?hEa#oSUuS!F3g|foPgg&ebxsLQ0E?M;Q~&?~ literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/header_convo_view_sender_bg_holo.9.png b/res/drawable-mdpi/header_convo_view_sender_bg_holo.9.png new file mode 100644 index 0000000000000000000000000000000000000000..2afef3c348611e671ec6bc114e21e56f3c8473bd GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^Y#_`5A|IT2?*XJ(ik&<|IDnvrBc%h#=PdAuEM{Qf z?gU{*^OL970tNLvT^vI=W+wmm|KFaOnfY+N$(aKu4jlL}Kk=C)o5+N`LkEsDFi+4s f$g%YQ;%;UJ4;604h~J8KK%ESpu6{1-oD!M-U literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_badge_attachment.png b/res/drawable-mdpi/ic_badge_attachment.png new file mode 100644 index 0000000000000000000000000000000000000000..7ce9ecf303d9a775bc03707364a6df1afd2a93bb GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`6FprVLo7}w|M>sko>_H9;uQuq zw&dhwfotrKoD}9U95L=ZaJkWfzvHOGEZ>G5>>XztW{74fDC#gAIp`3>`DhE%MyZfP z@*TGeZ*=8xm<#wb8ZJ}dY`XEtWrqVxN3z2`w!*B22yrbzjw5qjS`MlxoMVt=xwM4g z$X$mV-cm7UO^IU@*dNI#yi-0@7Q*Z{*@&4Ty+yz)wfOH`pyL=kUHx3vIVCg!0J<(j A(*OVf literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_badge_forward_holo_light.png b/res/drawable-mdpi/ic_badge_forward_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..7e801b8cdcaa03b95428e253a55421ead1eafef6 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`3q4&NLo7}w|M>sko>_H9;?-55 zEgMwzER%SDupRN6BJn}Kqn>G_yeeD62YH3QMMoHBC}!^9xxrDIWfW4#Euime%+S_- zfp`ZLQE~tP literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_badge_invite_holo_light.png b/res/drawable-mdpi/ic_badge_invite_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..febdc6dd720297d5cf9c005ba69fb1a22f89402f GIT binary patch literal 123 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`PM$7~Ar}70KmPx>XI7Q)SrfID zOZLCIThA)C1{Kd^&8HdG)E#L)nB~r^aF^jr#DWtITwXj3*~d~D6nU=qBr&K;Fg*Ry V{!j7LkvgDh44$rjF6*2UngCHBC*J@7 literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_badge_reply_forward_holo_light.png b/res/drawable-mdpi/ic_badge_reply_forward_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..c46ba10f43d5e37ccfa643329b871afb632a99ea GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`^F3W0Lo7}w|M>sko>^7GXHC>r zF4^k~y9Kiw1O)QgAN_wKk-&L`f8i`ovjrR-^-7s;$60tilv5bYdd_m6=gd5m&cNZ# z@X=6)Z4E<`pA%byfwaK+BS{P=E_gev@i@T{Bk^qOc851zNeo7AmmaVj+1?nz*I~ba z^_9HBx&!G9{3gB)IkS3H!yMQ`go_xD#vMpIP`RejV+mt-ft#|b1Vbo$|8tusYFB|y OW$<+Mb6Mw<&;$S)Qc3mz literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_badge_reply_holo_light.png b/res/drawable-mdpi/ic_badge_reply_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..7a7b07b8e3324259a74bcdd32a7616bcd1849161 GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`^E_P~Lo7}w|M>sko>^7GXHC>r zF7au{l-ZB{;JLu`NcxFX0^5=N#vjat_c7Kv{1IL?Gle;+@dy8;Xa6(U zWEmcD2E0#{n9%R6tarz0C8NTep0nKco(2yr1&kN7FESUXKP=d>&Y_0?tUz+k#SCU& z<{L?ltXluLI+#DIs>H4k-sqaFFVHPCd5UP`4<6M@#+KF&qXr=!hL^mhN5%PnEe5)j N!PC{xWt~$(69C;aN__wT literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_exchange_minitab_selected.png b/res/drawable-mdpi/ic_exchange_minitab_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..ef7e116577e0d61d332b6f127fb28b0b3ceb28af GIT binary patch literal 933 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1rX+877l!}s{b%+Ad7K3vkswhI zFm^kcZ3hx8D{xE)(qO#|6+TOs0ky65ba4!^IGsD?Z1$W`kz@7G@BDr?w^OlK(0+q& zmV)lm|Bcb+UGKVf6=`$ml}wx~9j+9zTWF=|n?(v87kU?5X-YsTR~S-kz+_os_1BBxD$^!mEOw3|;J2F-mc z;mi4|VgkRyWhWmOrUZsgN9pc_i3@JciTtSZg5?BrpSdB6Q`3nxu4;D|85v7FTPzpy z)c$au7RS14UFi&I?VN62_vh{}UA#9StFmJG=cydi?PGS_@V{|yvV3gBJ%`JDe!1`d zYOd6K?VaCo%a%M(<5!m=; zz0S>&C^hfsO(rXxZ{KnHur0vn@45cp^-Hy;u!t>U4==O+PdBjbZfdwW^J@P6%(?5h z*XP%K{3c(e<1TT2eN45!{mfSpvA_S^y*=mdi(K!ZrK|G~G22Haaz{kpzCUqERQS0@ z@r81eH9~y_{CzzuI0YWrI4SaaUh#51eCI#`(?YX~`cwZiv?LC{Xg0UIJc+U5)jApb zk3MBF(vNT4mJ`0-KQ~S)%D1NPXQX+Q*Vdr=?bX}2-`#zE_s>ti&hzb#fA_xReaZIS z-&X~GJ~eZi)YBiAqKaLEHkL3Xe!TMOS0Ed!i$R9PY%e`ut!UQu-jlRuo9Z5BG1R}C z%5^Mp`Ol=8&Z12*+@I&z1TM6AarckKo`5UHTcWEse18z0xZUn_y;OMd{$o%6<(?@} z-MrJ%W0OO#w&WSXl_`DynD=#6#dS3*>;a}?)e_f;l9a@fRIB8oR3OD*WMF8hYha;k zWEo;;WMyh;WdP)wTNxO9h~I67q9HdwB{QuOw}wV0<}RQHNstY}`DrEPiAAXljw$&` gsS2LCiRr09sfj6-g(p*OfQlGAUHx3vIVCg!03?W#J^%m! literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_exchange_selected.png b/res/drawable-mdpi/ic_exchange_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..1163406788d4e9e833c0d9eb1e305436ddd58b7b GIT binary patch literal 1403 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfizez!?|}o;S3Bc&7LlfArXhK&hYGz4HaqIU;NtFHsyt)bNPf8 zA1*aL9*r)6gRwCzE`gKhF4CK`txLl~AwNuQ0TYTGR8FPcj)v{MA#%zz_+vzSZO?Yb- zUXg2TnP2^WZ9m^s?aNuK1j4Vkt=F9or+x@jzh%`;Ei%baIV#?7P1tR9A8w2;Ny9 zwd7^^+eOUs&njn5^58xvy^*JfiDU81izh!n{yc4C%o84-4OJ6rH$GV^VX(*YeHGUa zzP<4+7hiWQY-qU2-0r4-H1JVl@YTPzPInfXd^YSbDcWha`1rxVMcwa?1upC;73%Qm z;aazFmYD7oxAw${mc&J!8>hwX`Y55W&gkUNKF&)LhM`qIHXb(k_x_FB{B1uaP8L7V z2=wxN$GNiX#DwOF0wHp{>N9+8+b6QR^Zoyj-OjR8g6&J~ue>LVHM$n>kaGR_W9i-g zpcf4LS>1e@^UzrP zJNI~aPI<@IX!6X>esEf}&T7a19cuHp#m zw08_CuUS{S7(B1uUCo|xxQHWQRgu`CqfE-%mNXc5Cv(pC5}KKs8Y|bhwCK>2=dstU zCHh*=cE7Q&Yt0HNi|hJqp2%6Lx|1{T(lb%-n>+OryQWU^n7WAd$t=d$)Sol5w<`Kg zmE&IH`rj-6qABO9N!tObw8a}+H0`RaNVuhbxRDJio;IbWU*moTYR{x#c@(S z>%4s*(h9CkE&8b3_Sf!zidKjxZZk3fl96V3udc9 zWt3`(YeY#(Vo9o1a#1RfVlXl=G}JY)&^59QF*LF=wX`w-a?Py_3_ir~Hbc>no1c=I zR*73fBNKBMP=h4MhT#0PlJdl&R0hYC{G?O`&)mfH)S%SFl*+=BsWw1G44$rjF6*2U Fng9Z%Z@mBj literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_folder_drafts_holo_light.png b/res/drawable-mdpi/ic_folder_drafts_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..6db6c812cfa9bf5e25a25f35151e4ec513ff9dd7 GIT binary patch literal 734 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~JOGfx-C5RcvFg-n7cZ;~lMWv)E zH*U=Ia@AGst#4ekbc+AsH)?Yf`oy*`NX@?pm2D~$@V5)UYY1U zQ(i1k>$>u$Q`K7KXZGy5<|PtitSUNnE}N1`gJA2PZ&RlE?ph_FV(YzDa7~m~_S)7f z%J+SeyjDrbIo{^l@Mh6&wb($8x%Mn9Z%S_k&-dM_a!tIdxS`Ph&Sq|R(`k0M1ukq2 z{L=O}>))x2XIou6c`Ubu{Pw@DQ_^jb*U-fg`uL{S%HVZp1Fq~2$w@I1c~l~MfpgJh zx3iVGC(q4$9v~^By6vH8pY@fQ6E%K|F_q2!dXDjT?1xaTzz&V4UMuh9R_jD3bH=<;CWV3!fLCan`RbzhN1_B}=slBy})(SO;tTcGln7(cIySenZp5XH4(HSz)x^B;?qo?pYJ@M!Y!y1Yj<9c2~w zx_{(rB)*t?Wu2n=)%q3pnJlM0`5&`-IWX!~OI#yLQW8s2t&)pUffR$0fuW(U0T7vl z7#dp{T3VSH>l#>C85mrh9QYkYLvDUbW?Cg~4NK0w^ag5>1lbUrpH@k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~H&il>WXh{y4_S8V+r2Z$W~c)#2C5!13n(VDlZ zDomnBLj*W?y@X*zf!L zmFt4++ROewyk7j>&()OSe1BI_$#Z7cm5s)&7rMW0imv;mxQ>D2-z%-O-+U5!e}$H= z`8Vm<>sJ?Tjd&fE<`%0qweftHyU$Q1I92&}o@?g{q5Gn7z4PzfDqAA8+pQt*z_Q}j zZF?m>?(SK>wji(g&8n>er8Y0BFKv7MOw`Em{*9~Omb`aNkGd}+FlCnCQ#}#BH#@{s zg)VNDW#?>DXDS9*gzQr|>sY06XFGU)d37?o{ob}8y%BEbnN8`n&<6&*YKdz^ zNlIc#s#S7PDv)9@GB7mMH2@-$5JO`tLrW_YV_gFaD+7b8lLNn_Xvob^$xN%ntzpTz zm)<}Pk{}y`^V3So6N^$A98>a>QWZRN6Vp?JQWH}u3s0un02MKKy85}Sb4q9e0663E A`v3p{ literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_folder_outbox_holo_light.png b/res/drawable-mdpi/ic_folder_outbox_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..a7039e12eb01ef09d84128c68a3cda814901258c GIT binary patch literal 540 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~Ijil>WXh{y4_Q*46{8wj|~&y^IObh3b5Z<3N| ziAhLIV_R_n3tQVlPxs@2JKPSjbjX}u6d>T8lXCV(>OL>~)yuD%?`~?Y(q`OI`uNP* zMInrLcxOAFvt%uG5#v-{pE14hmTu%Ro1(6_ED=KYES)DOD2hh2tuW|SKDIZ)rBgz6 zn~_5LVdh)S*<9;RRy>;AGpk8kB+~l9@%!d>H60IGemrUu)ie#(lSp)KP)^I)D=gNU zblOl`Wa*yY_6OQnvnL*$VYzaTG0z5{%Df%>mK+i7f626g`;T;R!AT+4qb-Z1FT_X} z>Ki#-^O9cRdu;cH^u;#>=R@N-&UwIqQ!R0gC`m~yNwrEYN(E93Mh1q4x&}aG z5@Kjk4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~H&t*47)h{y4_S2lKC2@qj>pg%JuZ9-yfb##2f z+c&JNj`0o~w``c5lzp>Gd-Yn8jp2?0^ADUmc8xv2F`z7Rv$5wo*Q7HklN<{Vq}A`3 zFnjjr`~UwupHt9R@5b`^mF3Uw9Ws2|S0}t$7V=~9jHTwAyc~n}bCuZ|3u~R?zn>#v`u*&k zsXO2Aj>tUjetBE)YZl>h9clHZ5Ut4y8#6l01^yH~xv^rK_V&lSDt1`KHa%E$J$qe! z@HI8@*&IcVdO=r?@~BxDo0i?)=#rOaw@yNi1zcBwlhqH>O7!~f{ zYs=ch#oJpN@Go6qnnSUG=7J6h_7tT|4#p&7l{#z5eeQg%CQf`0T9(BLSTKv;=6+hw zkfdl-%kRbBc*Mm~bCO!PMq6Xnidmv6QG7)^{yL-{e%^A5Dawjr$^=pNRSH(jXLXr_ zMBb||bz8EH*&|Ol;4-6+I!l^a6ab^z(8T}boFyt=akR{ E08$@orvLx| literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_list_combined_inbox.png b/res/drawable-mdpi/ic_list_combined_inbox.png new file mode 100644 index 0000000000000000000000000000000000000000..1d3bde131fb76d39a202f8a859fbed4700b9aede GIT binary patch literal 604 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~H&il>WXh{y4_S8V+r2Z$W~c)#2C5!13n(VDlZ zDomnBLj*W?y@X*zf!L zmFt4++ROewyk7j>&()OSe1BI_$#Z7cm5s)&7rMW0imv;mxQ>D2-z%-O-+U5!e}$H= z`8Vm<>sJ?Tjd&fE<`%0qweftHyU$Q1I92&}o@?g{q5Gn7z4PzfDqAA8+pQt*z_Q}j zZF?m>?(SK>wji(g&8n>er8Y0BFKv7MOw`Em{*9~Omb`aNkGd}+FlCnCQ#}#BH#@{s zg)VNDW#?>DXDS9*gzQr|>sY06XFGU)d37?o{ob}8y%BEbnN8`n&<6&*YKdz^ zNlIc#s#S7PDv)9@GB7mMH89jQG7T{_w=yuZGPTe(u&^>Pm>Bj-4n;$5eoAIqC2kE5 zxPuk}HAsSN2+mI{DNig)WpGT%PfAtr%uP&B4N6T+sVqF1Y6Dcn;OXk;vd$@?2>{E0 B@Pz;X literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_mailbox_collapsed_holo_light.png b/res/drawable-mdpi/ic_mailbox_collapsed_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..5b1a8159369bd68ee3745f3725275d2f86ae71b8 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$0wn*`OvwRKOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18J~chI31e>VO<4PZ!4!iOZ!YH}W@MesMj;6=qQ z6AzobIIH&SeRGS9ADvnA>7?jW)u< zmbgZgq$HN4S|t~y0x1R~14Bby14~^4!w^G5D?@WD0~1{XAZc*0z+@kahTQy=%(O~$ z4W?GcW)KZ)Pk4O=YLEok5S*V@Ql40p%HWuipOmWLnVXoN8kCxtQdxL1)dr}D!PC{x JWt~$(699drULXJf literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_menu_compose_normal_holo_light.png b/res/drawable-mdpi/ic_menu_compose_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..4f194f89195d55213f0bed6beb42f4305dac5755 GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJWu7jMArXg@fBgS%f7l_ZEs(v( zD&Y#_37O>N$1_b1IUi@NHQBJIUxa-F^G4~ZGE7E?nX}X`@?BulY|AQAysW4pcKD2n zDsRZ7mjXFr(}O2*Y_wd$-hMRBLo8b<(r0Q+Kxr znW*6akRi52T1=Efh;;x91s0>@R;XDl#5w?^xSa|XV|55I4wy+xOo{^-sEmh>189ne zgabe&nh6aoR|Dc=QqlmZj5i{}P&EQ-w1K#soJ0u2H9%~L#o!zyH91)1L3*kv$%g0{ zWSB10a7`@onov12Xy{Q~4&cHOK&*sCTo#BEsaX)=!ks|O3dAvJj^YO5K6*G{Ce){( z%0&{0#i0($qjphD2zLW%dDrTHo)7<-; zGPDG`nK!Zue0SK$8j_>6*UKqj&THq#a~My(lj&Gg!4(zK6Xv#k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~JOQcoAh5Rc!e)J)d1&&eKuJQY_bm=MhIAdzV0N$Kn&**X9J5hV)+9cQ}GuGw+t^ zmdkw$<>eHzuj&L{s9XMIF4Mb;N4w`$@wwWryJ)fYW!?01yPi+;+Pi~Gxk)5ceC?eL zFPBeUG;iYMpHH@@T=3EM*kw8AW6rrrQ_qyX{^h##QqQEKc-x@urW|(96*u=T&SbUO z`gC9O|MFuzG1r1cSIhmjP^);p?)E87<m@|L~b*%c+}8t}UM{cM%|yLr1F zDE$h2Ipv1Kv~67Xc3ojgDiBuzaSjm20I?Sk%Oa@*shLSl2be%D zoJjyT0dYDKpJE3H0C5c#i@Ko>0^uG!4x-2bl0e*!WLPs0+X68smJl)m;_1|MfCv!x zAsG|_#4I?XhaHG3sT~4^NQSuKNt7&TYN_G?B_splsgWf~bwCu<@~J>9G!PD`gF2v; zT7?p+4wwRUKkdg00009c5p#w0000W z0000W0EhaVod5s<`Yqh+sev?G%YZ)JAOVf}nl? zt+bL~`~<$#5lli-_zA?$MgkUE2MNA_or+4pMnr{#WGwF9^+t0yF`zgDQ_OPybDlY8 zo{jh~hn~5ez@*etR%_Zas0yz_>Yb$A5~2z(gaPB`%zNaHGg{SdGon5{jC&(nwd9jE z*`kxxmnK9VeqHv~B|{3Oy(WB?UF;&7awvs!vUxj&6cHf_BVJlH+7Z(K{3Hx`=e!gpcKx^CWIZoUE-YVkWe(2FY3SmaJ?0eM}r9blOjCh0Ok$=6S9W_4xm2iSL_Dev4R7k z0BoA#FIBh1yCTMeX+jFTT?1)AU;q{dAbtZlLKD|5NQ^B=30v^`=6`tzBgVoakKtMJ z`S0>7wp!Q)kV(D+Bwxo)3v&k&_8h<+$%cK8WGCrakOc7m3p}8&o+_-az-$o1ApigX M07*qoM6N<$g6+|I0ssI2 literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_menu_send_normal_holo_light.png b/res/drawable-mdpi/ic_menu_send_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..3275d70aed2a59195662ebe9fbf0c322f633fc32 GIT binary patch literal 681 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~JOMo$;V5Rc<;uh?gcI7%Gc`2g_G{7HEG=7 zR1|Z%=g8u?fy-92dx4{iW2uNo_(9K;@10NR%((IK+|6w|D_vZB zWzX=O`0;3sc-$_f^IVzCT>IZ7o}3oG&WKxO+nAVM^Icea%t8M7nRePP&P!eU_iSF2F(qSr ztoVh=Z3n)b;>d^oU9noCq#m-Vh6&!)@IrZR1MF=fGr%U!p%TJx{noz5qJG0Y(I zZ|CO=`-FNAFl$e_b~mK&?e(8OU#x3yyUX^z^u^Ky$G=mmbZ0Zw6`W6Y4qPa;<<$k& zl+SuA{-0dqBwu|kN6XcA>J{C`90n_dt1iw_>Err#Z`E>NbG20SiXg`623Nu#tPMTy zelXLhLS}-rgJRX&KHZn^55_tKSSAWuU&}dNdg#^Rm(5FgPn`SxZd$HO;kK0}93DTi zey&h{YtJ9L`CfHS?r&!KxBZJwnYdK}V^OulHKHUXu_VdNkAM=j05DMimB~@ zJ|LDRHVUMHxSfiYw?m_jq%%j2MCP@D^#(Fz_ycq*=P zfs$+(L4pG!Xi*L{li+|lYCAxhjQAI$rUN|4aDWFj9Z-s+WV3~0Yz0>VwH&|-P1VST z7Xz^fk{S`H7_yov=vk0l2WTJ}Isu5S@g!77s2Z4h6>2!Z5vfq&C9<&+kE@M9jsrlI rlMsoGRR*XYS&|(v+A$e*0KG8)kCRcN(*m?E00000NkvXXu0mjf`VoM6 literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_menu_star_off_holo_light.png b/res/drawable-mdpi/ic_menu_star_off_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..15f58453e38b6047df9532a2611ada7cb3a3e79e GIT binary patch literal 441 zcmV;q0Y?6bP)b1N0XKu#F2`p|uEr zuXu}He4aNz4fj~V3*2FKo&Xrtpj!#boh_!vx zo@7E)BV@XGofjZZu#2p= zI>7_~z!n8Kz)@z?22MkbO#M-;DGlHfTd9Wo5mxU*x7bhBzrnXs0Ah^p!rjdntqass z8`R@oH@+3si1TV006RFxA#U()f)|pg>10o>2tX21dWM%dQn7|!tZA14(8T&IAEwZH j7`2=nSpJwS0iH{byR@XrHML9q00000NkvXXu0mjfpkurb literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_menu_trash_holo_light.png b/res/drawable-mdpi/ic_menu_trash_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..7e7c3fe8a41d9b314155d9a06f805d1b492ca383 GIT binary patch literal 343 zcmV-d0jU0oP)zyZ3{aDWB{4p5_3 zIY6!hs8S9{Qs4k_YB)fM0tX0C!vUNWIDnH{mCk4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~H&tEY=&h{y4(SN*ev14URr+&>-9mFw8KB}m|< z+uaE9&TbF;P&;?K@P)fNq_rO{a&?L3zQh$Y+kSnwVdAAHzRN_!@79% z{@XbPZZa=_rN1{=la;@_M3tkcJSlL~i@%Joziqvgq}}>q#k#dmd#CM~vuIL_Fl#P< z&!3W$MT%2ZY!c3I+jyTZcKW+RJaV?1vs0d2m>RH&+wzC7J`d~r7>-A=&lEc3DpS`y z+q;=(UYBLl%rE!w559$c}X$I<@tzLkp?zPoWe^h2uXm6DvUeNr2^>TWkTOw+xt zwQoksO{E{XO&UMmH80yZ-OT6GTklfEM<+J!T6SIPCd2o44ikQVRlRDqZo6*)P_XRglEP)$@*Uj1nD`D-y!%T!dho|OvU{QAiJD=uPx z#Eb5?{XFG0(?b7A-NoJN|KgAHCfMiP%Jcc+4U8Gp64!{5l*E!$tK_0oAjM#0U}&go zV4-Vd8e(W-Wo%$&Xr^mmZe?Kb$~0paiiX_$l+3hB+!|(QTwnugkObKfoS#-wo>-L1 k;Fyx1l&avFo0y&&l$w}QS$Hzl2B?U^)78&qol`;+0BiyWKL7v# literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_newer_arrow_holo_light.png b/res/drawable-mdpi/ic_newer_arrow_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..02d528909ea7b41cd7a1744175c2858ea5fed6d6 GIT binary patch literal 697 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~JOK~ERQ5RcB-@k8Le9l(?f33>yT(Pq=8UFrd+V+%lm4wpU35n~v;zd|RCUB!OEAzWIvzT<8@6kz+~y^wX=>c+wLoC#n6?e9#JOse(rbQTl{kww%V8% zot|`xzuWF)i0hV9xt}gg5?y-4GP@}CjqH=P*-MSzX+B+N{wd-O! zW}S9iZjgUAedYz@7jqb=9`WTje7o_m+oiY9%9wf{ui`yZk&1ysE-*n{y>-&OB zUAF|2IyyO}UkIkuMU`Ki5*ZwKB6Q33NBx3p1$XM3#R{GI`Rb~~MYaw}FU{|lOm|+2 zO`p2yTiLc5M;!9+^xu)5%BtKw@AnOFc3tMXvmE!IR4Tr`>CUwG8}(A&o-P&JkocmF ze^QzA&C}O}J6HXCcm3MyaKR@^bvF#V_Fwqd{ezcHyvbtH*SSK#SXC`?jVMV;EJ?LW zE=mPb3`PcqhPnn8x<;lUhK5##7FH&vx(4P}1_no0nx&#>$jwj5Osj-yFwr$K3o)>? zGByPwhz8kp%Jx7Fk{}y`^V3So6N^$A98>a>QWZRN6Vp?JQWH}u3s0un02MKKy85}S Ib4q9e05~Qb$p8QV literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_notification_multiple_mail_holo_dark.png b/res/drawable-mdpi/ic_notification_multiple_mail_holo_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..8071c272d037b30ccd97eb4f9bbc4bdd3d128114 GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJxt=bLArXh)UOUU#V8Fw6p--_y z{oqZOgL94i7cPIH*nXk8H#19AnMHr;{l)eR83hlY;NmN2U}kAvv;RF))bwcH4O)f^ zu1~6GpTKi?GMmBNt!7R_X9Z0gTK`Q{J89ljENGK`o9$$LBdH<}-5uHT42}l;M=thq+`qti^gSax5BrbBR~Y{@$rxO6HOuk4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~H&xu=U`h{y4}R}W?h2Z|i~c;DyPgk^$Kx~f`t zcU)*K6@9d5z3KUh?XOk|Y?!@Ea97TXkXcF^8AfMI440Ul+&i;F+xE2a_dh?+S$?lO zY%lL=F;{=zG=o3J2Z{WHo6|VTG1uOF)8iQg**EWskCvNUvSIm z(KYv#hbB%x#WgXXS>m5tjOFy5m%ryUuE>2DD6NubDx>i=W~Nuz?GA=%0@tl}1^qtu zE-c?gJ5$!`Y0#LCd#3U0Tj3S|>TvGfaHu)oJ^` z9ph29cRH`NRw<$>PKsvn|Swg)`gy!@71*V$>?PF|ZmYhS(|zg9)WqbnsBYi<7h zI{r!ih^@fsY=u6_gb-j{sFt`!l%yn}eUBhwH=6Dwl_AksB3 zw=ys|eIV*DiiX_$l+3hB+!}VxedGw#APKS|I6tkVJh3R1!7(L2DOJHUH!(dmC^a#q UvhZZ84Nwt-r>mdKI;Vst00f`@aR2}S literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_older_arrow_holo_light.png b/res/drawable-mdpi/ic_older_arrow_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..3b72b1329f5218023920b40f5a1bd815d5bea108 GIT binary patch literal 679 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~JOdQTU}5Rc<`uUcn|1WK?yNY_hP%ILb0L(J?a zquWK%rdF{= zv3Kt7lvwk{B>l&;J?|^`zkWDBK5*;K=zrZJ6W;bNh|b&SVC`y?e=0@w!vsUUS z>9Sz)e|45x*`^}__uGzS-rqAtYn8^UWSdE^cI!>to$stS_eFNt2XE%qE7kF_CPk6w zmj}7-pPSJ2Xez7hO7CqvM-FX~HeG5X7(MOtGSfY!=K`j*UNbot!1CK>d(*tnbw0sc zL{=Fd{gM6d5L;6x_rYZCiu9}BBgG%NS#&z3#eO)tYhlC;zmz*1{!jCE=B8W^l|OBg z_LgP;!Rs~?yzg9?eCql6YsVEPT0fpR@rtbfu@5)fR?YUePLf34mk4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~JORZkbk5Rc<;rx<$6I7+n5ziGSU7{|S)#*k}b zD@F7g6Ba%+P~Z7~Qm)B2q1J3>&7dQPUQL)BvqNiwUi+i{21Oj=29Aam#*>qtr=_Q- zUtZ#~Y-a2G>iKj3-YXaTw|;+|QoYY+UAKh`Uz^@q<>naI@8vKv@$$?gQ{6qb{0@>= zxUeUM)A!AZv})d?Z2cEI@&p7HzbrT;$eYr);gjZTo}V5~ey$t~8jAfgKdtOA`1JhC z%8SWNFBlfA7L57O6eyVZ^!S;NZQ&f{NB9(2eb2OPb5vn_c&fbXvgU4u&Y$PRlxOjH zOt`YPX8znwCt1DMT%Xj!=<8OpW07mW<+q!BsVZfR@s(UR+wWXR?^RoUr|?9x*3NRK z_afb2D%zBi`tPo4`DHxA)Z{5lOnd z_#ETuIUAE?I29IoRi@p^uG^65liMP2QU2-eBUfh3h!MFQX~-PEZiY~G#|o=X!QgAY z8EfBrs6Sv93%Ghca^t}ZX>pN{H#KaR{Md5-%m4NWJ7R%T1k0gQ7VIDN`6wRf@f}GdTLN=VoGJ<$y6Jl OA_h-aKbLh*2~7Y5-zwt( literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_reply.png b/res/drawable-mdpi/ic_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..55288fa9648f0051c67d4b0dadfef61845ae2ef4 GIT binary patch literal 1146 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 literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_reply_holo_dark.png b/res/drawable-mdpi/ic_reply_holo_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..303b296a0a3418b10d0f0a609c046bae6c2168ab GIT binary patch literal 306 zcmV-20nPr2P)Q%eK0Htk;0NMk{~>_x4iEz3Gyf5Qt`3j@;`9Ge0o@!R z4a7J8V*u3y7cprwo7+ ztPMF100sNo|I`70fH;|)5a{|(Jpgiq6FF(1g^G>6FQAH%SRnB>HunB0BOOd6*l`M{Qv*}07*qoM6N<$ Eg3j@EP5=M^ literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_show_images_holo_light.png b/res/drawable-mdpi/ic_show_images_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..46c9e51f3135576a0216a9bd3e892bbe5a571a77 GIT binary patch literal 1172 zcmbVMO>EOv9CsZEP}C`i3xtn7xfPlav7hb6j@^bP*!3D|=}MYuy{(S@+L+WI*ssLR zsUQtD331poDtg!?CUKmE1QHU69ViD7M>GvCNbEAENzxuD?`+Bpz6h-w_#`GFlLGtcQlW)}N{6m)gcytO+`ZKs~hKMTLeglDuYtEt? zGVO)a57010rDmM^6rQSnqFTPonu!h@y8%H{)bL0gm{tp6&_J_}S7iRW{WAj`yU2W= zufl4ep{6s|3DIO{yl!<`mSQs_$H8!{5&{=t6U6SE7pZZPS>sj7J~`$Xu%?1rMP}Wp zspLxHQm^-MWiCr z#5hnnuHA04ZISiES&ml}CE*Z+9MQ-{3m!J(oEP47p)#$5vsv0C`>>gt7po0?!x9lCD6}5b{uv3Zm5I z*a|yZ5C$b9FNvBW^86?)@RDJOlCGCEjh72X7hCZnYz+I5KC4Q&3MSXB#=iT$wd z`*Yn6m}vUgkD7h}w8=cEnwH}wa#Ei4Xmu1iXOL}#z6;j!t2$fQKcPq(RCHP3|1a)! zto=SYU+$+&X z&iAB${9xZawVU0wckj1X9^d%=(G~HyX|-Jd@w%W2~f{A1Dljq*+nEq}S~ z=F`%_9V@RE+L)VEZ!rZ~}ql}I64h990^V!)P511M%inty)6fj)5@5h?M!Kc8(z{0BWU^+vX z9qWub!JCZX8)Yx{M92nS>Pt+s&T~!ZZ*=K8Fi%-hyz#8_%6;4-AJW7Gw*{J}ELGYG PbS#6XtDnm{r-UW|>vu?< literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/list_div_top_btm_email_widget_holo.9.png b/res/drawable-mdpi/list_div_top_btm_email_widget_holo.9.png new file mode 100644 index 0000000000000000000000000000000000000000..7d59de26cc3e499b5e4156665ae0b88243632fdf GIT binary patch literal 137 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2|p6gzo_Z~#FKM@k2f&spFRSg4gmzuYTo8)M@J_ueVtYC2~NRbAEyemaq>P;%o*=VSZkr7;^0bGLWM_8-|*{^~~g zWBIS|BKB_9le;N+WZn0$qoU_!Rtg7)2CKh1VH|!$SN=_!gLmyV#iX;+rIV)LixA%U zaPzHG4c=d!_ioa1oA+zktJ%xllCG}4Q#g|`w=`!BpN&A_^?A1Ct~Lb=j`dyrCepEg zvw5tzcG!{9+4naeadi+*-uixD$M0`fZ@4UXFxmWe!i1LGb(ZXYpC+Hx(3+FcX=1)S zU{9@JgMde!@pHn(MPT6PG!9C|36XQW9g&9oF4@&D^#c)dVZ06r`>eC^I$bSB| z)O588tuN~(_qNzEs_rxD5Z@=o2MlG=64!_l=c3falFa-(g^cp ztAuJW(KRv)F|f2UHU%Px2HADW_CO6XARB`7(@M${i&7aJQ}UBi72Gn5Qp@v;vWpd* WgG~#!J~jiYWAJqKb6Mw<&;$UCpXPl4 literal 0 HcmV?d00001 diff --git a/res/drawable-sw600dp-hdpi/btn_check_off_normal_holo_light.png b/res/drawable-sw600dp-hdpi/btn_check_off_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..402bef332269e4c5a0bfc9b912ae29eaab418643 GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfizez!?|}o;XqD=r;B4q#Np%w3D(64B0Wuw>^&B#mpqJ~>mQDJ zBcxMdqN{$br0!Y&!5vR#WQZ6o7VC*J$U3-!70OW9%n-j?X;OredpI?V4i)kjZUz%u1?^TBB!d7S!K`}aLPBDvr5 zQnmJEmN~ztJQUre!(G_+fGv8d?KT6=!#m3*WhCW;FE+h3%8Q;U^6<`W9={&{fY5-H zgMN|IbA3K}9J4mEa}9THWc~f*?RKM+2X}6lkdd69xGG8M`VZ!=Z?^@G)mI#R&~Uaw zNq5E5IE5aqB9Dt@+LLv9zJK3VE&jMTXzq+-2e=z=F51ukZ|Alc&SU8hY;!(*e_3zw zqdIw_m9x>yM24*jK|ijq1N!Ly|LWYWTrN*$Jly}><5+z3(lh!F+gLBv>^D=6*%j5= z_`St&(?@~%feDPv?~hlhT)(&P{rC5*lRkz|*D(ALl)<=4^u&YnPhGnNM1SZgD2aaj zUYm75@w#`Xd~X!7RhhQzP`5|0zhkCV zk89tqgq9HdwB{QuOp}{!B(A3Jn)XLNpqTxQ< zO;?}>NstY}`DrEPiAAXljw$&`sS2LCiRr09sfj6-g(p*OfQlGAUHx3vIVCg!0Q5Q| A7XSbN literal 0 HcmV?d00001 diff --git a/res/drawable-sw600dp-hdpi/btn_star_off_normal_email_holo_light.png b/res/drawable-sw600dp-hdpi/btn_star_off_normal_email_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..985ca74c9e010c45dea52f83281ad2ead1ab3d59 GIT binary patch literal 705 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfizez!?|}o;S3CnCp}#pLn02po#yR-*g&Ag{&wY#3zwQ$nEd|y zmp{rXAi{Y>SvOZPl5fhSCB1JKOyQn8XVTYq6>8sZZ8*$V-`lbzwkglE;Mh+yPPQdW zc(%D0bOaP^(PippRam-Rwn>nw!GO!ZE6OKzgGz(ltVP+Z_D>xxUff&6d_0<+MNmxB zRu=DpvrAM(j+?RP_p&E?23#+Eo9^v#a2C12 zYrAEhV&3E}#$n>}#){F_r<1}(r@Q{XaY^cuM&8MaWUu{Yc29KboDHeK^)_?MAn7T^q+B+7Cbpc~owZt`| zBqgyV)hf9t6-Y4{85kPs8d&HWnT8k|TbYEm_7=9ARaU45|6DKRpl9jdt#xDF^_QUF$NoWX_NgEeV6B|F0H@FNq zAVx)@LkM;NX{rw(A@za+s9RKQG^Du>h{-cM|$zCIyI%y2_A3eNY z<5<@tsj5hpK!E9Xu@o4_j;2lQ3009S_l*Dm&on>aZeMt4Zg#f;=(=wNc)j#7x7%_1 znPn;Xe9mL{g#cBNLOjpxU;5t9Gy7CUYTfVxNRl|bDZrM!4+L1)?ZlA{EkIQyi{H<@ zN)Y8~tyT*He)fo}NNYOgTCc!vKhK0$GV*>V2teGh0vN`AnB(Xb?H;!SE*JIEh7_PG z62@t+@`=#93}at4S1wn?ALpZ| z09*xdbBTEtKo-ChfEhuQ=S>Mv6)EI&lJB|Qj$I^4v=dgVlOze)NGk}!3etAx!jU8a zi^T%Fov3Qo=I2W=35liBrb8>~(26>WS_!3+p3{o@XgZTWH#awbQV`{_rMYY? zjIm?eBTcVWnoYA<5K2+DO0G13?YUfW`}se|&jBcF4416|L6m?m7iMDqB z!{tub^~Ch_9R41eet+lg(><&CRr?hZ!(r}B%NFlui)CH9;@dC`AR0}?Bhkd$9i8%b zmHew_qbkyaUXDJqb!*@$hofTgnL?qU;o8vD)nqc)C5ZA!H8C|!r>aOKMcKOBTKN-x z|C*ge0Dzg9IgE_Xe3Z{?9|)pcb>W%X52_;V4Q=*(-O}O3fNMij@f$ZXy97~=n2E2a7n-GM(pL|80C3V|5J1{& zd_4l5<9&>yj(7k7&ocn<>|Gb&a#8!8PO@J9plK2wkK@2y6`(4TjpOL&>t_EiI1jx~ zRV3=J2>`Ii%Q-!3CBW--dSLRVW=a4_5?zabn=A)iHxP*=;$zomW4dmv>dUeeD9UE4 z4on@XKCkoX)jEnyrht*rnV-|?{2Ks_WHR@8TPuHXCD9fO;N|GOX1LUR4ycM0Xm9u5 z3^u#VY`ShB7E8q=(M0bap-*0H8|Xj!%9b$K6AE#`$5wTk_Ui}&b}|NM-n?OU5@{hO6SYsIS&n(sDqlfkljw~b~^vN zscD*yXf!b%i>JFgI{Q}rl`e?#IDmr#{nDXev!^#4=E5X->vl%dBq++Z6TtVi@TqwP zSeDub+-iF#Ceye$82fZQmU_Gr`Ninyl)s6^Q;%I7jDH-Dr(qbzLheUvZl=2fe)h$Zrkidls)njal&VPX`ZJZ~ zb*YL}cWcr>0}V9LKu!1u5EGEH^r!V=0000bbVXQnWMOn=I%9HWVRU5xGB7bQEigDO zGBs2&GdeUhIx#UVFgH3dFmf^Fl>h($C3HntbYx+4WjbwdWNBu305UK!F)c7SEiyAy zF*Q0cH99plEigAaFfiW+*+l>V02y>eSaefwW^{L9a%BKPWN%_+AW3auXJt}lVPtu6 S$z?nM0000FVdQ&MBb@0O`jhYybcN literal 0 HcmV?d00001 diff --git a/res/drawable-sw600dp-mdpi/btn_check_on_normal_holo_light.png b/res/drawable-sw600dp-mdpi/btn_check_on_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..aede5316a231c5a9200ba579e5e5c59590fcb052 GIT binary patch literal 322 zcmV-I0lof-P)jR#o(5oI(5nvEp1TW0obw{ zC?v30PCEx6I|7g8v~>UofB5jB5|lXclnu0X01J0J)qf U(v0{{R3 literal 0 HcmV?d00001 diff --git a/res/drawable-sw600dp-mdpi/btn_star_off_normal_email_holo_light.png b/res/drawable-sw600dp-mdpi/btn_star_off_normal_email_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..a4d414043f928ff224e19e12d67ca4bc0802d752 GIT binary patch literal 377 zcmV-<0fzpGP)AM=2V^_RKc;CG8O>O{pve~rJ*|Pv$FJlTE5x50sPR1Al zpIH7jfbJC<0$VKUbpQ%v9Zi5m-MxN0i9mtO=4I7@^Q4qMf&!haLU4<*ibr!GL$X=? zJ_Ao6do~GE$Hp7T2;ty#K&2YHV1Xo(3Q*E5wYT9K*?t&-+N@u34m6luJp#39wFPoK zf?7x?J(Q#ZE1Y(F597THOi)K{SDFGfGMfW5nB@=XaL<=BS^_B_qe!wA0rKDx8Ppxn z_A<5wr2>_A_IKElL&aYW>hrB3z=Ll2qeJkqvSC>bfihU`yTuzP|L^59|1p^b-cH~T Xbk+x$V6i?(00000NkvXXu0mjf(0`ji literal 0 HcmV?d00001 diff --git a/res/drawable-sw600dp-mdpi/btn_star_on_normal_email_holo_light.png b/res/drawable-sw600dp-mdpi/btn_star_on_normal_email_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..fb93247061912d1c9c44b7fb0696c186a28242a5 GIT binary patch literal 607 zcmV-l0-*hgP)+Ln*)vKX~ak&5} z@pL>o&mel9`451|>x+{Wa!vq5Wd)#wlZlvx(Q`$oCl)|xH2~JMtNd;OcAg(sTN+F1+*(m9=gdDl0B|4* zfC4DdMzGQP;JGIU!?D0vAUtENu@l_Y7F3D>m`ucLUp}zt47DVU?~=52^RyUCoh>%l zcrvkoxl>{Qbl749yCXy5J~~{afi~UZ3LP4G^WtEQVgSZa&5=-LySGT=0zcKgSi@aD tYOzH)Mp6m>U#MMbs=t!=f6e*;&@WY3@`GncChh~JA%@0QCMH%OuDO+gL6PCpsVEw9^HVa@DiIotLkvx=3{0&|O(7cY zv)yzBYLEok5S*V@Ql40p%HWuipOmWLnVXoN8kCxtQdxL1)du8VPgg&ebxsLQ0B$OB A7XSbN literal 0 HcmV?d00001 diff --git a/res/drawable-sw600dp-xhdpi/btn_check_on_normal_holo_light.png b/res/drawable-sw600dp-xhdpi/btn_check_on_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..121986c2d3f4a0e881f187d73a31b4060cdd0a3b GIT binary patch literal 946 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;rX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfwTh{zxw$&WDrp5WyYa@^R2Y0be$tqo-@%hv;;0+Hnncc(2`fG-87>K8eB6J; zOgCeLy^7nRGoKlb%-we2-J5H2bI#w3yT9*!_5CyFd>C1n8XXjVus^7jaSDF+B=^N( z)isWm#-Hw5w03kT?wQH?Q|XKHQPE`=XU$Q}jaoc8{$6l+^xW_z95xNnm!!?qypcCC7VH zb=IF_JbOeh-%@e9@k0KD=}aq&E*tP`V=ZY1^;oZ;Hs8_s zJoQ;wWqMQ1`(K~f%mwym*{My}Ql9wXnv~|2i`H!G8b1~lOKa~Js59HQ(Z-$m;IVky z!?!0rw$Bf-pH{BHD3rfjeC!7jE9zxUo`C zTlhuG--O7@eI~keh-d^IO-aE>oWL=>(&dZ1GkpqrHb3g|b)l&J;R%=CIf5(vN1mn=kACZZBVNk-UQ0 zan6hd^Jj#AzZI>X?V~GH{A9xSjr+dkyZNp!V9-gBT-z0QxL4uyzJs6kXCHoG%J6_^ zpG;zGg5tl7|0mzydUpATq*ZhBxf@@n*atq6&N(4}A#204-V9su6CqpPe`&gw6%)PD z_(g8VX7d;?wY$AP^WJW?IM3w%;HvGfNW;DQm#U{FSG{9nkYQY3e3jMf-mY0(Z9iVU zu;9HOqcC~my>;??LQC$l%QPB3xBETU{jT6YDJy-Ib|zWnclWEbUhJFjVyge`|4bJf zs;=l9auT)FRAfD7lE5q-&&=UaFTi4}-zZe?IlWcYL{iiX_$l+3hBga+dfLsKgQQ!7(b zh=%)YH(h}mBtbR==ckpFCl;kLIHu$$r7C#lCZ?wbr6#6S7M@JC0V-nfboFyt=akR{ E0N~M+*#H0l literal 0 HcmV?d00001 diff --git a/res/drawable-sw600dp-xhdpi/btn_star_off_normal_email_holo_light.png b/res/drawable-sw600dp-xhdpi/btn_star_off_normal_email_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..e740d9bc05648e1b392baf03ea3575d365b2b128 GIT binary patch literal 935 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 literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/btn_check_off_normal_holo_light.png b/res/drawable-xhdpi/btn_check_off_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..d3809fa13e127068cff7698eb0dab6f710abe067 GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJP)`@fkch)?uPX{Q81T4U+{@5m zp?AWy=)(f-+nPDnH6LB?OqLFvI)~Gt*yFqSTK*GfUia=|5G*{G!=dnUh9J-cFgUP} k;kC-FVdQ&MBb@0R0XynE(I) literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/btn_check_on_normal_holo_light.png b/res/drawable-xhdpi/btn_check_on_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..7e1bc8cbab50a87573cc8cc84b80ef8ff14f0e1c GIT binary patch literal 496 zcmV*f=(6DN$~F7V}gG|r(k#r5@ZCEF~lH5 z6Xt@6B-3>L9x092+taqU4D!L3jcxDu`5b=l2jd*a;bs}`KMF)47eoPACt*eqge%Lk z4Ciwl{i;ARnY?bd+otoi-XI%9fpj{($}*~|?hJ>+7w7vbcnU-!kwq3M7K@tzZxZ0o zkT_KU?iYeYQIu_f8wB_hB<9%zECkBs@-Dz%aq_4MW7Ja+R6y7DJ%C#{+%(N*B9XZC zoC7QbDwWDUz&kj6Fc|1O&!40Ez6C^4T%y6N)#}0Mc;RqWmgV(veP05(TrLiUG{Z0? z61-Nci5~FBd_KR1YWfz)X0tI=Xk4__YMr{_^?Lmf9@{v)-|s7EFRB$tpx5ikcyW+8 zL+=`m#<2(dA(P2O(KmqwG))soUES?=Z{YGyf~QicmGS<-0<@z=fZw~}%miE#9SXc4 zNzyXDkC^~X0{Y~R1ZOTll0b0Uj9@O{o_Mxx-%c7(p$MRqN~Nty@K6P&&0N5@0L%PX mAdE~c5O)5q!2IWQ7Wn|`VD(!tOaFcV0000ht(u000HCNklz4qE`FWABs zw(z1Ou-OSBLKRI!sAdP-DFIR}v&b}a3=@-c|Dyt;42`2y(MXZQ@G4`(=w?bT{0|BU z!E1D~Ef3PCTxUee=cs@vrk;SK_rNdHyUQ`$5cwi8hX z?jog+{|66WiRa%D}pi${fZbNLjWkEmOH*45W6@*9gv}0_Bc6B zk854>mi|b8s=m=uRI*DAkLV99^hvFeN&qt2q`qVxwZ}lEB2MxU7sxf685@Q;C7cOX zOuRi6*hd2Z{ro|`h^jdN4v|hTg8($ps{G^LA_8S{>g&7Bu1hdLh5$ljW z2sQN_GiNmy;c%KZ!?E*RaXR_rvKZl{iMxjarRteST_*z6@RmAq0B&2d<_5BAm0Abv z3S^Jpd4C1?jDto7NFe#i^~6kAxO*s&jc1=y3PglfLwts-^mrWDS#Gv%Z&zSV1!e)B z5EYy@lFmJ@$X-u!*Sf#tar)Z@B$Z&yM zc`G2{0om|7M7yH`{RYJXqb~iJC`Z-X9OKS3?ziJtd5>20ah+~CY@;ZqnPO&$Nrz-C z!W`ri#eaZH(&cjMe&&sHjTXhAjS8Vl%RwTaxRm|V{QbLR1I$yK(y63@=bnfed9l>f`XgLfs$HLAffxyfnURUja@@iew(z3kUyn<* zmH0+jr2qf`C3HntbYx+4WjbSWWnpw>05UK!F)c7SEiyG!F*7ppnF)=MLI4v?YR53F;GBG+aHZ3qWIxsNES7M+5 z000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|DhWnpA_ami&o00000NkvXXu0mjf=w$ca literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/btn_no_off.png.png b/res/drawable-xhdpi/btn_no_off.png.png new file mode 100644 index 0000000000000000000000000000000000000000..c8bcae49b89ed58aa34183a176b20bcef163d42f GIT binary patch literal 1511 zcmVht(u000E(hwdzlx0c8v1M-viN;6k!sz=VJaKaAZ2!(zN^@2+uA;azrTXWqQ`{l1ym zHxCZwP!8o)7nf57;U_>dK3?Y-6_f+(Bv@mXI1|jNi~pkoqKXDO2+)YzqA@o#4;VmIH>?QL z)|FXOlv67K`q|K;1u!7WXyv->*SO6O_ExpjF6DCqvXSOX9^_gJB{}B^5z%S;$oldZV6U4G2H)5Cl>Tsl-sC1JyDtizJGI9$b97YuP3tmj0_ypy)F*=9oc2G!kPiEd_FG@`mEW!*&Rj( zb~mO)D&+t@z-~AV_KXaq^8BI;L!T*WYX)|G&66!UM4vfZGFQi*tjhDOI@dPbhkllR zTj+OVZR3q91~h^Igv1jyWn#XUvkuWC+=TVnEg4vntH5vM zK)5-bVcsr!@Ztwxnk-1@!03gGu+-e*sj=TA zE7mp?KlPLVO{BCb;ii|%8R#E6ARi^d6)J!vLuMJ4`LX*@u7g^HC_O;z5iZ*04v6vC zY=r5}NI(mp%0`Upf>u5tyhQi_fHeI)HcK&08BifgTs8uQq6gaOl5g=`8Fr5Axt3I- zcx8IJW4_N>?1fjkz$bG1jq|O#U_LJQuXl>UjGyO{oeU|%yo(6$>bL7b{>?Qqe}$xn z9zBbj;EugpvT)H&JLhEcDZl2^%U>ahv%xv(WGyF@xU#OTTbTFI%oQ4S^xrY6<+97LVQe}%|az?Gso9NtxH-qE2UaeTT~LP$phL- zwix11^`2(qV&k+}?M0!B0O1_P>PhAp=9yJ3v0w)PL@fc%;G>R8%4NBJomJ+DGeJy^ zJDu@R4&_i@b@>lWz@e5=h^2G@001R)MObuXVRU6WV{&C-bY%cCFflPLFgPtTHB>P( zIx;glF*hwRH##sd8;Ajw0000bbVXQnWMOn=I&E)cX=Zr8FWQhbW?9;ba!ELWdK2BZ(?O2No`?gWm08fWO;GPWjp`? N002ovPDHLkV1noap!@&; literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/btn_star_off_convo_holo_light.png b/res/drawable-xhdpi/btn_star_off_convo_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..8edd94cd3a63591cd782be9d57f588b883aab070 GIT binary patch literal 935 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;rX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfwTh{zxwIlntC0;uB<^TVk zMkYKa=8tX!$#X~^pI)K6{X@X~Jt-p3U%Ys;Zi|u641>f&78Z9bm~3C&ZDepIQsdB`UTzc6_3l^AADUbyJJ;{ zdCbvEN5UGPwH5F%vOLT9<8C$KK;|!VmpqR`%Y+@uYkM`n8Oj~8d?0n%`IgbQ^D+?= zt}-!jc6~i=DIk@ssK=eeVsVL&QP8nN^YE6ShR}+jZ_Xz}l8;Q9kaQu#ge$htb8*0m zYcKELU}_9l$>x7R-d@$x(sh2rdkYyU*5!Nu-Fcr$j7}$y(|$ zFL$2AStFf*<;CybJ8gcnbl;X`4CnhCtTu(NbQY{gyIHcWR{50X%**L7CcHHDzLs0_ zmWj#o_Qtbesh@cF-`JwRyjok&azdip?gWiWmG{@mBWq9bm+UQL_jyD2Opy?)QIX>%L-}>&z+{Ci-V*^@~5KTq;v=jdfndw6E@; zUT6jGxEFc!{3+{2)k1~m(=XJXG{3|6^UH&$wQV5{hk1k&mhjGsIj~{cZEw~SMF&2W zI&d+uta&p}q4+>4SLj@xuxE`v4%fVv1?EJQcX*soxw0u|t8VQE)_FIWs`o9lyi&(+ zlsB_sOPQRMi1)VR68Sd_lNr!_Pr6RoFV^QBx0UkHh>Uxe z*Vm)Tt;5bVZ~414pH~f!rFv73cdb33ukoRvCVR{K?B^DZ literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/btn_star_off_normal_email_holo_light.png b/res/drawable-xhdpi/btn_star_off_normal_email_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..7129d0016a6c1fb8234dfeb4625779ba01f2527a GIT binary patch literal 713 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 literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/btn_yes_off.png.png b/res/drawable-xhdpi/btn_yes_off.png.png new file mode 100644 index 0000000000000000000000000000000000000000..97d169b6112efe73e58632086f4b517a98c23092 GIT binary patch literal 1716 zcmV;l221&gP)ht(u000HNNklpQ}t-UV)z0cZv zFWAH;Hu17U%XNV86JR%fDk-9n0wByXi%c-b0F!d=1qL9B(K$$fI`S>Vs|+*A4aVid z3kX06c5#?jvQT|SJA+a_#{h(z8r~;hnYR@pO!5OG^67I3K;*N7FDXsDh%(PzLJYIW zW1?tm!H0*PRM0@7X=sS=7?rE`_MLNjjl(AQE8Jv^PUdCAq%#d-h^-7$#eQ7sZ7FZl z&fWjPfbi1Hhe~IS8O}2-=N%aHvy;=5s8LbQ(P=lvWoJxp7>M26Q*&xPbr-db?rCL7t_1s8MqW zH|WgOc>>qzR=@nzi0m2AU*{A)V2vKmkCm=P0K!MBV&(!J8*$GUd?U7t*cbF0zX0H(Rru1itr)0Q^>Kya z-18TC)X~URj#3RER~RH_B|rAYAplXv0RW=>C7*1-{pWm50Z<&@7vv*)l=y%sb7r7| z3IOK$X9MnQXrvHWp^XO#!6++9woORijP|JSzl%FLy4U!OeG~#+T#|z((Ji(S1Zvq$ zuM-0$Y5~rW(*y{O9m@R*ZQM^1hL{Gxm8dQ@4Cv}PoV4LOyZ@9X3W09gWPh?$E{j1< zBw4p%z@wfyoa=~fRh9>xUZ&}B9;9h)>uukZo|Nu3d$6k*vkoaaY#UCG7CcEBZ3qG zy+%XrDqPUm{$RfqqL;BuTt5s+GKCG7Re-7#{zBuO4e6_yeTmES zZr~vu#`c?VJaJcuzpdaR4ihu|=sj9B+EOET&aPDF)~xm>n$=c;M-t8I`Rr}x-mWjDg58u7pqK*Ht`D=oBq0WvlA&BJ zZekOgc-iqE!HdKzmHYFI0000bbVXQnWMOn=I%9HWVRU5xGB7bQEigDOGBs2&GdeOe zIx#mbFgH3dFdK*gl>h($C3HntbYx+4WjbwdWNBu305UK!F)c7SEiyG!F*7eSaefwW^{L9a%BKPWN%_+AW3auXJt}lVPtu6$z?nM0000< KMNUMnLSTYEH34nJ zaCd?*qxs3xYk`6Ro-U3d95Yk4nJ zaCd?*qxs3xYZ(|A4|}>ehE&XXd&@gp#8IT}VR}+hQ-;G)?pKEzq#a!rD|Ec5|K9Y# zOIgTKuyCouv!<>|Gl~L@mk0->ncQ7_=KH%`J#+U$G4xUH&V z@fX#H7VDBW8g6e}{^L%e&R!3T-st^5{|MTZig3&RUt!d&8k2wK?Ss90!yC;D9ov`Q zdg{5O$Gi8VnOw4L`jscMXK(7dE_>vvr=`vcrgOKa{u4d_VdKZWo3rN6knb!1z^A|U zwt@A;0{7e&WwQ?fzD4$%ceE?_?&Pny6jh?@ZL#p(n~fI_UulV3q5d#j=BVhLtTSs9 zS!c7$r&SAy6-pQP?%ZBscTmc_V4dN=w%h-~lNWzy_InM4nJ zaCd?*qxs3xYZ(|AFL=5*hE&XXd+TJjh@(W?$NMQFiwc`}JnA~_$|GpovEbj^rlv;+ zo13I71Ohp_tQU3OkjrYzY!+5KQ&O(-zWCLuc|V`6UcP#N)++bV(14rsLYhD3M>8t6 z2sm*hGOgOV&iDqGOOuC#ih_^;X9tT5lM>^^1{CgD&lit{zMa*$=NRLX@AmQEqRs`j zcMdc<{+7GvFZkYK9Z%o=2M=1bS)PcQl}Z@kMR{v(IH z)QQc3oKt2N2v=vX0aLKjrkvlid&#LW?ICjXu za;bpb(SEki*{+8sq|RBjHN24h{?w^YtjeyO+ka%T*r%*eC;9Zkdl?({e-KmenHL#0 zmFXAP{^%2{Lph!Ov{;K}_%~b%(koG6!0-Mas|{LrFA=Lzt2nS-h;PX*`P-X2 z>hGNss@C(gd!F<5$L*blA(v{**z6CQ7EAA)JHtiGX|cJJvfGlYiv*P~WnJtL%-ouB zq+?5z$t;duHK)a$7f?hbCWdvp^?0my1?vbDl-esOWvH~tc2ZdV{M_W|_${eot`&_} Rgn*7?@O1TaS?83{1OQ3cRZ0K= literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/header_bg_email_widget_holo.9.png b/res/drawable-xhdpi/header_bg_email_widget_holo.9.png new file mode 100644 index 0000000000000000000000000000000000000000..fa411415d2a8e3c344e99b7a2f1232f2ec5bb1e1 GIT binary patch literal 473 zcmeAS@N?(olHy`uVBq!ia0vp^Ng&L@1|(N6W#a=4nJ zaCd?*qxs3xYZ(|At36#DLn`LHy}8ks*-@h9;{P7)0!0-`xGxzqk ztL&9!$M^4VitrH-Z0gt`C@3XZ$babkjiV+?humB;Tv~cN3QS$W^p1F6sf{z-T<$bi zL{@Q`EP}|pxl}Cd767T+k>B#Y1Fo;02c%0usi&mH&4okBNkGs{$w^sh&jpBKAHJG8 zF+hY&K$2j)?FwCX%u-?zOjK4v2ucYmc5s9TxA@Bd-3F9l5qy}{b)kd95u_HZY~3=B z-VUG$n3#W18SHcpr9)Rm7J~c(B-&@*43}r>JpuGzQ-{U5w^6^MYA2*G7Zf~Z1$0xN zv2p%<`}M2&KfQriyTT%VUwYpC**4wtK0mFlV?4`VcA~D?wF($F44$rjF6*2UngGv2 BuVerK literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/header_convo_view_sender_bg_holo.9.png b/res/drawable-xhdpi/header_convo_view_sender_bg_holo.9.png new file mode 100644 index 0000000000000000000000000000000000000000..3f57a7bc605b35d2bfa93fbc32e8782b8a00fe1e GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4u@pObhHwBu4M$1`kk47*5n0T@ zz}*SLjOHg#uLTO~d%8G=NK8%s^Z&m+v$FEzdbU3e6DKw{KD0l4#`wq7rLzqT1PwU( h+?F}HY4{o(VBFEjA^7l6>K>q022WQ%mvv4FO#m=WD3bsH literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_badge_attachment.png b/res/drawable-xhdpi/ic_badge_attachment.png new file mode 100644 index 0000000000000000000000000000000000000000..8376cdf6e23d0b2f5ba111eefda4c8794da8c9c9 GIT binary patch literal 335 zcmV-V0kHmwP)%`eC1fGi}RBWnUh zOE5G&OeZp!aXEk&D&L3A0i94uB{WT0WW+Nr2XI5>d$2hG<~vp-4NOSlplD&CFbXuG zX4PSHz)UO-Kz5iKg_eW#H$%;e#}@?(REPq6cnT1UVRJwfjzkEGw=f`XpwQx0AkF|{ zXd13=gjh$m1n&PjFvtnl##l~`1$jEHl`F7ST7x#z(GNK#t;sBDBi+yr%%wS%g1vKsV8Sgch?%w#V19<=d002ovPDHLkV1it< Bu{r<% literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_badge_invite_holo_light.png b/res/drawable-xhdpi/ic_badge_invite_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..b3f1c50a23c68d519fd72bb950bdf67be0e92f01 GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ3{Mxwkch)?ukYpsO0Wb(T~F1S zW+R>4`N;lb*Xy1=PRtw(J0&Z*64K%VjB5KDmNQxiGI3=5ZMt_|&Uf#01||-Lh6Z7V z_2PecFU)3mE9+IxU}7YCo@r9J4O4`m>`m@%V&#nSdB-JF)*FbH_yo=e+R5PQ>gTe~ HDWM4fok}s$ literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_badge_reply_forward_holo_light.png b/res/drawable-xhdpi/ic_badge_reply_forward_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..79b07e0a415c6e872f6504ba3d9c33d52e8cf298 GIT binary patch literal 394 zcmV;50d@X~P)kT1 zybg=yba4O=5Ffx{IX?V>8V-;E;`3x!PBjOdB-3)LIY0}DA5!7~QZcqPAP2-Z2GRi_ zEQ%*Oh&6mNB@Te)hP@Ox0Hk&)5VMi%00yY$brd)NgttJ;6|x)vD-mZ>-~bRl2E;<- zIsk^-DR2M?UxJo1WIF(a^C@t^e`pq0p~L~`*Z|r__zA>{K-@?~c6|=jhv@)fFq#9l zKn(?@5q@Y{Kx~;|PpJc3p@#j07G8BE7F^#zp+>F)n2{>2Y0xs`EeV!`qAiV*5QswZ zy);^rjSvRu38z*XAT<|!gGL?0KsW#tMV5HW0UA2s3ACzb7zhX4hUOB6fpEZiY{f6V o9B>#~I57;A12{=;kPH+C05ca@n^K3jb^rhX07*qoM6N<$g1wfL<^TWy literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_badge_reply_holo_light.png b/res/drawable-xhdpi/ic_badge_reply_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..3f69cc412409742908c1197bb469c345fb818011 GIT binary patch literal 330 zcmV-Q0k!^#P)%z?TgZh%^XYA1G-3fOs7d zdSeL{L?B1}p`aj&C7{2Wn4&m}fFXoRyE-g-K0wVSx-tsK;|M|y5CY<#Nb0u(u{1f= zaR{10q|^l`p@}Yy(gsN&7Do`H4`i_%wVGspXpR_=MyUr7e;Y^#AUk5f900;jRCT~; c-*BKX0D5;GA=n0ASO5S307*qoM6N<$f|tpGMF0Q* literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_forward_holo_dark.png b/res/drawable-xhdpi/ic_forward_holo_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..95e617e9a6b61f3ae87a3ec373dbcf7c29a8ee6a GIT binary patch literal 549 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV0`20;uunK>+LPabY@4910R>R z2zhC!UY}R=YUP(H`~klhi(Z*NiE>)M@Joo(%S`{JPeQyj7q$d>`7M1iEBnRDDIeZH zZSA^w!?)~M-aYAWUpB?v%QrT@IkU6DP=}Kd0aF6FmWn`%M))=7Hz50{OU*F^tWsr*y_!UT1j!Xv+n-don)RxfxUynxF`wzFF3y=7AH&h^tSSt*o~2IaWR#Hnni)cq z*?i@7KOgeE#m^^uGA_ucQ?NjE#gDDi&TV_T{==>A<+m9&T>MmdlhN#+gQl^=no3uV z?Y>uxn7){<=lUMFN55cgW%`QcmH8W<8>e4n)H9vW|02syv1sAXo&fjHnj8II%qa`F i#K3a^2{}Cf%6|Qn^3}r+-#P)KpTX1B&t;ucLK6V^)A36H literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_list_combined_inbox.png b/res/drawable-xhdpi/ic_list_combined_inbox.png new file mode 100644 index 0000000000000000000000000000000000000000..11a61b895a20939735865030b13a926bbfd339c4 GIT binary patch literal 1006 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;rX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfwTh{zxwXF+>d=Hnj9pRT(+C=ERfJ#v~M@x zbAd@QN$PzoSKUkhT--nJ{qxWN?8@ujX@sl@Sm@OH?>$%9zcs5?21x~ME%Ob{JswcE z^4oLe3F!~G?{_cR{qoDw5QT5;{C9a+f)>|F2i(d3qdK8XS>U#TO~a?zkM=fKvN5ST zGiNwj=N~Qj?qE9Gj$`TEtNdmS2Lj~Q*ojKN>SQq6E690dQnKdU4pyc7oHFwXNrzQS z-@7^$&VTE1)M?`PS9&)$EZ_T6w<7P{lb9ytW$XxbDj_ z(Lu{>W#UcK&ERWf|CNW4ywBr-^+w=#)Lg}^_Op;ml zm@UsaxSOw2=chIIsb_oGl+#RX3(EdK*(-6vqa|n~_p5n3-%Di*Y~`H z?hviAdyBeu#jh>*NC_8?|9x?d;gLh@QmXR~`U$Ca9+}amzRIs=Pm$AAVcuk+lLu|z zA9pabpOPhAetT17`W(aTo4Jo~T{@g{zGMn>g*+l8&pa;sWIS#x+_U^x z9<%1XUx^O#8uxB}&YR%=p!q(#lS24X=bmrt&;NM45|j+N{+55?Pd)XT)iV2X4KORH zmbgZgq$HN4S|t~y0x1R~14Bby0}EXv(-1>LD?^fz8paw~h4Z-9GNil&vs!Pk8K!QZk#ZBuAnv3a%R<{Nt$~^F3evOBDw3vEg8*4$^kC-*E3a_ zzj!%!!TNYM<-bZ2uj>!a{U?0q_3{H3_cQt1GllmdKI;Vst0B+cVdjJ3c literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_menu_compose_normal_holo_light.png b/res/drawable-xhdpi/ic_menu_compose_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..a2d821ae3ddcc97f246d7518f8b156242883bdbd GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV9fV)aSW-r_4bCp*I@^d;~(?y zRxrE9aJ)M*RjA2*h3j6oy^ZM~n4H!x{CZxo$@R!y$08;TVB~NBGt;y^Ck39~#IspP7$V-nw1cHDO6VW!4o+T% z4eWEQ+Ga5HJMalGCq(T&uv57s|KM^_;RLZqJQm+XY{U+T8nE5dQebmU=aw)yIsbS+=_n#5!KZ$?O7nZV0=yoeXyt;y6!O^W& z4aa{S_7*%~#msVdHp>E4L@=;ixG2=XV8!==HAb|%&ueRdJ1_Z0QN)bgw zu67jMM5N##LJ6f z`MXfUO0zy7Ns>$gbYlpo@EfyugI}EvP#Ju8)wQ3^fXE8KYVb42uk+qTJWUH?s{y(( zjFTG(UJGItud&>20J#wScmu&xg=jTEn!&59i07Ew3`DB{auq-IT$88nB3=ZDrL6|Y zg}Z&M1;HDFXcj;&-0k5l2wq)Fco{I3nhlT-cRUNhYeCEfh+^6R`HG)V=54g8&bI9)4vt2aX*@nnMmhHw!(s$O5g1H9W{@DUu!+HPWQLJI%{s8eI)Ph)XGLhB5=Nh&%Wmp6A2AgfMLuD^_(^ zrF8I;wXSe6EOzGCxPAl|q<99A)wcKoK7_#z_JrxNrwRRlcrFkDB0vO)01+SpM1Tko y0U815^ojrxAOb{y2oM1xKm>>Y5g-C=&-w%I!s(Fo^b7L<0000#rJxH^U8)@()_Wd2 zi4tTFT;i-Kb9B;5bH-Kho!RMn^J!V}9Ns?98%}QHSyKxt&+n zF#IdvnW1OV!het9Uxc`VenVbI@PQ1+rtOXA6F(&IoZtyyjWUeyWsOZPX*f8&;pe*t zVWH1sw6zZ;FW}TsahSv2_g!!iuMYG3DpvMo4W7^MPs=3*zK8_^ zk6Ia97QW;v6`Jvq>mEa0u3CkCdQ;#9(@6@~8qB{%%I*;O^OB>2w`SAl(+BQNd9ZI~ zc$blAA5Y>&_LlXM)u#%tvc#$0iQahO>y`)2G2AZKbY~d~9B;U-`Q4sn-*J}tTb{+; zJTqVNNzw|@Eghx5-^{W-xFl-ByN2E;|2o6lOHFSu$wkKRW4scyE}m`P?d-4T!kWc% zE&qu8V2astP=DpKoHTRBGt*M-5179$d{B4DWhL(&*08s}2C-rdrVr-zvMz6YZM&|T zvu4ZHXUqxlGs{|}4%#Ff+I1+gw}3v;NxX{C;C(^LgI;^ObF_Vo@0jcdxl{@v5SX-Im!_FU)u@ z-1_a;&i}S0)9oxtl093b^G$oR|7+Q zCAVyyb75YAuIAjYn^r8C%-FEw`^+UO6}L~G^7U2v!pL{J$?40I>q|QGtke#ec-{Nr z@U-pOm049O3!hK9zIUvs_gx+edgyM38Wl98IX-j5hV zxfj0~k1)&4UlTDibEWlVW)qp`)w3p~FJ|!E@UTtQH|*bvlEuMVR;h3A->YK?e5w0< z^Noc3Q_VU8Q~!OM`@pdONB%4|+sTu*?O)r^tP=BG_}wigDQREUsl`6$AaSA$#I5t9DdddryDZe*z)Wg zUx@|xz6pCY%GqjyM1Q^$a5&UBuI+e^9UA|>$kLrpoFK6A-c92&{ zxY@qVc-`JjpF{U+uxG|?Kd>(K^rxu{PHh!mbvy6v`h87Pwz8`oTw5o+%J{0_r?vZv z4v3mhWP8tU`M5SjP=00Vff~k1m%c6FJS(rjRVQJx@F(+uRc|lWUi-t~^~yKn9s{R& zZStFr^f-o4;a#d%f;aF@*v#;C{=cOYMb~x)J?U}t7oVazbO!&vnyXs;Q z3n$kKU{+8qag8WRNi0dVN-jzTQVd20hK9NZ7P>~JA%=!lh89*Prn&~^Rt5$~SDK}w zXvob^$xN$+YB13?G7B-Vv@$jYB8Udrb;|ZY4U!-mg7ec#$`gxH85~pclTsBta}(23 ZgHjVyDhp4h+5ifn5h!$muMd(S2r9ulaIz$u^(Iq0N2ru3`^gnd0OGQ8EB9VlI1wleY zmk3QH%8IZ=gxn;SL_}QI4m07l%o_F3or$&05iZM zkaW2DV}Y802S3vRB;>~H6mt$MWGUbi@Dca|%x672>Br_vO+YQs100f{bx{X&%8>!a zfnlHxI8hcSsIle|`I1#WYSCUf$`=J3par-DG_A`pSOf=w^FX}FyfiQg%mPcmPoP=; z$~%AtpgL>>oC1y)c)kpb0&jr{2kW&0`=$Sgn@&KXaFhXF0ndR2jWs8wCiIxD0bK<) zKMg#Pv0WD(z(vzdpb0pa=h=JUzEpiTj`T`US*jNTF68BXMt{+ z)#c(d@X+O&b--1i#`G{)naA$ufjctHxj_oJ7KVDjK6#+Y#bZrRAKHRQw?g(q+CJ-@ zfO=q$LjE$W1qEFK+7+@tGVEDFz%GSsOlEvm5U^Dt`$mYN8wMK`vKdo0lU=lRS|HWc zfK7Sgw>27|nr}kvzpaqWY!@c~_V?0-10B%+L{0nRAobHImECPGfr z&|*je*ZIHY?G$lDy4l_KnIJ!4V>Ko$wf7y zVWO%^Edl*OoCUlf4cu~BKWIg?${H2VGKfY@(qsZMz+FV+L2l=nJLPZ#FKu@t2ylx9;Hg)Y4}yRJY3DtNdqZAtip>FUygsFemV>ctpSuxgH>eIk40<1LqMZl0^^MyS4 zlqrtIkF&rtIYNnw=zoKjBO)LoAR-_lAR@qK`~~ht(u000GLNklKDB z*=MijJ?}Z^`~2SLeV*q%%c`x~s{KD%9rfz3{IzrogTA<-9z+6)>b_jwP}f*Vfco6L zSf*HTIY!Mn9%!M*ViWGPSET7OPrI^O zlBF(z2wVKz8oATH>X=v|ZTGES$Pg?1z*#>sW6b03Mi^#O#$6X6$UW+FQke%FF(NG= zIw$Rf8f8|D9kb3Yny&SCuZa~C0|RsjRxdr+>Qy6R`+Qq6-}f%KLF{=qD*Q#Q_v~I` zcT?q?E)hH7hXaIFA!vG7Q{mHY(L1vG1JnsJ@AFv&e(j%vy!Y!H<&|9IS$`EN>|7E6 zB89ES1i6C)22>$3?N10kVMI3gUg!h}*kT>talBC|`mJZBLGYp+8*Lh9Gj;+L4K{&E z=d0fXXwWFUp@|RqpaJ`g9spPe-Yvtgzg39u$gtMhVX8s*B`7m`JY-f&s(Xj32i@cK zU9h19=->NQ9pZ<@IOqH@-C`F7>YM-{b$NZz)rt()T)KRIbf2xIA5LGYFUF~Uim676iUJ}oN))#0t3_u|^8!%2s5`Ua7|fxfl;y?aiM=d)$eFl zdh$izv%TALIq!hSyn=B*u7Fy0z<$eX{Bwp&U&f$OgX9jY9h7Qo+@4-6Zco4Z>^h6!I7wJ zxMywN=Ltcu-*~klOG_pFROnka8nc~mBNaqqu~;OMHL|i}Y*22@8|Cv{N@RBWrKC09 zSy7UH04)qCThz|+%9Y#^H%R9>6-#|hWVh+=MY>yLFT$$CJYmeW3lJii>s;e-*{m;2 zOhZ)eK2|9F*S!u(7L?;sg(r;)oiVq30A(kU+U<~dLAWMT_t-HnODr!y6$?2o*yXn! zDwLI@i!GZ1jX4#X9V(Pf+RG~nFi(Z^^HeCLV$XQKl?_%1phJbjVoj;Sh~viPy0q)d zv<+J}``sjHI&94KBEcc|`?sjuL3hYcN`qM2H^xisa+y~>Tm9~midO)jt&Yys3BAhE ziU71Blrfg~|H`mJ5X<||ja6H z05UK!F)c7SEiyS&F*G_bIXW^oEigAaFfafZ`tAS#03~!qSaf7zbY(hiZ)9m^c>ppn zF)=MLG%YeVR4_R@Gc`IhGc7PTIxsMPeij7)000?uMObuGZ)S9NVRB^vL1b@YWgtmy bVP|DhWnpA_ami&o00000NkvXXu0mjfXe;N( literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_menu_send_disabled_holo_light.png b/res/drawable-xhdpi/ic_menu_send_disabled_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..b3143f63bcb7a0850d20a3f7006a659bf6997b6e GIT binary patch literal 703 zcmV;w0zmzVP)hV^AvV17}S`3Uu2& zv$NfElBUgWH<|O>nc2B)6vr`Tj>rHEzyJ)u01UtY48Q<}j7FnT2Nr-epjU(>opYH2 z)PPsOKClXWlHa8P0C)$?$f12fSPd0I1K=T0KXNKq2n~Q6z{)Xy=>h9NI}`{FfOEi$ zQ~ufmHU#3&qya?0dtiEqOa2N(D=!EQ0DvdJr4*MY5FdfPNdUMFERRs0gTAJ*S7d;T zz|%3xbpUKijrdhWfGOz*&Wu^USRmS35Ly5L&w=w9l`{rDOO5zh6o9+HO@*7bnI(iK zKwUXubicAAFIaZSq z=8T;0hfNF7l#0!`;!r_2B?=iE++0CQ5+r#M_@?{5MXpen}} zMwoDQr3rL=Hi4?N=Rt)Z)G=yxg8$oonI3}!6EDL+s3VEzIuOcZC_K~%a1tj&+j7T) znG97p+$M{Z6CY_7=mX#i@JKoFX2=U7T1#S?tOWT0$Z}CM^Tf3QvRW30j2A(D0bBr{ zjZrSOWpG~rw}3k%Ca$^>;tSw0a5=@L-nPPg07UZqucS!4h38#iO@LuukjQO2+y}r( zugD~cw}IaT5E=jt;Odc6!MqaV1K_PJas-{Y7C;qvEs3uY@cV&U0Cixg2#KozOj;O# l0T_S*7=Qs7fB}>?{sFS(*^nb5L?8eF002ovPDHLkV1ilMArb%p literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_menu_send_holo_light.png b/res/drawable-xhdpi/ic_menu_send_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..9f4c7973c0496fcd9f9891a0d5aad945e07cd0d1 GIT binary patch literal 741 zcmVl!4N*jdA*~HTLlg}`L$oy%5m7V*4GlFk6w$wunn@@T{Uk0l zga{ErgG0~|MTjKY;CT&V>eF}byYId8T^`&YaK4{+?z!iD&uyBYpV!FIlmHSy0!RP} zAOR$R1dsq$8U6kJedxg$W@;hHEXyhi(2cwJjZwV7bRz-4Lu?L*rUSzBqC&&~xQxC< zr%Di`p?B0Zz;T@W&m}W>iIJi}!~ocX+bdi;gI59KSG@r=<1t!SamydP#?xv+!~lTn zI8er|C5RFHs0V;kIK77U%;F7(^MZ&4upc+p(XLs%3q9g{EdsQJabUwb+cy^=hGRj* z0)SiCTcLK&;eF^4-)aTmEKWrD)81ARA|}9LT#5Mjeh3g#MFnWbqdW%kFh)!j4FK+- zH>X$nM?AyVA_1Jov79G&WkJLSIEsr^n&yvV2$R(W=*0ahFZh;)hz(GR-Y_ZJ6i`a5 zM%aRma6aaE@flC>r5XU`#`f?QX+?K9)`N}V__lCtS6F?P3o#ge3ky(gbYLr*0t#GM z;hsqhWLY-R$N)oGmW>w?pwmKOwIy_Y*cl#)Htbk5wv-c-7{ElHk3d&w_)?8uYG8~t z2>yHa8|gK;Wa5o55t;~Hy{Z$TYD|S!H3O{2#n7kF)eEy2YPYaW)*LzUHLU{k062uJ zkrN**azP|kNvtB3pgaI7Srn~!;;{iLRTihhMbNwe_TlC_+7+z~o)^GLoLOVyQ8z;J z0=R~QW!#$ER#+YY%@)exZDk}rBm;kH!K%#2f;duZo_lZm?@nQT_fQSKb2}c&4 zD$FM_c>p}HP@*by;;{g_a4#f2rhuQt#RllZ?pjDZ3P8O@0!RP}AOR$R1dsp{K*Ppg XJ6+k3iebYZ00000NkvXXu0mjfwFN$4 literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_menu_send_normal_holo_light.png b/res/drawable-xhdpi/ic_menu_send_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..294f4769e6eeacae2afb35346d3fe09e7f854090 GIT binary patch literal 1206 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;rX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfwTh{zxwQJb(4v?{|&=|Nme4ta4tZWKxG>o5Uj}<)|NJ&cVt; zpZ3pURuKqa!+OQVgv~=ivs7)zIZxdyOL`kE1;g*$f1o}4$R$Usz-i1AAMn{HUAk?j z>#^kR*MF6P&m0Z}9#4_`G|_W*@0{{Q^W(0F?Tc-{(){j{P;_dyyN`9u1dc0LufCO^ z!ECwpy^GRBpJnfAf7`si^v^2ror7=M&gYsQOO(v3ZdK_9|1Dm0r;*40x3iA+if4Xz z@|WlDNyx09qp`L{>7dZ$Yo6&pr*yemWbBSTx9h<6#VRbnuX@h;x*_nU&iqG*I;CwM zYF_S}W%u8DFZ<%KZ2{+PBi?Rgt?4=vG*NWkmcX1-mfdGM{q{bHUYO$VtKxAt_l~0e z&P^veXT%jLX~k!`G-=Chk6Cf!`ngVdpNi|6Dwp2<&RAQsk}usRFI2TLLgmDoEeFM9 zzP|6Vk7Zdr%RxorYr__!khgOlWJ%|4x-#W*(~{Y9R;W!l5tCia*{C3#rgtJGEaJW8 z#>fBO9qY_WU3gM(<-|o&=k_ISwG3C9c=FDg%uCwqCY|~;bFq%@WG^O3zUqrbFZbx2 zh)H|8=YPd9Un9p?Ve6}IMweLz%I@VdF{#$w6kiqM9Wsu?^r!A zNU&*_H_k23X!sPZ;a>B{e=NOaiG>?7Y4!Jkep{u$(Q~j9T z?HYs6GwTkXvKD?Il-+%6-}$ilqMPgIPbpUt;Jg-*`;O7|K8S68`IuFF_1)*LY$=x< z=Lk&|Jn^B`cJt<1f%cG87N$D+C7;YzN#(lyn`nBX`4lh@0W*@G)jwu`U00!QYspMt zmR2osjVMV;EJ?LWE=mPb3`PcqhPnn8x`t*UhNe~q##SakuDO+g!KUQR3sE%W=BH$) zRpQog*p6Eks6i5BLvVgtNqJ&XDuZK6ep0G}XKrG8YEWuoN@d~6R2!fo22WQ%mvv4F FO#r*w6#)PM literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_menu_star_holo_light.png b/res/drawable-xhdpi/ic_menu_star_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..b69c03485e239bc08445b5e1d3194c4194a02536 GIT binary patch literal 840 zcmV-O1GoH%P)A#Rlv1i5s#r=Xt))mxSo|P_U=a_B2!fte5D`4tg9rbJS}%e}5f6SGL=+DJ z6&0!!X%PvPA}ACQu?C4q5Je<37!NyzB_x~OWM(qS&JP0Foz3pdGw;0f&O4i)N~J=S z6(i!Z04x9tzyh!UqO7=IVJekU{rrBQ46LhqKrWXv)&gR{Mc_hIhd>$_2NJ-Zr~!xp zM?J^KqXxhMwtJ3;fn<~b9IA2dU{nAM13PP+pQ^Hot_85)r!Ldo4xksv_>@oUu9I#6 z4lv+TzFD_3)D2)?fNL_{3SdjX-rXfaH~)A{I;10?=Y~tPFVpcd5(Vof5i8^D9g(L5Cdj} zXV$HLV60BrSkQb3aHEwW@DKPx=jxLTAMglx-hqwJ0`szbz(b%2oW%S@Y8ki-EXsC* zFNI^7X%Nr1Q@?;~K}^5dX>b*o4eDVv>oagEOyir41B$?9;bBeDB5(!xsWJ}mYF$h( ztH22nzqdmr;2yA~BM_>k1>hra-X{oBr95z51VwEPh4W(UEop50f*6K{R&NxSXo|Oi z55O4_7OT)-;0~~C>SE}Pm?g@J=J#2djaRiA+$7Pms;yPJWvKw5a^d830su!LfVAlV zhH$e#sx*S+Op$B=I<~nTlL0sy0HjR?FaS(w05BnQH7XN8GUWZrB5CmiH}e#x&XY3d zR#}<~xpch+d=tmZz_}0(GzqMj1|Siz*B=8sm13n14_V?S{<-ZD8yC+vcP-5 z@uZqS56}*D0-KTsKo;1o=5HkpfNo%=ny&`(NdjP-&$Bm^0>ElugU|CWLn|5UIt08YR`waglvAQCOeEn@8&V5u-UFJ$RkS_-rT{H|tr4L~8O7*Kxo?EMFtV&na?~Ow=gA7- zb>M0BY!Db)s3evN+Z%Ko=M7-Lp*3FuH>j`l0*BPR1PlPzM2oFC!ONnW#}uDG0z5Bh z*F*Ljb%ck&XJJ&o(lB@%IAz%AF5o0?eJ?8M06sPjKMtH;%;78L0Y%`f5zg9x4*F2HSYdD0HD9*O$+<|p~gLN2cSh#SpfyNl(mz>H0XDK$jZp%I_<{VM&&B>iP|C zf6((6hm`~E5D?C*g3E}~u)L)RV-91tQ|6Fbe}QL$4LXUS8q1L*LS#8;l<_vgW^jLDu8vBcrEi=>~#geY*ZBG z;3HM;I|Y1-+gj_xZL#}-UUvYVQ+0uh;^by5I@p3}(;nb8+!p$ZxB!q7_Vy=m33x

ht(u000CRNkl$_rOI+bHYn(4yE8-(9pW{6M`s}d7w|vb5pZkhIljLRR5}RCbw=zJxypbdP z2o@=IYmayN28B!yL3I)en$Ng^A;TcV#D#?#`wZ_z#KefRc!5vQ)s@i~0u5Nyl<8-> zt`T{~P`)hi5fF~mtLJTUl-x0a~WEd<`;ZhJRh71ctM0{V=%NZ3=B(!;l zwNmtjK`H9xbc`9Lbw-;nx$c`AstE|tWs@&>l{Z-Dc~&{e2^v!^4!F-gd)($Gw?pPd ze;vrv=oYttm{m@4if1^@GKPp}fj==sbhyhs?(u+q_hL=thGnJuN|dU3MC4-YgC@p| zX2zDMu?H<^@ILF9V#4vk@(9B#d_cp$KJ+J{p!tw%+~ik&;a@uRP$;@|T#->&mU$XM zXtK--FLRDd9P&8>-xAGV2f*+bD_kz!Q$UYSp`T@$X7OZ*Jq-AAz?*~#2!}w1LJ%VN z+9u5-^s*Qf3L&N&60(pbnWf8U5*qcX+_z!RhpjBo!TOgZAq0fZL_f!`)6fkm;$dsT zF5Xxkflf#xKLHi$Y77EABw&#qpg=2kRUh4uU1GQbvxQ&|3Mld%s0U$gIlxQ=U~V~} zoU8gFm{$@q>nI4)EsQ`K+;RxztmZ_Z55DE~ME~`i2()rBQ*6zBzeY%*YD-c_fi$c) zW9ZK}1bym`gS6Nu|4#x^5C}&ewlsuvm;#40gdpW`J_?Wo$8&-}KjefYEChja&SDc1 z0!5Fs*7>N#lF|zqx*-Z^GH#%0kOin9WHiv+APXs8-hKb;`C|JA6tA&VoP-DlJNC)7 z%C~X{T;o#|7Jl^j4?Cz7;3`{eu)#|lL!qWiu!Czyb?A zG5iP3%3pf4DLTUd001R)MObuXVRU6WV{&C-bY%cCFflPLFgPtTHB>P%Ix#ppG&L-(}k+&ba`tR=J&hURJGHO*Ls#6XI%Ya?io^a;cFzTu_IF)TPE-kYXk7nM?sU0&oWh!xf7~kT zE7($QJhS>|HH)j|!Tirsv-Mo<3@jCx6n`k4wrF_J#_9F3*sIxr!_Hq)nA1aN-H%J& zM?*vz9E`kX3d*nuF;CAu_efuOw!_iSayqA;Y@GUnZMR8Dmz7#$*ZaK*Qzdt;Votg7 znEep9LszXrhuxG2o(G$2(*61E#2N!W?M?9P)IV8pMDNAH;|c;z^PltbJ$U-h^0vh< zUE3seo`e3I+NQG#O}4-Cvf|V6?{?FexaxY!Qwl;~T3*TgebL{yal-e=Q#~tRaI?r~ zzEYdt$HcZ;r0L1#A7>JSesHij{nOtpu*egq&xl24o2ss#Ppa9UTiqJ^?t>qU-E0Y z!cMS$l74gXkjK25LXJO2IQLDqQCxlH!Yt+z4w(<3bE{eSir1*jRXdb+?`zTB--25z zTo~CK4FprH%I9U=UJr71~9SJK{0 zSnP8wT+x&@Z)HK_S($ZS$7K2~5BV-~RAH(WiHZvKk1EkUXnmJtv8E@5UpG>TF%Y7_#=Rd+=d)q}!E;B59V7z5H2iSy`@Md6}djxMRi? z$?L1zCAjVcEj-NXZ}{O?$~30q_c{&dvF&=i)8#^f```G4$?3jdPnw8+2;q43P)2ad zlCvy}Wvb(stZ}#?{ZN}dWrBxK&MoHMiYtFfag=2wBqnV>y8VZCX8gqvTV55j*;`(2 zIJnSHG~>Q7o7#`lY>Uj7@V!}PGKF*cFXN&`t8cCPFVeB=5zvLNUPEsv#jVMV;EJ?LWE=mPb3`PcqhPnn8x<;lUh9*|V23Ce8U}fi7AzZ TCsS>JiWody{an^LB{Ts56`H)} literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_newer_arrow_holo_light.png b/res/drawable-xhdpi/ic_newer_arrow_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..aba0e9b4af3aa01d7ff3069ff1695a271b3df795 GIT binary patch literal 1210 zcmV;r1V#IaP)ht(u000BRNklMyVv~I zn!Pqd4K>tI|8I>OKjpKjlCuC2{`qXKmdmQ$*Z)w;l%hld6Em8DAYhA~&R0l#z@{5u zyb7NxY_!&$_E;n~|G$<1L-4} zx=#4E>I0zLcBA>WSx|eQ#F&pe;oI%Me-8xAHfd=czL0pvX4zoj`v9m*FDSfVb=`QP z)N7s_boZT|O%=X+`s;1%DZM@J>h%3V!cPIvQ~3{@6eV@|Lq`2t8F8@i{dgde8}*V> zA}FN3b$=)P;F<~`C|qx=Im-GS@RJ98Ep69$n*s!-#H%irOHc|nIAGS`=Ialj6cjci z>9*Q?T;yc6uFV{9s?sxkxuVL#XM$A`2WJEzwciz%bgN*o3%pycTrn`h z837Jf2=Km}jMNB)WsW;gHRM4NrhFULN3g;?AG)F2h=T`hleIYm{agLL3kr!}to4Ib zAqaw3tgO#O1DSD3fQb%5>I>`qE+`~QbG_->&Med(p^psI2vOz}V@jn`BD&OD<|zi7 zFVlX0RGYDo`rHY()g>8S;zn=Fh4yCb3!tYSQU_dUnL;9w8*!O;svNXK=o6p^A+gtW z7R&3BT!N316GfTa;TgLjsS$$2WA;m>{`9B~O~Vgx zO*a9B(jV6P%8zchMb>;qZZe#nPNl>#YXnDT;D+QhIjHW3jyfV~HzN=9DyHq)$ literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_older_arrow_disabled_holo_light.png b/res/drawable-xhdpi/ic_older_arrow_disabled_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..6ef30a7b673eca26513c35492ac53a2d0ec71623 GIT binary patch literal 1054 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 literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_reply.png b/res/drawable-xhdpi/ic_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..01467a472be58a21f1218b995cba8a7afe62d3ac GIT binary patch literal 1996 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 literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_reply_all_holo_dark.png b/res/drawable-xhdpi/ic_reply_all_holo_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..4d1d4dd35ae0df60a7db3bd575829d570ae079f0 GIT binary patch literal 672 zcmV;R0$=@!P) zEZ-g)nrd%*&bdG5cD~HhnsL9MojEh-%#;9>{&OoS00p1`6o3Ly017|>C;$bZ0OD?e zpii$T00#IJr_T|9c@Y5k_!6~G9l%roEq4I=_%5z_rvdcvTU_(b0_b3|xaOS%&_=kz z`qV3e4vk zK%HoQ0-z!^pIZP_BU1oX)8|lNK9>Lvh(=}xR21{M15jm{_hK+l(Y`hFqFUu`=MeYImLV{WAi^};w6CfC;;||=3$Z`PMkVk z#5o0e%xp0W)g(t@P99LR>IG5=Vc)T1;!^jh&yS=nkL7b9(fnQlS=&aE+C z4D6LOuEsh7Gra@=GyuGrEOiwrUygPKX8JJ$*f6ocsq1my)vK{MA4vE`0w8@NGbj=O z=_9G2NC2eI+P-c?ktHi$3H4g zQ=R5_ab>WMV0BkVho9iaH<_Kbx`Kk$Z?9-hyAfQ{7@j4r9z=uB|)H)o@p^{vKBYmq9{_5pO_pkr8)*dV~>Yfyajw zz6FjuQn((>_;8Zv0O!R?oDFLKC-YrkTX*-q1~d0E#w`>4Z^x#lU1piVsOMpxe;`hZ z!SVrDaS^|cH{+8#<(IaUG?sEU)SmI>zcI0bHL6WnMAfdzw^?xBqBR1OX3KZZ@p{WF z)yDMYaNevXtL#s1QORzsI@12e&%$&S({{sW=e)z3x3)00S)bAue5UXHoq3gTRYT#b zgG>eDLJw?u`vO#1oNg3V+*`}o^vr1OyazkND(=)dOnC3B{$^dkCj%8dhkyOTYX584 z^Ufw6*?HjEjP~Bkf>P_;*bhh=q(3@2;lGK6W`pv58S}vJ|0kt4->Yp{!f@)q;@viW zXI~a@{_x;f@StqzvUha{xIW~){>zXrQk%VVD#Jay|5wu(*dtiva@am>+g24E!YJZ^ cf`X*uFQ0AMFgM1c6Bzdlp00i_>zopr0MN7Opa1{> literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_show_images_holo_light.png b/res/drawable-xhdpi/ic_show_images_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..571bc57a964defb5db46b1c6b5c774dd34f9966e GIT binary patch literal 794 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;rX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfwTh{zxw^#?3!dJj@1Ybrc8 zZ}v>}arh@K|DbV8S<;zZEX6s6#&0!diyl^;z>vPvcQUKk%1zEHYjs*04=B80%~OnA z<}W&b#`cmNjTtHmA69HSDW$rr;el4F$om&3er!AB#?O!7aSa)OlAm~0On34WWOmRZ=B{%46}v-b}djdumT-)){sKfEY) zQh$Lu)3itz!2pK~&Wwt7+E*&idhfgCp&&b-p;1Xq?#U7X7N5&Yju*24*Y`M(`Cs>E2gdmt*Du%JUF%@h$d_C9R7m4f?Elyt zy$>Dgi{%!(7EXwFxGi5tCL zWF!%L_}CGdH)_`cqnOlVs@o4gEST|8T;y!2LSy1wP!dutag8WRNi0dVN-jzTQVd20 zhK9NZ7P>~JA%@0QrUq7~Cb|aZRt5$+YP*Y2H00)|WTsWZH5i(MG#Hy$8CyU!XkI9J y4AdYAvLQG>t)x7$D3!r6B|j-u!8128JvAsbF{QHbWU38N5re0zpUXO@geCx9XFX*A literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_spam_normal_holo_light.png b/res/drawable-xhdpi/ic_spam_normal_holo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..4edbfe06d0b6313f19b3c1983ff1dbe54e4af77c GIT binary patch literal 399 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU<~tgaSW-r_4f8|-a`f=tq%`} zTBSMYOq{DBI9b7~?39p(aX`D8iHFC=TD@;akE?Pi{&$!26WE;^dEv7`i5-gq10#n6 z1JjENo@g3_6Y zU>lQ5=YN+4j4oaNx6*6{1REG&mJ2j6{E~5AdVX&owWJ^Ie?<=WCQK{UQdGKW*q zdS8j$1tzCJn`EUB;e{6SOoi7pI&`M{wbpk!bf@3v{=?KL^}f*fNvA{q_g&Io911$w p8|MBqTmGDp3Fs1#qrO + + + + + + + + + + + + + + + + + + + diff --git a/res/drawable/menu_item_newer.xml b/res/drawable/menu_item_newer.xml new file mode 100644 index 000000000..195c1be33 --- /dev/null +++ b/res/drawable/menu_item_newer.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/res/drawable/menu_item_older.xml b/res/drawable/menu_item_older.xml new file mode 100644 index 000000000..42dc10d47 --- /dev/null +++ b/res/drawable/menu_item_older.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/res/layout-land/message_view_header_actions.xml b/res/layout-land/message_view_header_actions.xml new file mode 100644 index 000000000..5c60d9c84 --- /dev/null +++ b/res/layout-land/message_view_header_actions.xml @@ -0,0 +1,43 @@ + + + + + + + + + + diff --git a/res/layout-sw600dp-land/account_setup_exchange.xml b/res/layout-sw600dp-land/account_setup_exchange.xml new file mode 100644 index 000000000..812e9bc60 --- /dev/null +++ b/res/layout-sw600dp-land/account_setup_exchange.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + +