1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-23 09:52:16 -05:00

Complete merge of DAmail functionality into K9mail. Following

features are added to K9mail:

1) Show unread message count on each folder
2) Sum unread count of all shown folders in an account to the account display
3) Periodically check selected folders for new mail, not just Inbox
4) Don't refresh folder when opened (unless folder is empty)
5) Show date and time of last sync for each folder
6) Fix timer for automatic periodic sync (use wakelock to assure completion)
7) Optimize local folder queries (speeds up account and folder lists)
8) Show Loading... message in status bar indicating which folder is being synced
9) Eliminate redundant sync of new messages (performance enhancement)
10) Improve notification text for multiple accounts
11) Do not automatically sync folders more often than the account-specific period
12) Use user-configured date and time formats
13) Select which folders are shown, using configurable Classes
14) Select which folders are synced, using configurable Classes
15) Added context (long press) menu to folders, to provide for Refresh
and Folder Settings
16) Status light flashes purple when there are unread messages
17) Folder list more quickly eliminates display of deleted and out-of-Class folders.
18) Delete works 
19) Mark all messages as read (in the folder context menu)
20) Notifications only for new unread messages
21) One minute synchronization frequency
22) Deleting an unread message decrements unread counter
23) Notifications work for POP3 accounts
24) Message deletes work for POP3 accounts
25) Explicit errors show in folder list
26) Stack traces saved to folder K9mail-errors
27) Clear pending actions (danger, for emergencies only!)
28) Delete policy in Account settings
29) DNS cache in InetAddress disabled
30) Trapped some crash-causing error conditions
31) Eliminate duplicate copies to Sent folder
32) Prevent crashes due to message listener concurrency
33) Empty Trash
34) Nuclear "Mark all messages as read" (marks all messages as read in
server-side folder, irrespective of which messages have been downloaded)
35) Forward (alternate) to allow forwarding email through other programs
36) Accept text/plain Intents to allow other programs to send email through K9mail
37) Displays Outbox sending status
38) Manual retry of outbox sending when "Refresh"ing Outbox
39) Folder error status is persisted
40) Ability to log to arbitrary file

Fixes K9 issues 11, 23, 24, 65, 69, 71, 79, 81, 82, 83, 87, 101, 104,
107, 120, 148, 154
This commit is contained in:
Daniel Applebaum 2008-12-31 03:49:09 +00:00
parent 72c4095ceb
commit 334d64141f
35 changed files with 3328 additions and 907 deletions

View File

@ -8,7 +8,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<permission android:name="com.android.email.permission.READ_ATTACHMENT" <permission android:name="com.android.email.permission.READ_ATTACHMENT"
android:permissionGroup="android.permission-group.MESSAGES" android:permissionGroup="android.permission-group.MESSAGES"
android:protectionLevel="dangerous" android:protectionLevel="dangerous"
@ -72,6 +72,12 @@
android:label="@string/account_settings_title_fmt" android:label="@string/account_settings_title_fmt"
> >
</activity> </activity>
<activity
android:name="com.android.email.activity.setup.FolderSettings"
android:label="@string/folder_settings_title"
>
</activity>
<activity <activity
android:name="com.android.email.activity.Debug" android:name="com.android.email.activity.Debug"
@ -102,6 +108,12 @@
<data android:mimeType="image/*" /> <data android:mimeType="image/*" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="text/*" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<data android:scheme="mailto" /> <data android:scheme="mailto" />
@ -110,7 +122,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<receiver android:name="com.android.email.service.BootReceiver" <receiver android:name="com.android.email.service.BootReceiver"
android:enabled="false" android:enabled="true"
> >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />

View File

@ -33,17 +33,17 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="0dip" android:layout_width="0dip"
android:layout_weight="1" /> android:layout_weight="1" />
<Button
android:id="@+id/reply_all"
android:text="@string/reply_all_action"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1" />
<Button <Button
android:id="@+id/delete" android:id="@+id/delete"
android:text="@string/delete_action" android:text="@string/delete_action"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="0dip" android:layout_width="0dip"
android:layout_weight="1" /> android:layout_weight="1" />
<Button
android:id="@+id/forward"
android:text="@string/forward_action"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -39,11 +39,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:text="@string/account_setup_options_notify_label" /> android:text="@string/account_setup_options_notify_label" />
<CheckBox
android:id="@+id/account_notify_ringtone"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:text="@string/account_setup_options_notify_ringtone_label" />
<View <View
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"

View File

@ -8,7 +8,7 @@
android:paddingLeft="36px" android:paddingLeft="36px"
android:paddingRight="4px"> android:paddingRight="4px">
<LinearLayout <LinearLayout
android:layout_width="fill_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:gravity="center_vertical"> android:gravity="center_vertical">
@ -26,11 +26,11 @@
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
</LinearLayout> </LinearLayout>
<View <View
android:layout_height="0dip" android:layout_height="fill_parent"
android:layout_width="0dip" android:layout_width="0px"
android:layout_weight="1" /> android:layout_weight="1" />
<TextView <TextView
android:id="@+id/new_message_count" android:id="@+id/folder_unread_message_count"
android:ellipsize="end" android:ellipsize="end"
android:singleLine="true" android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge" android:textAppearance="?android:attr/textAppearanceLarge"

View File

@ -2,8 +2,12 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/open" <item android:id="@+id/open"
android:title="@string/open_action" /> android:title="@string/open_action" />
<item android:id="@+id/empty_trash"
android:title="@string/empty_trash_action" />
<item android:id="@+id/edit_account" <item android:id="@+id/edit_account"
android:title="@string/account_settings_action" /> android:title="@string/account_settings_action" />
<item android:id="@+id/delete_account" <item android:id="@+id/delete_account"
android:title="@string/remove_account_action" /> android:title="@string/remove_account_action" />
<item android:id="@+id/clear_pending"
android:title="@string/clear_pending_action" />
</menu> </menu>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/mark_all_as_read"
android:title="@string/mark_all_as_read_action"
/>
<item
android:id="@+id/empty_trash"
android:title="@string/empty_trash_action"
/>
<item
android:id="@+id/refresh"
android:title="@string/refresh_action"
/>
<item
android:id="@+id/folder_settings"
android:title="@string/folder_settings_action"
/>
</menu>

View File

@ -24,4 +24,8 @@
android:id="@+id/mark_as_read" android:id="@+id/mark_as_read"
android:title="@string/mark_as_read_action" android:title="@string/mark_as_read_action"
/> />
<item
android:id="@+id/send_alternate"
android:title="@string/send_alternate_action"
/>
</menu> </menu>

View File

@ -12,6 +12,12 @@
android:title="@string/compose_action" android:title="@string/compose_action"
android:icon="@drawable/ic_menu_compose" android:icon="@drawable/ic_menu_compose"
/> />
<item
android:id="@+id/empty_trash"
android:alphabeticShortcut="e"
android:title="@string/empty_trash_action"
android:icon="@drawable/ic_menu_delete"
/>
<item <item
android:id="@+id/accounts" android:id="@+id/accounts"
android:title="@string/accounts_action" android:title="@string/accounts_action"

View File

@ -30,4 +30,10 @@
android:title="@string/delete_action" android:title="@string/delete_action"
android:icon="@drawable/ic_menu_delete" android:icon="@drawable/ic_menu_delete"
/> />
<item
android:id="@+id/send_alternate"
android:alphabeticShortcut="s"
android:title="@string/send_alternate_action"
android:icon="@drawable/ic_menu_forward_mail"
/>
</menu> </menu>

View File

@ -18,6 +18,7 @@
<string-array name="account_settings_check_frequency_entries"> <string-array name="account_settings_check_frequency_entries">
<item>@string/account_setup_options_mail_check_frequency_never</item> <item>@string/account_setup_options_mail_check_frequency_never</item>
<item>@string/account_setup_options_mail_check_frequency_1min</item>
<item>@string/account_setup_options_mail_check_frequency_5min</item> <item>@string/account_setup_options_mail_check_frequency_5min</item>
<item>@string/account_setup_options_mail_check_frequency_10min</item> <item>@string/account_setup_options_mail_check_frequency_10min</item>
<item>@string/account_setup_options_mail_check_frequency_15min</item> <item>@string/account_setup_options_mail_check_frequency_15min</item>
@ -27,6 +28,7 @@
<string-array name="account_settings_check_frequency_values"> <string-array name="account_settings_check_frequency_values">
<item>-1</item> <item>-1</item>
<item>1</item>
<item>5</item> <item>5</item>
<item>10</item> <item>10</item>
<item>15</item> <item>15</item>
@ -47,4 +49,69 @@
<item>50</item> <item>50</item>
<item>100</item> <item>100</item>
</string-array> </string-array>
<string-array name="account_settings_folder_display_mode_entries">
<item>@string/account_settings_folder_display_mode_all</item>
<item>@string/account_settings_folder_display_mode_first_class</item>
<item>@string/account_settings_folder_display_mode_first_and_second_class</item>
<item>@string/account_settings_folder_display_mode_not_second_class</item>
</string-array>
<string-array name="account_settings_folder_display_mode_values">
<item>ALL</item>
<item>FIRST_CLASS</item>
<item>FIRST_AND_SECOND_CLASS</item>
<item>NOT_SECOND_CLASS</item>
</string-array>
<string-array name="account_settings_folder_sync_mode_entries">
<item>@string/account_settings_folder_sync_mode_all</item>
<item>@string/account_settings_folder_sync_mode_first_class</item>
<item>@string/account_settings_folder_sync_mode_first_and_second_class</item>
<item>@string/account_settings_folder_sync_mode_not_second_class</item>
</string-array>
<string-array name="account_settings_folder_sync_mode_values">
<item>ALL</item>
<item>FIRST_CLASS</item>
<item>FIRST_AND_SECOND_CLASS</item>
<item>NOT_SECOND_CLASS</item>
</string-array>
<string-array name="folder_settings_folder_display_mode_entries">
<item>@string/folder_settings_folder_display_mode_normal</item>
<item>@string/folder_settings_folder_display_mode_first_class</item>
<item>@string/folder_settings_folder_display_mode_second_class</item>
</string-array>
<string-array name="folder_settings_folder_display_mode_values">
<item>NONE</item>
<item>FIRST_CLASS</item>
<item>SECOND_CLASS</item>
</string-array>
<string-array name="folder_settings_folder_sync_mode_entries">
<item>@string/folder_settings_folder_sync_mode_normal</item>
<item>@string/folder_settings_folder_sync_mode_first_class</item>
<item>@string/folder_settings_folder_sync_mode_second_class</item>
</string-array>
<string-array name="folder_settings_folder_sync_mode_values">
<item>NONE</item>
<item>FIRST_CLASS</item>
<item>SECOND_CLASS</item>
</string-array>
<string-array name="account_setup_delete_policy_entries">
<item>@string/account_setup_incoming_delete_policy_never_label</item>
<item>@string/account_setup_incoming_delete_policy_delete_label</item>
<item>@string/account_setup_incoming_delete_policy_markread_label</item>
</string-array>
<string-array name="account_setup_delete_policy_values">
<item>0</item>
<item>2</item>
<item>3</item>
</string-array>
</resources> </resources>

View File

@ -31,16 +31,23 @@
<string name="save_draft_action">Save as draft</string> <string name="save_draft_action">Save as draft</string>
<string name="retry_action">Retry</string> <string name="retry_action">Retry</string>
<string name="refresh_action">Refresh</string> <string name="refresh_action">Refresh</string>
<string name="mark_all_as_read_action">Mark all messages as read</string>
<string name="add_account_action">Add account</string> <string name="add_account_action">Add account</string>
<string name="compose_action">Compose</string> <string name="compose_action">Compose</string>
<string name="search_action">Search</string> <string name="search_action">Search</string>
<string name="preferences_action">Preferences</string> <string name="preferences_action">Preferences</string>
<string name="open_action">Open</string> <string name="open_action">Open</string>
<string name="account_settings_action">Account settings</string> <string name="account_settings_action">Account settings</string>
<string name="folder_settings_action">Folder settings</string>
<string name="remove_account_action">Remove account</string> <string name="remove_account_action">Remove account</string>
<string name="clear_pending_action">Clear pending actions (danger!)</string>
<string name="accounts_action">Accounts</string> <string name="accounts_action">Accounts</string>
<string name="read_action">Read</string> <string name="read_action">Read</string>
<string name="mark_as_read_action">Mark as read</string> <string name="mark_as_read_action">Mark as read</string>
<string name="send_alternate_action">Forward (alternate)</string>
<string name="send_alternate_chooser_title">Choose sender</string>
<string name="mark_as_unread_action">Mark as unread</string> <string name="mark_as_unread_action">Mark as unread</string>
<string name="move_to_action">Move to</string> <string name="move_to_action">Move to</string>
<string name="folders_action">Folders</string> <string name="folders_action">Folders</string>
@ -53,6 +60,7 @@
<string name="about_action">About</string> <string name="about_action">About</string>
<string name="accounts_context_menu_title">Account options</string> <string name="accounts_context_menu_title">Account options</string>
<string name="folder_context_menu_title">Folder options</string>
<string name="general_no_subject">(No subject)</string> <!-- Shown in place of the subject when a message has no subject. Showing this in parentheses is customary. --> <string name="general_no_subject">(No subject)</string> <!-- Shown in place of the subject when a message has no subject. Showing this in parentheses is customary. -->
@ -69,8 +77,11 @@
<string name="notification_new_multi_account_fmt">in <xliff:g id="number_accounts">%d</xliff:g> accounts</string> <string name="notification_new_multi_account_fmt">in <xliff:g id="number_accounts">%d</xliff:g> accounts</string>
<string name="notification_unsent_title">Message not sent</string> <string name="notification_unsent_title">Message not sent</string>
<string name="notification_bg_sync_ticker">Checking email: <xliff:g id="account">%s</xliff:g></string> <string name="notification_bg_sync_ticker">Checking email: <xliff:g id="account">%s</xliff:g>:<xliff:g id="folder">%s</xliff:g></string>
<string name="notification_bg_sync_title">Checking email</string> <string name="notification_bg_sync_title">Checking email</string>
<string name="notification_bg_send_ticker">Sending email: <xliff:g id="account">%s</xliff:g></string>
<string name="notification_bg_send_title">Sending email</string>
<string name="notification_bg_title_separator">:</string>
<string name="special_mailbox_name_inbox">Inbox</string> <string name="special_mailbox_name_inbox">Inbox</string>
<string name="special_mailbox_name_outbox">Outbox</string> <string name="special_mailbox_name_outbox">Outbox</string>
@ -78,6 +89,8 @@
<string name="special_mailbox_name_drafts">Drafts</string> <string name="special_mailbox_name_drafts">Drafts</string>
<string name="special_mailbox_name_trash">Trash</string> <string name="special_mailbox_name_trash">Trash</string>
<string name="special_mailbox_name_sent">Sent</string> <string name="special_mailbox_name_sent">Sent</string>
<string name="end_of_folder">No more messages</string>
<string name="accounts_welcome"> <string name="accounts_welcome">
Welcome to K-9 Mail setup. K-9 is an open source email client for Android based on the standard Android Mail client. Welcome to K-9 Mail setup. K-9 is an open source email client for Android based on the standard Android Mail client.
@ -86,6 +99,8 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
* Better performance * Better performance
* Email signatures * Email signatures
* Bcc-to-self * Bcc-to-self
* Folder subscriptions
* All folder synchronization
* Return-address configuration * Return-address configuration
* Keyboard shortcuts * Keyboard shortcuts
* Better IMAP support * Better IMAP support
@ -142,6 +157,7 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
<string name="message_deleted_toast">Message deleted.</string> <string name="message_deleted_toast">Message deleted.</string>
<string name="message_discarded_toast">Message discarded.</string> <string name="message_discarded_toast">Message discarded.</string>
<string name="message_saved_toast">Message saved as draft.</string> <string name="message_saved_toast">Message saved as draft.</string>
<string name="message_delete_failed">Message could not be deleted.</string>
<string name="about_header">About <xliff:g id="app_name">%s</xliff:g></string> <string name="about_header">About <xliff:g id="app_name">%s</xliff:g></string>
<string name="about_version">Version: <xliff:g id="version">%s</xliff:g></string> <string name="about_version">Version: <xliff:g id="version">%s</xliff:g></string>
@ -189,10 +205,13 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
<string name="account_setup_incoming_security_ssl_label">SSL (always)</string> <string name="account_setup_incoming_security_ssl_label">SSL (always)</string>
<string name="account_setup_incoming_security_tls_optional_label">TLS (if available)</string> <string name="account_setup_incoming_security_tls_optional_label">TLS (if available)</string>
<string name="account_setup_incoming_security_tls_label">TLS (always)</string> <string name="account_setup_incoming_security_tls_label">TLS (always)</string>
<string name="account_setup_incoming_delete_policy_label">Delete email from server:</string>
<string name="account_setup_incoming_delete_policy_never_label">Never</string> <string name="account_setup_incoming_delete_policy_label">When I delete a message</string>
<string name="account_setup_incoming_delete_policy_never_label">Do not delete on server</string>
<string name="account_setup_incoming_delete_policy_7days_label">After 7 days</string> <string name="account_setup_incoming_delete_policy_7days_label">After 7 days</string>
<string name="account_setup_incoming_delete_policy_delete_label">When I delete from Inbox</string> <string name="account_setup_incoming_delete_policy_delete_label">Delete from server</string>
<string name="account_setup_incoming_delete_policy_markread_label">Mark as read on server</string>
<string name="account_setup_incoming_imap_path_prefix_label">IMAP path prefix</string> <string name="account_setup_incoming_imap_path_prefix_label">IMAP path prefix</string>
<string name="account_setup_incoming_imap_path_prefix_hint">Optional</string> <string name="account_setup_incoming_imap_path_prefix_hint">Optional</string>
<string name="account_setup_incoming_webdav_path_prefix_label">WebDav(Exchange) path prefix</string> <string name="account_setup_incoming_webdav_path_prefix_label">WebDav(Exchange) path prefix</string>
@ -219,9 +238,11 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
<string name="account_setup_outgoing_authentication_webdav_before_smtp_label">WebDav(Exchange) before SMTP</string> <string name="account_setup_outgoing_authentication_webdav_before_smtp_label">WebDav(Exchange) before SMTP</string>
<string name="account_setup_options_title">Account options</string> <string name="account_setup_options_title">Account options</string>
<string name="account_setup_options_mail_check_frequency_label">Email checking frequency</string> <string name="account_setup_options_mail_check_frequency_label">Email checking frequency</string>
<!-- Frequency also used in account_settings_* --> <!-- Frequency also used in account_settings_* -->
<string name="account_setup_options_mail_check_frequency_never">Never</string> <string name="account_setup_options_mail_check_frequency_never">Never</string>
<string name="account_setup_options_mail_check_frequency_1min">Every minute</string>
<string name="account_setup_options_mail_check_frequency_5min">Every 5 minutes</string> <string name="account_setup_options_mail_check_frequency_5min">Every 5 minutes</string>
<string name="account_setup_options_mail_check_frequency_10min">Every 10 minutes</string> <string name="account_setup_options_mail_check_frequency_10min">Every 10 minutes</string>
<string name="account_setup_options_mail_check_frequency_15min">Every 15 minutes</string> <string name="account_setup_options_mail_check_frequency_15min">Every 15 minutes</string>
@ -229,7 +250,6 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
<string name="account_setup_options_mail_check_frequency_1hour">Every hour</string> <string name="account_setup_options_mail_check_frequency_1hour">Every hour</string>
<string name="account_setup_options_default_label">Send email from this account by default.</string> <string name="account_setup_options_default_label">Send email from this account by default.</string>
<string name="account_setup_options_notify_label">Notify me when email arrives.</string> <string name="account_setup_options_notify_label">Notify me when email arrives.</string>
<string name="account_setup_options_notify_ringtone_label">Play a sound when email arrives.</string>
<!-- Number of displayed messages, also used in account_settings_* --> <!-- Number of displayed messages, also used in account_settings_* -->
<string name="account_setup_options_mail_display_count_label">Number of emails to display</string> <string name="account_setup_options_mail_display_count_label">Number of emails to display</string>
@ -248,14 +268,41 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
<string name="account_settings_default">Default account</string> <string name="account_settings_default">Default account</string>
<string name="account_settings_default_label">Default account</string> <string name="account_settings_default_label">Default account</string>
<string name="account_settings_default_summary">Send email from this account by default</string> <string name="account_settings_default_summary">Send email from this account by default</string>
<string name="account_settings_email_label">Your email address</string>
<string name="account_settings_notify_label">Email notifications</string> <string name="account_settings_notify_label">Email notifications</string>
<string name="account_settings_email_label">Your email address</string>
<string name="account_settings_notify_summary">Notify in status bar when email arrives</string> <string name="account_settings_notify_summary">Notify in status bar when email arrives</string>
<string name="account_settings_notify_ringtone_label">Ringtone notifications</string>
<string name="account_settings_notify_ringtone_summary">Play a sound when email arrives</string>
<string name="account_settings_show_combined_label">Show combined Inbox</string> <string name="account_settings_show_combined_label">Show combined Inbox</string>
<string name="account_settings_display_sync">Display and synchronization</string>
<string name="account_settings_mail_check_frequency_label">Email check frequency</string> <string name="account_settings_mail_check_frequency_label">Email check frequency</string>
<string name="account_settings_second_class_check_frequency_label">2nd class check frequency</string>
<string name="account_settings_mail_display_count_label">Number of emails to display</string> <string name="account_settings_mail_display_count_label">Number of emails to display</string>
<string name="account_settings_folder_display_mode_label">Folder display mode</string>
<string name="account_settings_folder_display_mode_all">All</string>
<string name="account_settings_folder_display_mode_first_class">Only 1st Class folders</string>
<string name="account_settings_folder_display_mode_first_and_second_class">1st and 2nd Class folders</string>
<string name="account_settings_folder_display_mode_not_second_class">All except 2nd Class folders</string>
<string name="account_settings_folder_sync_mode_label">Folder sync mode</string>
<string name="account_settings_folder_sync_mode_all">All</string>
<string name="account_settings_folder_sync_mode_first_class">Only 1st Class folders</string>
<string name="account_settings_folder_sync_mode_first_and_second_class">1st and 2nd Class folders</string>
<string name="account_settings_folder_sync_mode_not_second_class">All except 2nd Class folders</string>
<string name="folder_settings_title">Folder settings</string>
<string name="folder_settings_folder_display_mode_label">Folder display class</string>
<string name="folder_settings_folder_display_mode_normal">None</string>
<string name="folder_settings_folder_display_mode_first_class">1st Class</string>
<string name="folder_settings_folder_display_mode_second_class">2nd Class</string>
<string name="folder_settings_folder_sync_mode_label">Folder sync class</string>
<string name="folder_settings_folder_sync_mode_normal">Same as display class</string>
<string name="folder_settings_folder_sync_mode_first_class">1st Class</string>
<string name="folder_settings_folder_sync_mode_second_class">2nd Class</string>
<string name="account_settings_incoming_label">Incoming settings</string> <string name="account_settings_incoming_label">Incoming settings</string>
<string name="account_settings_incoming_summary">Configure the incoming email server</string> <string name="account_settings_incoming_summary">Configure the incoming email server</string>
<string name="account_settings_outgoing_label">Outgoing settings</string> <string name="account_settings_outgoing_label">Outgoing settings</string>
@ -295,7 +342,9 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
your correct email address and password, you may not have a paid your correct email address and password, you may not have a paid
\"Plus\" account. Please launch the Web browser to gain access to \"Plus\" account. Please launch the Web browser to gain access to
these mail accounts.</string> these mail accounts.</string>
<string name="account_setup_failed_dlg_invalid_certificate_title">Unrecognized Certificate</string> <string name="account_setup_failed_dlg_invalid_certificate_title">Unrecognized Certificate</string>
<string name="account_setup_failed_dlg_invalid_certificate_accept">Accept Key</string> <string name="account_setup_failed_dlg_invalid_certificate_accept">Accept Key</string>
<string name="account_setup_failed_dlg_invalid_certificate_reject">Reject Key</string> <string name="account_setup_failed_dlg_invalid_certificate_reject">Reject Key</string>
</resources> </resources>

View File

@ -26,12 +26,10 @@
android:summary="" android:summary=""
android:dialogTitle="@string/account_settings_description_label" /> android:dialogTitle="@string/account_settings_description_label" />
<ListPreference <CheckBoxPreference
android:key="account_check_frequency" android:key="account_default"
android:title="@string/account_settings_mail_check_frequency_label" android:title="@string/account_settings_default_label"
android:entries="@array/account_settings_check_frequency_entries" android:summary="@string/account_settings_default_summary" />
android:entryValues="@array/account_settings_check_frequency_values"
android:dialogTitle="@string/account_settings_mail_check_frequency_label" />
<ListPreference <ListPreference
android:key="account_display_count" android:key="account_display_count"
@ -40,6 +38,40 @@
android:entryValues="@array/account_settings_display_count_values" android:entryValues="@array/account_settings_display_count_values"
android:dialogTitle="@string/account_settings_mail_display_count_label" /> android:dialogTitle="@string/account_settings_mail_display_count_label" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/account_settings_display_sync">
<ListPreference
android:key="account_check_frequency"
android:title="@string/account_settings_mail_check_frequency_label"
android:entries="@array/account_settings_check_frequency_entries"
android:entryValues="@array/account_settings_check_frequency_values"
android:dialogTitle="@string/account_settings_mail_check_frequency_label" />
<ListPreference
android:key="folder_display_mode"
android:title="@string/account_settings_folder_display_mode_label"
android:entries="@array/account_settings_folder_display_mode_entries"
android:entryValues="@array/account_settings_folder_display_mode_values"
android:dialogTitle="@string/account_settings_folder_display_mode_label" />
<ListPreference
android:key="folder_sync_mode"
android:title="@string/account_settings_folder_sync_mode_label"
android:entries="@array/account_settings_folder_sync_mode_entries"
android:entryValues="@array/account_settings_folder_sync_mode_values"
android:dialogTitle="@string/account_settings_folder_sync_mode_label" />
<ListPreference
android:key="delete_policy"
android:title="@string/account_setup_incoming_delete_policy_label"
android:entries="@array/account_setup_delete_policy_entries"
android:entryValues="@array/account_setup_delete_policy_values"
android:dialogTitle="@string/account_setup_incoming_delete_policy_label" />
<PreferenceScreen <PreferenceScreen
android:key="composition" android:key="composition"
android:title="@string/account_settings_composition_label" /> android:title="@string/account_settings_composition_label" />
@ -54,11 +86,6 @@
android:title="@string/account_settings_notify_label" android:title="@string/account_settings_notify_label"
android:defaultValue="true" android:defaultValue="true"
android:summary="@string/account_settings_notify_summary" /> android:summary="@string/account_settings_notify_summary" />
<CheckBoxPreference
android:key="account_notify_ringtone"
android:title="@string/account_settings_notify_ringtone_label"
android:defaultValue="true"
android:summary="@string/account_settings_notify_ringtone_summary" />
<RingtonePreference <RingtonePreference
android:layout="?android:attr/preferenceLayoutChild" android:layout="?android:attr/preferenceLayoutChild"
@ -89,13 +116,6 @@
android:title="@string/account_settings_outgoing_label" /> android:title="@string/account_settings_outgoing_label" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/account_settings_default">
<CheckBoxPreference
android:key="account_default"
android:title="@string/account_settings_default_label"
android:summary="@string/account_settings_default_summary" />
</PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="folder_settings">
<ListPreference
android:key="folder_settings_folder_display_mode"
android:title="@string/folder_settings_folder_display_mode_label"
android:entries="@array/folder_settings_folder_display_mode_entries"
android:entryValues="@array/folder_settings_folder_display_mode_values"
android:dialogTitle="@string/folder_settings_folder_display_mode_label" />
<ListPreference
android:key="folder_settings_folder_sync_mode"
android:title="@string/folder_settings_folder_sync_mode_label"
android:entries="@array/folder_settings_folder_sync_mode_entries"
android:entryValues="@array/folder_settings_folder_sync_mode_values"
android:dialogTitle="@string/folder_settings_folder_sync_mode_label" />
</PreferenceCategory>
</PreferenceScreen>

View File

@ -5,9 +5,17 @@ import java.io.Serializable;
import java.util.Arrays; import java.util.Arrays;
import java.util.UUID; import java.util.UUID;
import com.android.email.mail.Folder;
import com.android.email.mail.MessagingException;
import com.android.email.mail.Store;
import com.android.email.mail.store.LocalStore;
import com.android.email.mail.store.LocalStore.LocalFolder;
import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
import android.util.Log;
/** /**
* Account stores all of the settings for a single account defined by the user. It is able to save * Account stores all of the settings for a single account defined by the user. It is able to save
@ -17,6 +25,7 @@ public class Account implements Serializable {
public static final int DELETE_POLICY_NEVER = 0; public static final int DELETE_POLICY_NEVER = 0;
public static final int DELETE_POLICY_7DAYS = 1; public static final int DELETE_POLICY_7DAYS = 1;
public static final int DELETE_POLICY_ON_DELETE = 2; public static final int DELETE_POLICY_ON_DELETE = 2;
public static final int DELETE_POLICY_MARK_AS_READ = 3;
private static final long serialVersionUID = 2975156672298625121L; private static final long serialVersionUID = 2975156672298625121L;
@ -33,14 +42,19 @@ public class Account implements Serializable {
int mDisplayCount; int mDisplayCount;
long mLastAutomaticCheckTime; long mLastAutomaticCheckTime;
boolean mNotifyNewMail; boolean mNotifyNewMail;
boolean mNotifyRingtone;
String mDraftsFolderName; String mDraftsFolderName;
String mSentFolderName; String mSentFolderName;
String mTrashFolderName; String mTrashFolderName;
String mOutboxFolderName; String mOutboxFolderName;
FolderMode mFolderDisplayMode;
FolderMode mFolderSyncMode;
int mAccountNumber; int mAccountNumber;
boolean mVibrate; boolean mVibrate;
String mRingtoneUri; String mRingtoneUri;
public enum FolderMode {
ALL, FIRST_CLASS, FIRST_AND_SECOND_CLASS, NOT_SECOND_CLASS;
}
/** /**
* <pre> * <pre>
@ -59,9 +73,10 @@ public class Account implements Serializable {
mDisplayCount = -1; mDisplayCount = -1;
mAccountNumber = -1; mAccountNumber = -1;
mNotifyNewMail = true; mNotifyNewMail = true;
mNotifyRingtone = false;
mSignature = "Sent from my Android phone with K-9. Please excuse my brevity."; mSignature = "Sent from my Android phone with K-9. Please excuse my brevity.";
mVibrate = false; mVibrate = false;
mFolderDisplayMode = FolderMode.ALL;
mFolderSyncMode = FolderMode.ALL;
mRingtoneUri = "content://settings/system/notification_sound"; mRingtoneUri = "content://settings/system/notification_sound";
} }
@ -91,8 +106,6 @@ public class Account implements Serializable {
+ ".lastAutomaticCheckTime", 0); + ".lastAutomaticCheckTime", 0);
mNotifyNewMail = preferences.mSharedPreferences.getBoolean(mUuid + ".notifyNewMail", mNotifyNewMail = preferences.mSharedPreferences.getBoolean(mUuid + ".notifyNewMail",
false); false);
mNotifyRingtone = preferences.mSharedPreferences.getBoolean(mUuid + ".notifyRingtone",
false);
mDeletePolicy = preferences.mSharedPreferences.getInt(mUuid + ".deletePolicy", 0); mDeletePolicy = preferences.mSharedPreferences.getInt(mUuid + ".deletePolicy", 0);
mDraftsFolderName = preferences.mSharedPreferences.getString(mUuid + ".draftsFolderName", mDraftsFolderName = preferences.mSharedPreferences.getString(mUuid + ".draftsFolderName",
"Drafts"); "Drafts");
@ -106,6 +119,26 @@ public class Account implements Serializable {
mVibrate = preferences.mSharedPreferences.getBoolean(mUuid + ".vibrate", false); mVibrate = preferences.mSharedPreferences.getBoolean(mUuid + ".vibrate", false);
mRingtoneUri = preferences.mSharedPreferences.getString(mUuid + ".ringtone", mRingtoneUri = preferences.mSharedPreferences.getString(mUuid + ".ringtone",
"content://settings/system/notification_sound"); "content://settings/system/notification_sound");
try
{
mFolderDisplayMode = FolderMode.valueOf(preferences.mSharedPreferences.getString(mUuid + ".folderDisplayMode",
FolderMode.NOT_SECOND_CLASS.name()));
}
catch (Exception e)
{
mFolderDisplayMode = FolderMode.ALL;
}
try
{
mFolderSyncMode = FolderMode.valueOf(preferences.mSharedPreferences.getString(mUuid + ".folderSyncMode",
FolderMode.FIRST_CLASS.name()));
}
catch (Exception e)
{
mFolderSyncMode = FolderMode.ALL;
}
} }
public String getUuid() { public String getUuid() {
@ -218,6 +251,9 @@ public class Account implements Serializable {
editor.remove(mUuid + ".accountNumber"); editor.remove(mUuid + ".accountNumber");
editor.remove(mUuid + ".vibrate"); editor.remove(mUuid + ".vibrate");
editor.remove(mUuid + ".ringtone"); editor.remove(mUuid + ".ringtone");
editor.remove(mUuid + ".lastFullSync");
editor.remove(mUuid + ".folderDisplayMode");
editor.remove(mUuid + ".folderSyncMode");
editor.commit(); editor.commit();
} }
@ -269,7 +305,6 @@ public class Account implements Serializable {
editor.putInt(mUuid + ".displayCount", mDisplayCount); editor.putInt(mUuid + ".displayCount", mDisplayCount);
editor.putLong(mUuid + ".lastAutomaticCheckTime", mLastAutomaticCheckTime); editor.putLong(mUuid + ".lastAutomaticCheckTime", mLastAutomaticCheckTime);
editor.putBoolean(mUuid + ".notifyNewMail", mNotifyNewMail); editor.putBoolean(mUuid + ".notifyNewMail", mNotifyNewMail);
editor.putBoolean(mUuid + ".notifyRingtone", mNotifyRingtone);
editor.putInt(mUuid + ".deletePolicy", mDeletePolicy); editor.putInt(mUuid + ".deletePolicy", mDeletePolicy);
editor.putString(mUuid + ".draftsFolderName", mDraftsFolderName); editor.putString(mUuid + ".draftsFolderName", mDraftsFolderName);
editor.putString(mUuid + ".sentFolderName", mSentFolderName); editor.putString(mUuid + ".sentFolderName", mSentFolderName);
@ -278,6 +313,9 @@ public class Account implements Serializable {
editor.putInt(mUuid + ".accountNumber", mAccountNumber); editor.putInt(mUuid + ".accountNumber", mAccountNumber);
editor.putBoolean(mUuid + ".vibrate", mVibrate); editor.putBoolean(mUuid + ".vibrate", mVibrate);
editor.putString(mUuid + ".ringtone", mRingtoneUri); editor.putString(mUuid + ".ringtone", mRingtoneUri);
editor.putString(mUuid + ".folderDisplayMode", mFolderDisplayMode.name());
editor.putString(mUuid + ".folderSyncMode", mFolderSyncMode.name());
editor.commit(); editor.commit();
} }
@ -303,6 +341,49 @@ public class Account implements Serializable {
public int getAutomaticCheckIntervalMinutes() { public int getAutomaticCheckIntervalMinutes() {
return mAutomaticCheckIntervalMinutes; return mAutomaticCheckIntervalMinutes;
} }
public int getUnreadMessageCount(Context context, Application application) throws MessagingException
{
int unreadMessageCount = 0;
LocalStore localStore = (LocalStore) Store.getInstance(
getLocalStoreUri(),
application);
Account.FolderMode aMode = getFolderDisplayMode();
Preferences prefs = Preferences.getPreferences(context);
for (LocalFolder folder : localStore.getPersonalNamespaces())
{
folder.refresh(prefs);
Folder.FolderClass fMode = folder.getDisplayClass();
if (folder.getName().equals(getTrashFolderName()) == false &&
folder.getName().equals(getDraftsFolderName()) == false &&
folder.getName().equals(getOutboxFolderName()) == false &&
folder.getName().equals(getSentFolderName()) == false &&
folder.getName().equals(getErrorFolderName()) == false)
{
if (aMode == Account.FolderMode.FIRST_CLASS &&
fMode != Folder.FolderClass.FIRST_CLASS)
{
continue;
}
if (aMode == Account.FolderMode.FIRST_AND_SECOND_CLASS &&
fMode != Folder.FolderClass.FIRST_CLASS &&
fMode != Folder.FolderClass.SECOND_CLASS)
{
continue;
}
if (aMode == Account.FolderMode.NOT_SECOND_CLASS &&
fMode == Folder.FolderClass.SECOND_CLASS)
{
continue;
}
unreadMessageCount += folder.getUnreadMessageCount();
}
}
return unreadMessageCount;
}
public int getDisplayCount() { public int getDisplayCount() {
if (mDisplayCount == -1) { if (mDisplayCount == -1) {
@ -337,15 +418,6 @@ public class Account implements Serializable {
this.mLastAutomaticCheckTime = lastAutomaticCheckTime; this.mLastAutomaticCheckTime = lastAutomaticCheckTime;
} }
public boolean isNotifyRingtone() {
return mNotifyRingtone;
}
public void setNotifyRingtone(boolean notifyRingtone) {
this.mNotifyRingtone = notifyRingtone;
}
public boolean isNotifyNewMail() { public boolean isNotifyNewMail() {
return mNotifyNewMail; return mNotifyNewMail;
} }
@ -373,6 +445,11 @@ public class Account implements Serializable {
public String getSentFolderName() { public String getSentFolderName() {
return mSentFolderName; return mSentFolderName;
} }
public String getErrorFolderName()
{
return Email.ERROR_FOLDER_NAME;
}
public void setSentFolderName(String sentFolderName) { public void setSentFolderName(String sentFolderName) {
mSentFolderName = sentFolderName; mSentFolderName = sentFolderName;
@ -405,4 +482,25 @@ public class Account implements Serializable {
} }
return super.equals(o); return super.equals(o);
} }
public FolderMode getFolderDisplayMode()
{
return mFolderDisplayMode;
}
public void setFolderDisplayMode(FolderMode displayMode)
{
mFolderDisplayMode = displayMode;
}
public FolderMode getFolderSyncMode()
{
return mFolderSyncMode;
}
public void setFolderSyncMode(FolderMode syncMode)
{
mFolderSyncMode = syncMode;
}
} }

View File

@ -20,6 +20,14 @@ public class Email extends Application {
public static Application app = null; public static Application app = null;
public static File tempDirectory; public static File tempDirectory;
public static final String LOG_TAG = "k9"; public static final String LOG_TAG = "k9";
/**
* Some log messages can be sent to a file, so that the logs
* can be read using unprivileged access (eg. Terminal Emulator)
* on the phone, without adb. Set to null to disable
*/
public static final String logFile = null;
//public static final String logFile = "/sdcard/k9mail/debug.log";
/** /**
* If this is enabled there will be additional logging information sent to * If this is enabled there will be additional logging information sent to
@ -33,6 +41,12 @@ public class Email extends Application {
*/ */
public static boolean DEBUG_SENSITIVE = false; public static boolean DEBUG_SENSITIVE = false;
/**
* Can create messages containing stack traces that can be forwarded
* to the development team.
*/
public static boolean ENABLE_ERROR_FOLDER = true;
public static String ERROR_FOLDER_NAME = "K9mail-errors";
/** /**
* The MIME type(s) of attachments we're willing to send. At the moment it is not possible * The MIME type(s) of attachments we're willing to send. At the moment it is not possible
@ -41,7 +55,7 @@ public class Email extends Application {
* with Intent.ACTION_SEND. * with Intent.ACTION_SEND.
*/ */
public static final String[] ACCEPTABLE_ATTACHMENT_SEND_TYPES = new String[] { public static final String[] ACCEPTABLE_ATTACHMENT_SEND_TYPES = new String[] {
"*/*", "*/*"
}; };
/** /**
@ -103,7 +117,7 @@ public class Email extends Application {
/** /**
* Max time (in millis) the wake lock will be held for when background sync is happening * Max time (in millis) the wake lock will be held for when background sync is happening
*/ */
public static final int WAKE_LOCK_TIMEOUT = 30000; public static final int WAKE_LOCK_TIMEOUT = 600000;
/** /**
* LED color used for the new email notitication * LED color used for the new email notitication
@ -120,8 +134,10 @@ public class Email extends Application {
*/ */
public static final int NOTIFICATION_LED_OFF_TIME = 2000; public static final int NOTIFICATION_LED_OFF_TIME = 2000;
public static final int NEW_EMAIL_NOTIFICATION_ID = 1; // Must not conflict with an account number
public static final int FETCHING_EMAIL_NOTIFICATION_ID = 2; public static final int FETCHING_EMAIL_NOTIFICATION_ID = -4;
public static final int FETCHING_EMAIL_NOTIFICATION_MULTI_ACCOUNT_ID = -1;
public static final int FETCHING_EMAIL_NOTIFICATION_NO_ACCOUNT = -2;
/** /**
* Called throughout the application when the number of accounts has changed. This method * Called throughout the application when the number of accounts has changed. This method

File diff suppressed because it is too large Load Diff

View File

@ -79,16 +79,26 @@ public class MessagingListener {
public void checkMailFinished(Context context, Account account) { public void checkMailFinished(Context context, Account account) {
} }
public void checkMailFailed(Context context, Account account, String reason) { public void checkMailFailed(Context context, Account account, String reason) {
} }
public void sendPendingMessagesStarted(Account account) {
}
public void sendPendingMessagesCompleted(Account account) { public void sendPendingMessagesCompleted(Account account) {
} }
public void sendPendingMessagesFailed(Account account) {
}
public void emptyTrashCompleted(Account account) { public void emptyTrashCompleted(Account account) {
} }
public void folderStatusChanged(Account account, String folderName) {
}
public void messageUidChanged(Account account, String folder, String oldUid, String newUid) { public void messageUidChanged(Account account, String folder, String oldUid, String newUid) {
} }

View File

@ -12,7 +12,7 @@ import android.util.Log;
public class Preferences { public class Preferences {
private static Preferences preferences; private static Preferences preferences;
SharedPreferences mSharedPreferences; public SharedPreferences mSharedPreferences;
private Preferences(Context context) { private Preferences(Context context) {
mSharedPreferences = context.getSharedPreferences("AndroidMail.Main", Context.MODE_PRIVATE); mSharedPreferences = context.getSharedPreferences("AndroidMail.Main", Context.MODE_PRIVATE);

View File

@ -37,6 +37,7 @@ import com.android.email.R;
import com.android.email.activity.setup.AccountSettings; import com.android.email.activity.setup.AccountSettings;
import com.android.email.activity.setup.AccountSetupBasics; import com.android.email.activity.setup.AccountSetupBasics;
import com.android.email.activity.setup.AccountSetupCheckSettings; import com.android.email.activity.setup.AccountSetupCheckSettings;
import com.android.email.mail.Folder;
import com.android.email.mail.MessagingException; import com.android.email.mail.MessagingException;
import com.android.email.mail.Store; import com.android.email.mail.Store;
import com.android.email.mail.store.LocalStore; import com.android.email.mail.store.LocalStore;
@ -85,7 +86,7 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli
NotificationManager notifMgr = (NotificationManager) NotificationManager notifMgr = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE); getSystemService(Context.NOTIFICATION_SERVICE);
notifMgr.cancel(1); notifMgr.cancelAll();
refresh(); refresh();
} }
@ -106,6 +107,16 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli
private void onRefresh() { private void onRefresh() {
MessagingController.getInstance(getApplication()).checkMail(this, null, null); MessagingController.getInstance(getApplication()).checkMail(this, null, null);
} }
private void onClearCommands(Account account) {
MessagingController.getInstance(getApplication()).clearAllPending(account);
}
private void onEmptyTrash(Account account)
{
MessagingController.getInstance(getApplication()).emptyTrash(account, null);
}
private void onCompose() { private void onCompose() {
Account defaultAccount = Account defaultAccount =
@ -183,6 +194,12 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli
case R.id.open: case R.id.open:
onOpenAccount(account); onOpenAccount(account);
break; break;
case R.id.clear_pending:
onClearCommands(account);
break;
case R.id.empty_trash:
onEmptyTrash(account);
break;
} }
return true; return true;
} }
@ -286,6 +303,7 @@ getPackageManager().getPackageInfo(getPackageName(), 0);
} }
return super.onKeyDown(keyCode, event); return super.onKeyDown(keyCode, event);
} }
class AccountsAdapter extends ArrayAdapter<Account> { class AccountsAdapter extends ArrayAdapter<Account> {
public AccountsAdapter(Account[] accounts) { public AccountsAdapter(Account[] accounts) {
@ -317,13 +335,7 @@ getPackageManager().getPackageInfo(getPackageName(), 0);
} }
int unreadMessageCount = 0; int unreadMessageCount = 0;
try { try {
LocalStore localStore = (LocalStore) Store.getInstance( unreadMessageCount = account.getUnreadMessageCount(Accounts.this, getApplication());
account.getLocalStoreUri(),
getApplication());
LocalFolder localFolder = (LocalFolder) localStore.getFolder(Email.INBOX);
if (localFolder.exists()) {
unreadMessageCount = localFolder.getUnreadMessageCount();
}
} }
catch (MessagingException me) { catch (MessagingException me) {
/* /*

File diff suppressed because it is too large Load Diff

View File

@ -370,6 +370,17 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
return; return;
} }
String text = intent.getStringExtra(Intent.EXTRA_TEXT);
if (text != null)
{
mMessageContentView.setText(text);
}
String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
if (subject != null)
{
mSubjectView.setText(subject);
}
String type = intent.getType(); String type = intent.getType();
Uri stream = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); Uri stream = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
if (stream != null && type != null) { if (stream != null && type != null) {
@ -397,7 +408,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mAccount, mAccount,
mFolder, mFolder,
mSourceMessageUid, mSourceMessageUid,
mListener); null);
} }
if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action) || if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action) ||
ACTION_EDIT_DRAFT.equals(action)) { ACTION_EDIT_DRAFT.equals(action)) {

View File

@ -93,8 +93,31 @@ public class MessageView extends Activity
private String mNextMessageUid = null; private String mNextMessageUid = null;
private String mPreviousMessageUid = null; private String mPreviousMessageUid = null;
private DateFormat mDateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); private DateFormat dateFormat = null;
private DateFormat mTimeFormat = DateFormat.getTimeInstance(DateFormat.SHORT); private DateFormat timeFormat = null;
private DateFormat getDateFormat()
{
if (dateFormat == null)
{
dateFormat = android.pim.DateFormat.getDateFormat(getApplication());
}
return dateFormat;
}
private DateFormat getTimeFormat()
{
if (timeFormat == null)
{
timeFormat = android.pim.DateFormat.getTimeFormat(getApplication());
}
return timeFormat;
}
private void clearFormats()
{
dateFormat = null;
timeFormat = null;
}
private Listener mListener = new Listener(); private Listener mListener = new Listener();
private MessageViewHandler mHandler = new MessageViewHandler(); private MessageViewHandler mHandler = new MessageViewHandler();
@ -109,7 +132,11 @@ public class MessageView extends Activity
case KeyEvent.KEYCODE_F: { onForward(); return true;} case KeyEvent.KEYCODE_F: { onForward(); return true;}
case KeyEvent.KEYCODE_A: { onReplyAll(); return true; } case KeyEvent.KEYCODE_A: { onReplyAll(); return true; }
case KeyEvent.KEYCODE_R: { onReply(); return true; } case KeyEvent.KEYCODE_R: { onReply(); return true; }
case KeyEvent.KEYCODE_J: { onPrevious(); return true; } case KeyEvent.KEYCODE_J:
case KeyEvent.KEYCODE_P:
{ onPrevious(); return true; }
case KeyEvent.KEYCODE_SPACE:
case KeyEvent.KEYCODE_N:
case KeyEvent.KEYCODE_K: { onNext(); return true; } case KeyEvent.KEYCODE_K: { onNext(); return true; }
case KeyEvent.KEYCODE_Z: { if (event.isShiftPressed()) { case KeyEvent.KEYCODE_Z: { if (event.isShiftPressed()) {
mMessageContentView.zoomIn(); mMessageContentView.zoomIn();
@ -301,10 +328,10 @@ public class MessageView extends Activity
mAttachments.setVisibility(View.GONE); mAttachments.setVisibility(View.GONE);
mAttachmentIcon.setVisibility(View.GONE); mAttachmentIcon.setVisibility(View.GONE);
findViewById(R.id.reply).setOnClickListener(this); setOnClickListener(R.id.reply);
findViewById(R.id.delete).setOnClickListener(this); setOnClickListener(R.id.delete);
findViewById(R.id.forward).setOnClickListener(this); setOnClickListener(R.id.forward);
findViewById(R.id.show_pictures).setOnClickListener(this); setOnClickListener(R.id.show_pictures);
// UrlInterceptRegistry.registerHandler(this); // UrlInterceptRegistry.registerHandler(this);
@ -318,11 +345,12 @@ public class MessageView extends Activity
mFolder = intent.getStringExtra(EXTRA_FOLDER); mFolder = intent.getStringExtra(EXTRA_FOLDER);
mMessageUid = intent.getStringExtra(EXTRA_MESSAGE); mMessageUid = intent.getStringExtra(EXTRA_MESSAGE);
mFolderUids = intent.getStringArrayListExtra(EXTRA_FOLDER_UIDS); mFolderUids = intent.getStringArrayListExtra(EXTRA_FOLDER_UIDS);
findSurroundingMessagesUid();
View next = findViewById(R.id.next); View next = findViewById(R.id.next);
View previous = findViewById(R.id.previous); View previous = findViewById(R.id.previous);
findSurroundingMessagesUid();
/* /*
* Next and Previous Message are not shown in landscape mode, so * Next and Previous Message are not shown in landscape mode, so
* we need to check before we use them. * we need to check before we use them.
@ -331,6 +359,7 @@ public class MessageView extends Activity
next.setOnClickListener(this); next.setOnClickListener(this);
previous.setOnClickListener(this); previous.setOnClickListener(this);
previous.setVisibility(mPreviousMessageUid != null ? View.VISIBLE : View.GONE); previous.setVisibility(mPreviousMessageUid != null ? View.VISIBLE : View.GONE);
next.setVisibility(mNextMessageUid != null ? View.VISIBLE : View.GONE); next.setVisibility(mNextMessageUid != null ? View.VISIBLE : View.GONE);
@ -351,10 +380,19 @@ public class MessageView extends Activity
mAccount, mAccount,
mFolder, mFolder,
mMessageUid, mMessageUid,
mListener); null);
} }
}.start(); }.start();
} }
private void setOnClickListener(int viewCode)
{
View thisView = findViewById(viewCode);
if (thisView != null)
{
thisView.setOnClickListener(this);
}
}
private void findSurroundingMessagesUid() { private void findSurroundingMessagesUid() {
for (int i = 0, count = mFolderUids.size(); i < count; i++) { for (int i = 0, count = mFolderUids.size(); i < count; i++) {
@ -374,6 +412,7 @@ public class MessageView extends Activity
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
clearFormats();
MessagingController.getInstance(getApplication()).addListener(mListener); MessagingController.getInstance(getApplication()).addListener(mListener);
} }
@ -427,8 +466,22 @@ public class MessageView extends Activity
finish(); finish();
} }
} }
private void onSendAlternate() {
if (mMessage != null) {
MessagingController.getInstance(getApplication()).sendAlternate(this, mAccount, mMessage);
}
}
private void onNext() { private void onNext() {
if (mNextMessageUid == null)
{
Toast.makeText(this,
getString(R.string.end_of_folder),
Toast.LENGTH_SHORT).show();
return;
}
Bundle extras = new Bundle(1); Bundle extras = new Bundle(1);
extras.putBoolean(EXTRA_NEXT, true); extras.putBoolean(EXTRA_NEXT, true);
MessageView.actionView(this, mAccount, mFolder, mNextMessageUid, mFolderUids, extras); MessageView.actionView(this, mAccount, mFolder, mNextMessageUid, mFolderUids, extras);
@ -436,6 +489,13 @@ public class MessageView extends Activity
} }
private void onPrevious() { private void onPrevious() {
if (mPreviousMessageUid == null)
{
Toast.makeText(this,
getString(R.string.end_of_folder),
Toast.LENGTH_SHORT).show();
return;
}
MessageView.actionView(this, mAccount, mFolder, mPreviousMessageUid, mFolderUids); MessageView.actionView(this, mAccount, mFolder, mPreviousMessageUid, mFolderUids);
finish(); finish();
} }
@ -518,6 +578,9 @@ public class MessageView extends Activity
case R.id.reply: case R.id.reply:
onReply(); onReply();
break; break;
case R.id.reply_all:
onReplyAll();
break;
case R.id.delete: case R.id.delete:
onDelete(); onDelete();
break; break;
@ -556,6 +619,9 @@ public class MessageView extends Activity
case R.id.forward: case R.id.forward:
onForward(); onForward();
break; break;
case R.id.send_alternate:
onSendAlternate();
break;
case R.id.mark_as_unread: case R.id.mark_as_unread:
onMarkAsUnread(); onMarkAsUnread();
break; break;
@ -712,8 +778,8 @@ public class MessageView extends Activity
String subjectText = message.getSubject(); String subjectText = message.getSubject();
String fromText = Address.toFriendly(message.getFrom()); String fromText = Address.toFriendly(message.getFrom());
String dateText = Utility.isDateToday(message.getSentDate()) ? String dateText = Utility.isDateToday(message.getSentDate()) ?
mTimeFormat.format(message.getSentDate()) : getTimeFormat().format(message.getSentDate()) :
mDateTimeFormat.format(message.getSentDate()); getDateFormat().format(message.getSentDate());
String toText = Address.toFriendly(message.getRecipients(RecipientType.TO)); String toText = Address.toFriendly(message.getRecipients(RecipientType.TO));
boolean hasAttachments = ((LocalMessage) message).getAttachmentCount() > 0; boolean hasAttachments = ((LocalMessage) message).getAttachmentCount() > 0;
mHandler.setHeaders(subjectText, mHandler.setHeaders(subjectText,
@ -773,7 +839,7 @@ public class MessageView extends Activity
/* /*
* TODO this should be smarter, change to regex for img, but consider how to * TODO this should be smarter, change to regex for img, but consider how to
* get backgroung images and a million other things that HTML allows. * get background images and a million other things that HTML allows.
*/ */
if (text.contains("<img")) { if (text.contains("<img")) {
mHandler.showShowPictures(true); mHandler.showShowPictures(true);

View File

@ -28,11 +28,13 @@ public class AccountSettings extends PreferenceActivity {
private static final String PREFERENCE_DISPLAY_COUNT = "account_display_count"; private static final String PREFERENCE_DISPLAY_COUNT = "account_display_count";
private static final String PREFERENCE_DEFAULT = "account_default"; private static final String PREFERENCE_DEFAULT = "account_default";
private static final String PREFERENCE_NOTIFY = "account_notify"; private static final String PREFERENCE_NOTIFY = "account_notify";
private static final String PREFERENCE_NOTIFY_RINGTONE = "account_notify_ringtone";
private static final String PREFERENCE_VIBRATE = "account_vibrate"; private static final String PREFERENCE_VIBRATE = "account_vibrate";
private static final String PREFERENCE_RINGTONE = "account_ringtone"; private static final String PREFERENCE_RINGTONE = "account_ringtone";
private static final String PREFERENCE_INCOMING = "incoming"; private static final String PREFERENCE_INCOMING = "incoming";
private static final String PREFERENCE_OUTGOING = "outgoing"; private static final String PREFERENCE_OUTGOING = "outgoing";
private static final String PREFERENCE_DISPLAY_MODE = "folder_display_mode";
private static final String PREFERENCE_SYNC_MODE = "folder_sync_mode";
private static final String PREFERENCE_DELETE_POLICY = "delete_policy";
private Account mAccount; private Account mAccount;
@ -41,9 +43,11 @@ public class AccountSettings extends PreferenceActivity {
private ListPreference mDisplayCount; private ListPreference mDisplayCount;
private CheckBoxPreference mAccountDefault; private CheckBoxPreference mAccountDefault;
private CheckBoxPreference mAccountNotify; private CheckBoxPreference mAccountNotify;
private CheckBoxPreference mAccountNotifyRingtone;
private CheckBoxPreference mAccountVibrate; private CheckBoxPreference mAccountVibrate;
private RingtonePreference mAccountRingtone; private RingtonePreference mAccountRingtone;
private ListPreference mDisplayMode;
private ListPreference mSyncMode;
private ListPreference mDeletePolicy;
public static void actionSettings(Context context, Account account) { public static void actionSettings(Context context, Account account) {
Intent i = new Intent(context, AccountSettings.class); Intent i = new Intent(context, AccountSettings.class);
@ -87,6 +91,45 @@ public class AccountSettings extends PreferenceActivity {
return false; return false;
} }
}); });
mDisplayMode = (ListPreference) findPreference(PREFERENCE_DISPLAY_MODE);
mDisplayMode.setValue(mAccount.getFolderDisplayMode().name());
mDisplayMode.setSummary(mDisplayMode.getEntry());
mDisplayMode.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
final String summary = newValue.toString();
int index = mDisplayMode.findIndexOfValue(summary);
mDisplayMode.setSummary(mDisplayMode.getEntries()[index]);
mDisplayMode.setValue(summary);
return false;
}
});
mSyncMode = (ListPreference) findPreference(PREFERENCE_SYNC_MODE);
mSyncMode.setValue(mAccount.getFolderSyncMode().name());
mSyncMode.setSummary(mSyncMode.getEntry());
mSyncMode.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
final String summary = newValue.toString();
int index = mSyncMode.findIndexOfValue(summary);
mSyncMode.setSummary(mSyncMode.getEntries()[index]);
mSyncMode.setValue(summary);
return false;
}
});
mDeletePolicy = (ListPreference) findPreference(PREFERENCE_DELETE_POLICY);
mDeletePolicy.setValue("" + mAccount.getDeletePolicy());
mDeletePolicy.setSummary(mDeletePolicy.getEntry());
mDeletePolicy.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
final String summary = newValue.toString();
int index = mDeletePolicy.findIndexOfValue(summary);
mDeletePolicy.setSummary(mDeletePolicy.getEntries()[index]);
mDeletePolicy.setValue(summary);
return false;
}
});
mDisplayCount = (ListPreference) findPreference(PREFERENCE_DISPLAY_COUNT); mDisplayCount = (ListPreference) findPreference(PREFERENCE_DISPLAY_COUNT);
mDisplayCount.setValue(String.valueOf(mAccount.getDisplayCount())); mDisplayCount.setValue(String.valueOf(mAccount.getDisplayCount()));
@ -108,9 +151,6 @@ public class AccountSettings extends PreferenceActivity {
mAccountNotify = (CheckBoxPreference) findPreference(PREFERENCE_NOTIFY); mAccountNotify = (CheckBoxPreference) findPreference(PREFERENCE_NOTIFY);
mAccountNotify.setChecked(mAccount.isNotifyNewMail()); mAccountNotify.setChecked(mAccount.isNotifyNewMail());
mAccountNotifyRingtone = (CheckBoxPreference) findPreference(PREFERENCE_NOTIFY_RINGTONE);
mAccountNotifyRingtone.setChecked(mAccount.isNotifyRingtone());
mAccountRingtone = (RingtonePreference) findPreference(PREFERENCE_RINGTONE); mAccountRingtone = (RingtonePreference) findPreference(PREFERENCE_RINGTONE);
// XXX: The following two lines act as a workaround for the RingtonePreference // XXX: The following two lines act as a workaround for the RingtonePreference
@ -159,14 +199,17 @@ public class AccountSettings extends PreferenceActivity {
} }
mAccount.setDescription(mAccountDescription.getText()); mAccount.setDescription(mAccountDescription.getText());
mAccount.setNotifyNewMail(mAccountNotify.isChecked()); mAccount.setNotifyNewMail(mAccountNotify.isChecked());
mAccount.setNotifyRingtone(mAccountNotifyRingtone.isChecked());
mAccount.setAutomaticCheckIntervalMinutes(Integer.parseInt(mCheckFrequency.getValue())); mAccount.setAutomaticCheckIntervalMinutes(Integer.parseInt(mCheckFrequency.getValue()));
mAccount.setDisplayCount(Integer.parseInt(mDisplayCount.getValue())); mAccount.setDisplayCount(Integer.parseInt(mDisplayCount.getValue()));
mAccount.setVibrate(mAccountVibrate.isChecked()); mAccount.setVibrate(mAccountVibrate.isChecked());
mAccount.setFolderDisplayMode(Account.FolderMode.valueOf(mDisplayMode.getValue()));
mAccount.setFolderSyncMode(Account.FolderMode.valueOf(mSyncMode.getValue()));
mAccount.setDeletePolicy(Integer.parseInt(mDeletePolicy.getValue()));
SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences(); SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
mAccount.setRingtone(prefs.getString(PREFERENCE_RINGTONE, null)); mAccount.setRingtone(prefs.getString(PREFERENCE_RINGTONE, null));
mAccount.save(Preferences.getPreferences(this)); mAccount.save(Preferences.getPreferences(this));
Email.setServicesEnabled(this); Email.setServicesEnabled(this);
// TODO: refresh folder list here
} }
@Override @Override

View File

@ -12,6 +12,7 @@ import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.text.method.DigitsKeyListener; import android.text.method.DigitsKeyListener;
import android.util.Log;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.widget.AdapterView; import android.widget.AdapterView;
@ -22,6 +23,7 @@ import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import com.android.email.Account; import com.android.email.Account;
import com.android.email.Email;
import com.android.email.Preferences; import com.android.email.Preferences;
import com.android.email.R; import com.android.email.R;
import com.android.email.Utility; import com.android.email.Utility;
@ -104,12 +106,15 @@ public class AccountSetupIncoming extends Activity implements OnClickListener {
}; };
SpinnerOption deletePolicies[] = { SpinnerOption deletePolicies[] = {
new SpinnerOption(0, new SpinnerOption(Account.DELETE_POLICY_NEVER,
getString(R.string.account_setup_incoming_delete_policy_never_label)), getString(R.string.account_setup_incoming_delete_policy_never_label)),
new SpinnerOption(1, /*new SpinnerOption(Account.DELETE_POLICY_7DAYS,
getString(R.string.account_setup_incoming_delete_policy_7days_label)), getString(R.string.account_setup_incoming_delete_policy_7days_label)),*/
new SpinnerOption(2, new SpinnerOption(Account.DELETE_POLICY_ON_DELETE,
getString(R.string.account_setup_incoming_delete_policy_delete_label)), getString(R.string.account_setup_incoming_delete_policy_delete_label)),
new SpinnerOption(Account.DELETE_POLICY_MARK_AS_READ,
getString(R.string.account_setup_incoming_delete_policy_markread_label)),
}; };
ArrayAdapter<SpinnerOption> securityTypesAdapter = new ArrayAdapter<SpinnerOption>(this, ArrayAdapter<SpinnerOption> securityTypesAdapter = new ArrayAdapter<SpinnerOption>(this,
@ -203,8 +208,6 @@ public class AccountSetupIncoming extends Activity implements OnClickListener {
mAccountPorts = imapPorts; mAccountPorts = imapPorts;
mAccountSchemes = imapSchemes; mAccountSchemes = imapSchemes;
findViewById(R.id.account_delete_policy_label).setVisibility(View.GONE);
mDeletePolicyView.setVisibility(View.GONE);
if (uri.getPath() != null && uri.getPath().length() > 0) { if (uri.getPath() != null && uri.getPath().length() > 0) {
mImapPathPrefixView.setText(uri.getPath().substring(1)); mImapPathPrefixView.setText(uri.getPath().substring(1));
} }
@ -326,9 +329,10 @@ public class AccountSetupIncoming extends Activity implements OnClickListener {
throw new Error(use); throw new Error(use);
} }
mAccount.setDeletePolicy((Integer)((SpinnerOption)mDeletePolicyView.getSelectedItem()).value); int deleteSpinnerVal = (Integer)((SpinnerOption)mDeletePolicyView.getSelectedItem()).value;
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, true, false);
mAccount.setDeletePolicy(deleteSpinnerVal);
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, true, false);
} }
public void onClick(View v) { public void onClick(View v) {

View File

@ -28,7 +28,6 @@ public class AccountSetupOptions extends Activity implements OnClickListener {
private CheckBox mDefaultView; private CheckBox mDefaultView;
private CheckBox mNotifyView; private CheckBox mNotifyView;
private CheckBox mNotifyRingtoneView;
private Account mAccount; private Account mAccount;
@ -48,13 +47,14 @@ public class AccountSetupOptions extends Activity implements OnClickListener {
mDisplayCountView = (Spinner)findViewById(R.id.account_display_count); mDisplayCountView = (Spinner)findViewById(R.id.account_display_count);
mDefaultView = (CheckBox)findViewById(R.id.account_default); mDefaultView = (CheckBox)findViewById(R.id.account_default);
mNotifyView = (CheckBox)findViewById(R.id.account_notify); mNotifyView = (CheckBox)findViewById(R.id.account_notify);
mNotifyRingtoneView = (CheckBox)findViewById(R.id.account_notify_ringtone);
findViewById(R.id.next).setOnClickListener(this); findViewById(R.id.next).setOnClickListener(this);
SpinnerOption checkFrequencies[] = { SpinnerOption checkFrequencies[] = {
new SpinnerOption(-1, new SpinnerOption(-1,
getString(R.string.account_setup_options_mail_check_frequency_never)), getString(R.string.account_setup_options_mail_check_frequency_never)),
new SpinnerOption(1,
getString(R.string.account_setup_options_mail_check_frequency_1min)),
new SpinnerOption(5, new SpinnerOption(5,
getString(R.string.account_setup_options_mail_check_frequency_5min)), getString(R.string.account_setup_options_mail_check_frequency_5min)),
new SpinnerOption(10, new SpinnerOption(10,
@ -96,7 +96,6 @@ public class AccountSetupOptions extends Activity implements OnClickListener {
mDefaultView.setChecked(true); mDefaultView.setChecked(true);
} }
mNotifyView.setChecked(mAccount.isNotifyNewMail()); mNotifyView.setChecked(mAccount.isNotifyNewMail());
mNotifyRingtoneView.setChecked(mAccount.isNotifyRingtone());
SpinnerOption.setSpinnerOptionValue(mCheckFrequencyView, mAccount SpinnerOption.setSpinnerOptionValue(mCheckFrequencyView, mAccount
.getAutomaticCheckIntervalMinutes()); .getAutomaticCheckIntervalMinutes());
SpinnerOption.setSpinnerOptionValue(mDisplayCountView, mAccount SpinnerOption.setSpinnerOptionValue(mDisplayCountView, mAccount
@ -106,7 +105,6 @@ public class AccountSetupOptions extends Activity implements OnClickListener {
private void onDone() { private void onDone() {
mAccount.setDescription(mAccount.getEmail()); mAccount.setDescription(mAccount.getEmail());
mAccount.setNotifyNewMail(mNotifyView.isChecked()); mAccount.setNotifyNewMail(mNotifyView.isChecked());
mAccount.setNotifyRingtone(mNotifyRingtoneView.isChecked());
mAccount.setAutomaticCheckIntervalMinutes((Integer)((SpinnerOption)mCheckFrequencyView mAccount.setAutomaticCheckIntervalMinutes((Integer)((SpinnerOption)mCheckFrequencyView
.getSelectedItem()).value); .getSelectedItem()).value);
mAccount.setDisplayCount((Integer)((SpinnerOption)mDisplayCountView mAccount.setDisplayCount((Integer)((SpinnerOption)mDisplayCountView

View File

@ -0,0 +1,136 @@
package com.android.email.activity.setup;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.preference.PreferenceActivity;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.RingtonePreference;
import com.android.email.Account;
import com.android.email.Email;
import com.android.email.Preferences;
import com.android.email.R;
import com.android.email.mail.Folder;
import com.android.email.mail.MessagingException;
import com.android.email.mail.Store;
import com.android.email.mail.Folder.FolderClass;
import com.android.email.mail.store.LocalStore.LocalFolder;
public class FolderSettings extends PreferenceActivity {
private static final String EXTRA_FOLDER_NAME = "com.android.email.folderName";
private static final String EXTRA_ACCOUNT = "com.android.email.account";
private static final String PREFERENCE_TOP_CATERGORY = "folder_settings";
private static final String PREFERENCE_DISPLAY_CLASS = "folder_settings_folder_display_mode";
private static final String PREFERENCE_SYNC_CLASS = "folder_settings_folder_sync_mode";
private LocalFolder mFolder;
private ListPreference mDisplayClass;
private ListPreference mSyncClass;
public static void actionSettings(Context context, Account account, String folderName) {
Intent i = new Intent(context, FolderSettings.class);
i.putExtra(EXTRA_FOLDER_NAME, folderName);
i.putExtra(EXTRA_ACCOUNT, account);
context.startActivity(i);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String folderName = (String)getIntent().getSerializableExtra(EXTRA_FOLDER_NAME);
Account mAccount = (Account)getIntent().getSerializableExtra(EXTRA_ACCOUNT);
try
{
Store localStore = Store.getInstance(mAccount.getLocalStoreUri(),
getApplication());
mFolder = (LocalFolder) localStore.getFolder(folderName);
mFolder.refresh(Preferences.getPreferences(this));
}
catch (MessagingException me)
{
Log.e(Email.LOG_TAG, "Unable to edit folder " + folderName + " preferences", me);
return;
}
addPreferencesFromResource(R.xml.folder_settings_preferences);
Preference category = findPreference(PREFERENCE_TOP_CATERGORY);
category.setTitle(getString(R.string.folder_settings_title));
mDisplayClass = (ListPreference) findPreference(PREFERENCE_DISPLAY_CLASS);
mDisplayClass.setValue(mFolder.getDisplayClass().name());
mDisplayClass.setSummary(mDisplayClass.getEntry());
mDisplayClass.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
final String summary = newValue.toString();
int index = mDisplayClass.findIndexOfValue(summary);
mDisplayClass.setSummary(mDisplayClass.getEntries()[index]);
mDisplayClass.setValue(summary);
return false;
}
});
mSyncClass = (ListPreference) findPreference(PREFERENCE_SYNC_CLASS);
mSyncClass.setValue(mFolder.getRawSyncClass().name());
mSyncClass.setSummary(mSyncClass.getEntry());
mSyncClass.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
final String summary = newValue.toString();
int index = mSyncClass.findIndexOfValue(summary);
mSyncClass.setSummary(mSyncClass.getEntries()[index]);
mSyncClass.setValue(summary);
return false;
}
});
}
@Override
public void onResume() {
super.onResume();
try
{
mFolder.refresh(Preferences.getPreferences(this));
}
catch (MessagingException me)
{
Log.e(Email.LOG_TAG, "Could not refresh folder preferences for folder " + mFolder.getName(), me);
}
}
private void saveSettings() {
mFolder.setDisplayClass(FolderClass.valueOf(mDisplayClass.getValue()));
mFolder.setSyncClass(FolderClass.valueOf(mSyncClass.getValue()));
try
{
mFolder.save(Preferences.getPreferences(this));
}
catch (MessagingException me)
{
Log.e(Email.LOG_TAG, "Could not refresh folder preferences for folder " + mFolder.getName(), me);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
saveSettings();
}
return super.onKeyDown(keyCode, event);
}
}

View File

@ -18,6 +18,10 @@ public enum Flag {
* these flags and Strings to represent user defined flags. At that point the below * these flags and Strings to represent user defined flags. At that point the below
* flags should become user defined flags. * flags should become user defined flags.
*/ */
/*
* For POP3 to indicate that the message does not have SEEN info
*/
X_NO_SEEN_INFO,
/** /**
* Delete and remove from the LocalStore immediately. * Delete and remove from the LocalStore immediately.
*/ */
@ -45,4 +49,9 @@ public enum Flag {
* This does not include attachments, which are never downloaded fully. * This does not include attachments, which are never downloaded fully.
*/ */
X_DOWNLOADED_PARTIAL, X_DOWNLOADED_PARTIAL,
/**
* Indicates that the copy of a message to the Sent folder has started.
*/
X_REMOTE_COPY_STARTED,
} }

View File

@ -1,11 +1,19 @@
package com.android.email.mail; package com.android.email.mail;
import com.android.email.Preferences;
public abstract class Folder { public abstract class Folder {
private String status = null;
private long lastChecked = 0;
public enum OpenMode { public enum OpenMode {
READ_WRITE, READ_ONLY, READ_WRITE, READ_ONLY,
} }
public enum FolderClass {
NONE, FIRST_CLASS, SECOND_CLASS;
}
public enum FolderType { public enum FolderType {
HOLDS_FOLDERS, HOLDS_MESSAGES, HOLDS_FOLDERS, HOLDS_MESSAGES,
} }
@ -84,6 +92,10 @@ public abstract class Folder {
public abstract void setFlags(Message[] messages, Flag[] flags, boolean value) public abstract void setFlags(Message[] messages, Flag[] flags, boolean value)
throws MessagingException; throws MessagingException;
public abstract void setFlags(Flag[] flags, boolean value) throws MessagingException;
public abstract String getUidFromMessageId(Message message) throws MessagingException;
public abstract Message[] expunge() throws MessagingException; public abstract Message[] expunge() throws MessagingException;
@ -104,4 +116,42 @@ public abstract class Folder {
public String toString() { public String toString() {
return getName(); return getName();
} }
public long getLastChecked()
{
return lastChecked;
}
public void setLastChecked(long lastChecked) throws MessagingException
{
this.lastChecked = lastChecked;
}
public FolderClass getDisplayClass()
{
return FolderClass.NONE;
}
public FolderClass getSyncClass()
{
return getDisplayClass();
}
public void refresh(Preferences preferences) throws MessagingException
{
}
public String getStatus()
{
return status;
}
public void setStatus(String status) throws MessagingException
{
this.status = status;
}
} }

View File

@ -83,6 +83,8 @@ public abstract class Message implements Part, Body {
public boolean isMimeType(String mimeType) throws MessagingException { public boolean isMimeType(String mimeType) throws MessagingException {
return getContentType().startsWith(mimeType); return getContentType().startsWith(mimeType);
} }
public void delete(String trashFolderName) throws MessagingException {} ;
/* /*
* TODO Refactor Flags at some point to be able to store user defined flags. * TODO Refactor Flags at some point to be able to store user defined flags.

View File

@ -7,6 +7,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress; import java.net.SocketAddress;
@ -17,6 +18,7 @@ import java.nio.CharBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.Security;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@ -44,6 +46,7 @@ import com.android.email.mail.MessagingException;
import com.android.email.mail.Part; import com.android.email.mail.Part;
import com.android.email.mail.Store; import com.android.email.mail.Store;
import com.android.email.mail.CertificateValidationException; import com.android.email.mail.CertificateValidationException;
import com.android.email.mail.Folder.FolderType;
import com.android.email.mail.internet.MimeBodyPart; import com.android.email.mail.internet.MimeBodyPart;
import com.android.email.mail.internet.MimeHeader; import com.android.email.mail.internet.MimeHeader;
import com.android.email.mail.internet.MimeMessage; import com.android.email.mail.internet.MimeMessage;
@ -167,7 +170,7 @@ public class ImapStore extends Store {
synchronized (mFolderCache) { synchronized (mFolderCache) {
folder = mFolderCache.get(name); folder = mFolderCache.get(name);
if (folder == null) { if (folder == null) {
folder = new ImapFolder(name); folder = new ImapFolder(this, name);
mFolderCache.put(name, folder); mFolderCache.put(name, folder);
} }
} }
@ -263,7 +266,10 @@ public class ImapStore extends Store {
} }
private void releaseConnection(ImapConnection connection) { private void releaseConnection(ImapConnection connection) {
mConnections.offer(connection); synchronized(mConnections)
{
mConnections.offer(connection);
}
} }
private String encodeFolderName(String name) { private String encodeFolderName(String name) {
@ -307,14 +313,16 @@ public class ImapStore extends Store {
private ImapConnection mConnection; private ImapConnection mConnection;
private OpenMode mMode; private OpenMode mMode;
private boolean mExists; private boolean mExists;
private ImapStore store = null;
public ImapFolder(String name) { public ImapFolder(ImapStore nStore, String name) {
store = nStore;
this.mName = name; this.mName = name;
} }
public String getPrefixedName() { public String getPrefixedName() {
String prefixedName = ""; String prefixedName = "";
if(mPathPrefix.length() > 0 && !mName.equalsIgnoreCase(Email.INBOX)){ if(mPathPrefix != null && mPathPrefix.length() > 0 && !Email.INBOX.equalsIgnoreCase(mName)){
prefixedName += mPathPrefix + mPathDelimeter; prefixedName += mPathPrefix + mPathDelimeter;
} }
@ -399,11 +407,14 @@ public class ImapStore extends Store {
return mMode; return mMode;
} }
public void close(boolean expunge) { public void close(boolean expunge) throws MessagingException {
if (!isOpen()) { if (!isOpen()) {
return; return;
} }
// TODO implement expunge if (expunge)
{
expunge();
}
mMessageCount = -1; mMessageCount = -1;
synchronized (this) { synchronized (this) {
releaseConnection(mConnection); releaseConnection(mConnection);
@ -1002,31 +1013,63 @@ public class ImapStore extends Store {
while (response.more()); while (response.more());
} while(response.mTag == null); } while(response.mTag == null);
/* String newUid = getUidFromMessageId(message);
* Try to find the UID of the message we just appended using the if (Config.LOGD)
* Message-ID header. {
*/ Log.d(Email.LOG_TAG, "Got UID " + newUid + " for message");
String[] messageIdHeader = message.getHeader("Message-ID"); }
if (messageIdHeader == null || messageIdHeader.length == 0) {
continue; if (newUid != null)
{
message.setUid(newUid);
} }
String messageId = messageIdHeader[0];
List<ImapResponse> responses =
mConnection.executeSimpleCommand(
String.format("UID SEARCH (HEADER MESSAGE-ID %s)", messageId));
for (ImapResponse response1 : responses) {
if (response1.mTag == null && response1.get(0).equals("SEARCH")
&& response1.size() > 1) {
message.setUid(response1.getString(1));
}
}
} }
} }
catch (IOException ioe) { catch (IOException ioe) {
throw ioExceptionHandler(mConnection, ioe); throw ioExceptionHandler(mConnection, ioe);
} }
} }
public String getUidFromMessageId(Message message) throws MessagingException
{
try
{
/*
* Try to find the UID of the message we just appended using the
* Message-ID header.
*/
String[] messageIdHeader = message.getHeader("Message-ID");
if (messageIdHeader == null || messageIdHeader.length == 0) {
if (Config.LOGD)
{
Log.d(Email.LOG_TAG, "Did not get a message-id in order to search for UID");
}
return null;
}
String messageId = messageIdHeader[0];
if (Config.LOGD)
{
Log.d(Email.LOG_TAG, "Looking for UID for message with message-id " + messageId);
}
List<ImapResponse> responses =
mConnection.executeSimpleCommand(
String.format("UID SEARCH (HEADER MESSAGE-ID %s)", messageId));
for (ImapResponse response1 : responses) {
if (response1.mTag == null && response1.get(0).equals("SEARCH")
&& response1.size() > 1) {
return response1.getString(1);
}
}
return null;
}
catch (IOException ioe)
{
throw new MessagingException("Could not find UID for message based on Message-ID", ioe);
}
}
public Message[] expunge() throws MessagingException { public Message[] expunge() throws MessagingException {
checkOpen(); checkOpen();
@ -1038,6 +1081,31 @@ public class ImapStore extends Store {
return null; return null;
} }
@Override
public void setFlags(Flag[] flags, boolean value)
throws MessagingException {
checkOpen();
ArrayList<String> flagNames = new ArrayList<String>();
for (int i = 0, count = flags.length; i < count; i++) {
Flag flag = flags[i];
if (flag == Flag.SEEN) {
flagNames.add("\\Seen");
}
else if (flag == Flag.DELETED) {
flagNames.add("\\Deleted");
}
}
try {
mConnection.executeSimpleCommand(String.format("UID STORE 1:* %sFLAGS.SILENT (%s)",
value ? "+" : "-",
Utility.combine(flagNames.toArray(new String[flagNames.size()]), ' ')));
}
catch (IOException ioe) {
throw ioExceptionHandler(mConnection, ioe);
}
}
public void setFlags(Message[] messages, Flag[] flags, boolean value) public void setFlags(Message[] messages, Flag[] flags, boolean value)
throws MessagingException { throws MessagingException {
checkOpen(); checkOpen();
@ -1086,6 +1154,11 @@ public class ImapStore extends Store {
} }
return super.equals(o); return super.equals(o);
} }
protected ImapStore getStore()
{
return store;
}
} }
/** /**
@ -1104,9 +1177,21 @@ public class ImapStore extends Store {
} }
mNextCommandTag = 1; mNextCommandTag = 1;
try
{
Security.setProperty("networkaddress.cache.ttl", "0");
}
catch (Exception e)
{
Log.w(Email.LOG_TAG, "Could not set DNS ttl to 0", e);
}
try { try {
SocketAddress socketAddress = new InetSocketAddress(mHost, mPort);
SocketAddress socketAddress = new InetSocketAddress(mHost, mPort);
Log.i(Email.LOG_TAG, "Connecting to " + mHost + " @ IP addr " + socketAddress);
if (mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED || if (mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED ||
mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) { mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
SSLContext sslContext = SSLContext.getInstance("TLS"); SSLContext sslContext = SSLContext.getInstance("TLS");
@ -1177,6 +1262,20 @@ public class ImapStore extends Store {
throw new MessagingException( throw new MessagingException(
"Unable to open connection to IMAP server due to security error.", gse); "Unable to open connection to IMAP server due to security error.", gse);
} }
catch (ConnectException ce)
{
String ceMess = ce.getMessage();
String[] tokens = ceMess.split("-");
if (tokens != null && tokens.length > 1 && tokens[1] != null)
{
Log.e(Email.LOG_TAG, "Stripping host/port from ConnectionException", ce);
throw new ConnectException(tokens[1].trim());
}
else
{
throw ce;
}
}
} }
public boolean isOpen() { public boolean isOpen() {
@ -1245,11 +1344,19 @@ public class ImapStore extends Store {
public List<ImapResponse> executeSimpleCommand(String command, boolean sensitive) public List<ImapResponse> executeSimpleCommand(String command, boolean sensitive)
throws IOException, ImapException, MessagingException { throws IOException, ImapException, MessagingException {
if (Config.LOGV)
{
Log.v(Email.LOG_TAG, "Sending IMAP command " + command);
}
String tag = sendCommand(command, sensitive); String tag = sendCommand(command, sensitive);
ArrayList<ImapResponse> responses = new ArrayList<ImapResponse>(); ArrayList<ImapResponse> responses = new ArrayList<ImapResponse>();
ImapResponse response; ImapResponse response;
do { do {
response = mParser.readResponse(); response = mParser.readResponse();
if (Config.LOGV)
{
Log.v(Email.LOG_TAG, "Got IMAP response " + response);
}
responses.add(response); responses.add(response);
} while (response.mTag == null); } while (response.mTag == null);
if (response.size() < 1 || !response.get(0).equals("OK")) { if (response.size() < 1 || !response.get(0).equals("OK")) {
@ -1282,6 +1389,40 @@ public class ImapStore extends Store {
super.setFlag(flag, set); super.setFlag(flag, set);
mFolder.setFlags(new Message[] { this }, new Flag[] { flag }, set); mFolder.setFlags(new Message[] { this }, new Flag[] { flag }, set);
} }
@Override
public void delete(String trashFolderName) throws MessagingException
{
ImapFolder iFolder = (ImapFolder)getFolder();
Folder remoteTrashFolder = iFolder.getStore().getFolder(trashFolderName);
/*
* Attempt to copy the remote message to the remote trash folder.
*/
if (!remoteTrashFolder.exists()) {
/*
* If the remote trash folder doesn't exist we try to create it.
*/
Log.i(Email.LOG_TAG, "IMAPMessage.delete: attempting to create remote " + trashFolderName + " folder");
remoteTrashFolder.create(FolderType.HOLDS_MESSAGES);
}
if (remoteTrashFolder.exists()) {
if (Config.LOGD)
{
Log.d(Email.LOG_TAG, "IMAPMessage.delete: copying remote message to " + trashFolderName);
}
iFolder.copyMessages(new Message[] { this }, remoteTrashFolder);
setFlag(Flag.DELETED, true);
iFolder.expunge();
}
else
{
// Toast.makeText(context, R.string.message_delete_failed, Toast.LENGTH_SHORT).show();
Log.e(Email.LOG_TAG, "IMAPMessage.delete: remote Trash folder " + trashFolderName + " does not exist and could not be created");
}
}
} }
class ImapBodyPart extends MimeBodyPart { class ImapBodyPart extends MimeBodyPart {

View File

@ -8,12 +8,14 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URI; import java.net.URI;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.UUID; import java.util.UUID;
import android.content.SharedPreferences;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@ -26,6 +28,7 @@ import android.util.Config;
import android.util.Log; import android.util.Log;
import com.android.email.Email; import com.android.email.Email;
import com.android.email.Preferences;
import com.android.email.Utility; import com.android.email.Utility;
import com.android.email.codec.binary.Base64OutputStream; import com.android.email.codec.binary.Base64OutputStream;
import com.android.email.mail.Address; import com.android.email.mail.Address;
@ -38,6 +41,7 @@ import com.android.email.mail.MessageRetrievalListener;
import com.android.email.mail.MessagingException; import com.android.email.mail.MessagingException;
import com.android.email.mail.Part; import com.android.email.mail.Part;
import com.android.email.mail.Store; import com.android.email.mail.Store;
import com.android.email.mail.Folder.FolderClass;
import com.android.email.mail.Message.RecipientType; import com.android.email.mail.Message.RecipientType;
import com.android.email.mail.internet.MimeBodyPart; import com.android.email.mail.internet.MimeBodyPart;
import com.android.email.mail.internet.MimeHeader; import com.android.email.mail.internet.MimeHeader;
@ -52,14 +56,15 @@ import com.android.email.provider.AttachmentProvider;
* Implements a SQLite database backed local store for Messages. * Implements a SQLite database backed local store for Messages.
* </pre> * </pre>
*/ */
public class LocalStore extends Store { public class LocalStore extends Store implements Serializable {
private static final int DB_VERSION = 18; private static final int DB_VERSION = 22;
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.X_DESTROYED, Flag.SEEN }; private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.X_DESTROYED, Flag.SEEN };
private String mPath; private String mPath;
private SQLiteDatabase mDb; private SQLiteDatabase mDb;
private File mAttachmentsDir; private File mAttachmentsDir;
private Application mApplication; private Application mApplication;
private String uUid = null;
/** /**
* @param uri local://localhost/path/to/database/uuid.db * @param uri local://localhost/path/to/database/uuid.db
@ -77,6 +82,14 @@ public class LocalStore extends Store {
} }
mPath = uri.getPath(); mPath = uri.getPath();
// We need to associate the localstore with the account. Since we don't have the account
// handy here, we'll take the filename from the DB and use the basename of the filename
// Folders probably should have references to their containing accounts
File dbFile = new File(mPath);
String[] tokens = dbFile.getName().split("\\.");
uUid = tokens[0];
File parentDir = new File(mPath).getParentFile(); File parentDir = new File(mPath).getParentFile();
if (!parentDir.exists()) { if (!parentDir.exists()) {
parentDir.mkdirs(); parentDir.mkdirs();
@ -95,58 +108,58 @@ public class LocalStore extends Store {
private void doDbUpgrade ( SQLiteDatabase mDb) { private void doDbUpgrade ( SQLiteDatabase mDb) {
if (Config.LOGV) {
if (mDb.getVersion() < 18) { Log.v(Email.LOG_TAG, String.format("Upgrading database from %d to %d", mDb
if (Config.LOGV) { .getVersion(), DB_VERSION));
Log.v(Email.LOG_TAG, String.format("Upgrading database from %d to %d", mDb
.getVersion(), 18));
}
mDb.execSQL("DROP TABLE IF EXISTS folders");
mDb.execSQL("CREATE TABLE folders (id INTEGER PRIMARY KEY, name TEXT, "
+ "last_updated INTEGER, unread_count INTEGER, visible_limit INTEGER)");
mDb.execSQL("DROP TABLE IF EXISTS messages");
mDb.execSQL("CREATE TABLE messages (id INTEGER PRIMARY KEY, folder_id INTEGER, uid TEXT, subject TEXT, "
+ "date INTEGER, flags TEXT, sender_list TEXT, to_list TEXT, cc_list TEXT, bcc_list TEXT, reply_to_list TEXT, "
+ "html_content TEXT, text_content TEXT, attachment_count INTEGER, internal_date INTEGER)");
mDb.execSQL("DROP TABLE IF EXISTS attachments");
mDb.execSQL("CREATE TABLE attachments (id INTEGER PRIMARY KEY, message_id INTEGER,"
+ "store_data TEXT, content_uri TEXT, size INTEGER, name TEXT,"
+ "mime_type TEXT)");
mDb.execSQL("DROP TABLE IF EXISTS pending_commands");
mDb.execSQL("CREATE TABLE pending_commands " +
"(id INTEGER PRIMARY KEY, command TEXT, arguments TEXT)");
mDb.execSQL("DROP TRIGGER IF EXISTS delete_folder");
mDb.execSQL("CREATE TRIGGER delete_folder BEFORE DELETE ON folders BEGIN DELETE FROM messages WHERE old.id = folder_id; END;");
mDb.execSQL("DROP TRIGGER IF EXISTS delete_message");
mDb.execSQL("CREATE TRIGGER delete_message BEFORE DELETE ON messages BEGIN DELETE FROM attachments WHERE old.id = message_id; END;");
mDb.setVersion(18);
} }
mDb.execSQL("DROP TABLE IF EXISTS folders");
mDb.execSQL("CREATE TABLE folders (id INTEGER PRIMARY KEY, name TEXT, "
+ "last_updated INTEGER, unread_count INTEGER, visible_limit INTEGER, status TEXT)");
mDb.execSQL("DROP TABLE IF EXISTS messages");
mDb.execSQL("CREATE TABLE messages (id INTEGER PRIMARY KEY, folder_id INTEGER, uid TEXT, subject TEXT, "
+ "date INTEGER, flags TEXT, sender_list TEXT, to_list TEXT, cc_list TEXT, bcc_list TEXT, reply_to_list TEXT, "
+ "html_content TEXT, text_content TEXT, attachment_count INTEGER, internal_date INTEGER, message_id TEXT)");
mDb.execSQL("DROP TABLE IF EXISTS attachments");
mDb.execSQL("CREATE TABLE attachments (id INTEGER PRIMARY KEY, message_id INTEGER,"
+ "store_data TEXT, content_uri TEXT, size INTEGER, name TEXT,"
+ "mime_type TEXT)");
mDb.execSQL("DROP TABLE IF EXISTS pending_commands");
mDb.execSQL("CREATE TABLE pending_commands " +
"(id INTEGER PRIMARY KEY, command TEXT, arguments TEXT)");
mDb.execSQL("DROP TRIGGER IF EXISTS delete_folder");
mDb.execSQL("CREATE TRIGGER delete_folder BEFORE DELETE ON folders BEGIN DELETE FROM messages WHERE old.id = folder_id; END;");
mDb.execSQL("DROP TRIGGER IF EXISTS delete_message");
mDb.execSQL("CREATE TRIGGER delete_message BEFORE DELETE ON messages BEGIN DELETE FROM attachments WHERE old.id = message_id; END;");
mDb.setVersion(DB_VERSION);
if (mDb.getVersion() != DB_VERSION) { if (mDb.getVersion() != DB_VERSION) {
throw new Error("Database upgrade failed!"); throw new Error("Database upgrade failed!");
} }
} }
@Override @Override
public Folder getFolder(String name) throws MessagingException { public LocalFolder getFolder(String name) throws MessagingException {
return new LocalFolder(name); return new LocalFolder(name);
} }
// TODO this takes about 260-300ms, seems slow. // TODO this takes about 260-300ms, seems slow.
@Override @Override
public Folder[] getPersonalNamespaces() throws MessagingException { public LocalFolder[] getPersonalNamespaces() throws MessagingException {
ArrayList<Folder> folders = new ArrayList<Folder>(); ArrayList<LocalFolder> folders = new ArrayList<LocalFolder>();
Cursor cursor = null; Cursor cursor = null;
try { try {
cursor = mDb.rawQuery("SELECT name FROM folders", null); cursor = mDb.rawQuery("SELECT name, id, unread_count, visible_limit, last_updated, status FROM folders", null);
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
folders.add(new LocalFolder(cursor.getString(0))); LocalFolder folder = new LocalFolder(cursor.getString(0));
folder.open(cursor.getInt(1), cursor.getInt(2), cursor.getInt(3), cursor.getLong(4), cursor.getString(5));
folders.add(folder);
} }
} }
finally { finally {
@ -154,7 +167,7 @@ public class LocalStore extends Store {
cursor.close(); cursor.close();
} }
} }
return folders.toArray(new Folder[] {}); return folders.toArray(new LocalFolder[] {});
} }
@Override @Override
@ -300,6 +313,10 @@ public class LocalStore extends Store {
public void removePendingCommand(PendingCommand command) { public void removePendingCommand(PendingCommand command) {
mDb.delete("pending_commands", "id = ?", new String[] { Long.toString(command.mId) }); mDb.delete("pending_commands", "id = ?", new String[] { Long.toString(command.mId) });
} }
public void removePendingCommands() {
mDb.delete("pending_commands", null, null);
}
public static class PendingCommand { public static class PendingCommand {
private long mId; private long mId;
@ -310,21 +327,24 @@ public class LocalStore extends Store {
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append(command); sb.append(command);
sb.append("\n"); sb.append(": ");
for (String argument : arguments) { for (String argument : arguments) {
sb.append(" "); sb.append(" ");
sb.append(argument); sb.append(argument);
sb.append("\n"); //sb.append("\n");
} }
return sb.toString(); return sb.toString();
} }
} }
public class LocalFolder extends Folder { public class LocalFolder extends Folder implements Serializable {
private String mName; private String mName;
private long mFolderId = -1; private long mFolderId = -1;
private int mUnreadMessageCount = -1; private int mUnreadMessageCount = -1;
private int mVisibleLimit = -1; private int mVisibleLimit = -1;
private FolderClass displayClass = FolderClass.NONE;
private FolderClass syncClass = FolderClass.NONE;
private String prefId = null;
public LocalFolder(String name) { public LocalFolder(String name) {
this.mName = name; this.mName = name;
@ -341,26 +361,32 @@ public class LocalStore extends Store {
} }
Cursor cursor = null; Cursor cursor = null;
try { try {
cursor = mDb.rawQuery( cursor = mDb.rawQuery("SELECT id, unread_count, visible_limit, last_updated, status FROM folders "
"SELECT id, unread_count, visible_limit FROM folders " + "where folders.name = ?",
+ "where folders.name = ?", new String[] {
new String[] { mName }); mName
if (cursor.moveToFirst()) { });
mFolderId = cursor.getInt(0); cursor.moveToFirst();
mUnreadMessageCount = cursor.getInt(1); open(cursor.getInt(0), cursor.getInt(1), cursor.getInt(2), cursor.getLong(3), cursor.getString(4));
mVisibleLimit = cursor.getInt(2);
} else { }
// Calling exists on open is a little expensive. Instead, finally {
// just handle it when we don't find it.
create(FolderType.HOLDS_MESSAGES);
open(mode);
}
} finally {
if (cursor != null) { if (cursor != null) {
cursor.close(); cursor.close();
} }
} }
} }
private void open(int id, int unreadCount, int visibleLimit, long lastChecked, String status) throws MessagingException
{
mFolderId = id;
mUnreadMessageCount = unreadCount;
mVisibleLimit = visibleLimit;
super.setStatus(status);
// Only want to set the local variable stored in the super class. This class
// does a DB update on setLastChecked
super.setLastChecked(lastChecked);
}
@Override @Override
public boolean isOpen() { public boolean isOpen() {
@ -461,6 +487,13 @@ public class LocalStore extends Store {
mDb.execSQL("UPDATE folders SET unread_count = ? WHERE id = ?", mDb.execSQL("UPDATE folders SET unread_count = ? WHERE id = ?",
new Object[] { mUnreadMessageCount, mFolderId }); new Object[] { mUnreadMessageCount, mFolderId });
} }
public void setLastChecked(long lastChecked) throws MessagingException {
open(OpenMode.READ_WRITE);
super.setLastChecked(lastChecked);
mDb.execSQL("UPDATE folders SET last_updated = ? WHERE id = ?",
new Object[] { lastChecked, mFolderId });
}
public int getVisibleLimit() throws MessagingException { public int getVisibleLimit() throws MessagingException {
open(OpenMode.READ_WRITE); open(OpenMode.READ_WRITE);
@ -474,8 +507,128 @@ public class LocalStore extends Store {
mDb.execSQL("UPDATE folders SET visible_limit = ? WHERE id = ?", mDb.execSQL("UPDATE folders SET visible_limit = ? WHERE id = ?",
new Object[] { mVisibleLimit, mFolderId }); new Object[] { mVisibleLimit, mFolderId });
} }
public void setStatus(String status) throws MessagingException
{
open(OpenMode.READ_WRITE);
super.setStatus(status);
mDb.execSQL("UPDATE folders SET status = ? WHERE id = ?",
new Object[] { status, mFolderId });
}
@Override
public FolderClass getDisplayClass()
{
return displayClass;
}
@Override
public FolderClass getSyncClass()
{
if (FolderClass.NONE == syncClass)
{
return displayClass;
}
else
{
return syncClass;
}
}
public FolderClass getRawSyncClass()
{
return syncClass;
}
public void setDisplayClass(FolderClass displayClass)
{
this.displayClass = displayClass;
}
public void setSyncClass(FolderClass syncClass)
{
this.syncClass = syncClass;
}
private String getPrefId() throws MessagingException
{
open(OpenMode.READ_WRITE);
if (prefId == null)
{
prefId = uUid + "." + mName;
}
return prefId;
}
public void delete(Preferences preferences) throws MessagingException {
String id = getPrefId();
SharedPreferences.Editor editor = preferences.mSharedPreferences.edit();
editor.remove(id + ".displayMode");
editor.remove(id + ".syncMode");
editor.commit();
}
public void save(Preferences preferences) throws MessagingException {
String id = getPrefId();
SharedPreferences.Editor editor = preferences.mSharedPreferences.edit();
// there can be a lot of folders. For the defaults, let's not save prefs, saving space
if (displayClass == FolderClass.NONE)
{
editor.remove(id + ".displayMode");
}
else
{
editor.putString(id + ".displayMode", displayClass.name());
}
if (syncClass == FolderClass.NONE)
{
editor.remove(id + ".syncMode");
}
else
{
editor.putString(id + ".syncMode", syncClass.name());
}
editor.commit();
}
public void refresh(Preferences preferences) throws MessagingException {
String id = getPrefId();
try
{
displayClass = FolderClass.valueOf(preferences.mSharedPreferences.getString(id + ".displayMode",
FolderClass.NONE.name()));
}
catch (Exception e)
{
Log.e(Email.LOG_TAG, "Unable to load displayMode for " + getName(), e);
displayClass = FolderClass.NONE;
}
try
{
syncClass = FolderClass.valueOf(preferences.mSharedPreferences.getString(id + ".syncMode",
FolderClass.NONE.name()));
}
catch (Exception e)
{
Log.e(Email.LOG_TAG, "Unable to load syncMode for " + getName(), e);
syncClass = FolderClass.NONE;
}
}
@Override @Override
public void fetch(Message[] messages, FetchProfile fp, MessageRetrievalListener listener) public void fetch(Message[] messages, FetchProfile fp, MessageRetrievalListener listener)
throws MessagingException { throws MessagingException {
@ -596,6 +749,7 @@ public class LocalStore extends Store {
message.setReplyTo(Address.unpack(cursor.getString(9))); message.setReplyTo(Address.unpack(cursor.getString(9)));
message.mAttachmentCount = cursor.getInt(10); message.mAttachmentCount = cursor.getInt(10);
message.setInternalDate(new Date(cursor.getLong(11))); message.setInternalDate(new Date(cursor.getLong(11)));
message.setHeader("Message-ID", cursor.getString(12));
} }
@Override @Override
@ -614,7 +768,7 @@ public class LocalStore extends Store {
try { try {
cursor = mDb.rawQuery( cursor = mDb.rawQuery(
"SELECT subject, sender_list, date, uid, flags, id, to_list, cc_list, " "SELECT subject, sender_list, date, uid, flags, id, to_list, cc_list, "
+ "bcc_list, reply_to_list, attachment_count, internal_date " + "bcc_list, reply_to_list, attachment_count, internal_date, message_id "
+ "FROM messages " + "WHERE uid = ? " + "AND folder_id = ?", + "FROM messages " + "WHERE uid = ? " + "AND folder_id = ?",
new String[] { new String[] {
message.getUid(), Long.toString(mFolderId) message.getUid(), Long.toString(mFolderId)
@ -640,7 +794,7 @@ public class LocalStore extends Store {
try { try {
cursor = mDb.rawQuery( cursor = mDb.rawQuery(
"SELECT subject, sender_list, date, uid, flags, id, to_list, cc_list, " "SELECT subject, sender_list, date, uid, flags, id, to_list, cc_list, "
+ "bcc_list, reply_to_list, attachment_count, internal_date " + "bcc_list, reply_to_list, attachment_count, internal_date, message_id "
+ "FROM messages " + "WHERE folder_id = ?", new String[] { + "FROM messages " + "WHERE folder_id = ?", new String[] {
Long.toString(mFolderId) Long.toString(mFolderId)
}); });
@ -761,6 +915,11 @@ public class LocalStore extends Store {
cv.put("attachment_count", attachments.size()); cv.put("attachment_count", attachments.size());
cv.put("internal_date", message.getInternalDate() == null cv.put("internal_date", message.getInternalDate() == null
? System.currentTimeMillis() : message.getInternalDate().getTime()); ? System.currentTimeMillis() : message.getInternalDate().getTime());
String[] mHeaders = message.getHeader("Message-ID");
if (mHeaders != null && mHeaders.length > 0)
{
cv.put("message_id", mHeaders[0]);
}
long messageId = mDb.insert("messages", "uid", cv); long messageId = mDb.insert("messages", "uid", cv);
for (Part attachment : attachments) { for (Part attachment : attachments) {
saveAttachment(messageId, attachment, copy); saveAttachment(messageId, attachment, copy);
@ -968,7 +1127,21 @@ public class LocalStore extends Store {
message.setFlags(flags, value); message.setFlags(flags, value);
} }
} }
@Override
public void setFlags(Flag[] flags, boolean value)
throws MessagingException {
open(OpenMode.READ_WRITE);
for (Message message : getMessages(null)) {
message.setFlags(flags, value);
}
}
@Override
public String getUidFromMessageId(Message message) throws MessagingException
{
throw new MessagingException("Cannot call getUidFromMessageId on LocalFolder");
}
@Override @Override
public Message[] expunge() throws MessagingException { public Message[] expunge() throws MessagingException {
open(OpenMode.READ_WRITE); open(OpenMode.READ_WRITE);
@ -979,6 +1152,13 @@ public class LocalStore extends Store {
*/ */
return expungedMessages.toArray(new Message[] {}); return expungedMessages.toArray(new Message[] {});
} }
public void deleteMessagesOlderThan(long cutoff) throws MessagingException
{
open(OpenMode.READ_ONLY);
mDb.execSQL("DELETE FROM messages WHERE folder_id = ? and date < ?", new Object[] {
Long.toString(mFolderId), new Long(cutoff) } );
}
@Override @Override
public void delete(boolean recurse) throws MessagingException { public void delete(boolean recurse) throws MessagingException {
@ -1066,7 +1246,7 @@ public class LocalStore extends Store {
this.mUid = uid; this.mUid = uid;
this.mFolder = folder; this.mFolder = folder;
} }
public int getAttachmentCount() { public int getAttachmentCount() {
return mAttachmentCount; return mAttachmentCount;
} }

View File

@ -282,7 +282,7 @@ public class Pop3Store extends Store {
@Override @Override
public OpenMode getMode() throws MessagingException { public OpenMode getMode() throws MessagingException {
return OpenMode.READ_ONLY; return OpenMode.READ_WRITE;
} }
@Override @Override
@ -487,13 +487,13 @@ public class Pop3Store extends Store {
@Override @Override
public Message[] getMessages(MessageRetrievalListener listener) throws MessagingException { public Message[] getMessages(MessageRetrievalListener listener) throws MessagingException {
throw new UnsupportedOperationException("Pop3Folder.getMessage(MessageRetrievalListener)"); throw new UnsupportedOperationException("Pop3: No getMessages");
} }
@Override @Override
public Message[] getMessages(String[] uids, MessageRetrievalListener listener) public Message[] getMessages(String[] uids, MessageRetrievalListener listener)
throws MessagingException { throws MessagingException {
throw new UnsupportedOperationException("Pop3Folder.getMessage(MessageRetrievalListener)"); throw new UnsupportedOperationException("Pop3: No getMessages by uids");
} }
/** /**
@ -560,7 +560,7 @@ public class Pop3Store extends Store {
*/ */
pop3Message.setBody(null); pop3Message.setBody(null);
} }
if (listener != null && !fp.contains(FetchProfile.Item.ENVELOPE)) { if (listener != null && !(fp.contains(FetchProfile.Item.ENVELOPE) && fp.size() == 1)) {
listener.messageFinished(message, i, count); listener.messageFinished(message, i, count);
} }
} catch (IOException ioe) { } catch (IOException ioe) {
@ -683,11 +683,24 @@ public class Pop3Store extends Store {
public void delete(boolean recurse) throws MessagingException { public void delete(boolean recurse) throws MessagingException {
} }
@Override
public String getUidFromMessageId(Message message) throws MessagingException
{
return null;
}
public Message[] expunge() throws MessagingException { public Message[] expunge() throws MessagingException {
return null; return null;
} }
@Override
public void setFlags(Flag[] flags, boolean value)
throws MessagingException {
Message[] messages = getMessages(null);
setFlags(messages, flags, value);
}
public void setFlags(Message[] messages, Flag[] flags, boolean value) public void setFlags(Message[] messages, Flag[] flags, boolean value)
throws MessagingException { throws MessagingException {
if (!value || !Utility.arrayContains(flags, Flag.DELETED)) { if (!value || !Utility.arrayContains(flags, Flag.DELETED)) {
@ -696,6 +709,20 @@ public class Pop3Store extends Store {
*/ */
return; return;
} }
ArrayList<String> uids = new ArrayList<String>();
try
{
for (Message message : messages)
{
uids.add(message.getUid());
}
indexUids(uids);
}
catch (IOException ioe)
{
throw new MessagingException("Could not get message number for uid " + uids, ioe);
}
try { try {
for (Message message : messages) { for (Message message : messages) {
executeSimpleCommand(String.format("DELE %s", executeSimpleCommand(String.format("DELE %s",
@ -792,13 +819,20 @@ public class Pop3Store extends Store {
private String executeSimpleCommand(String command) throws IOException, MessagingException { private String executeSimpleCommand(String command) throws IOException, MessagingException {
try { try {
open(OpenMode.READ_WRITE); open(OpenMode.READ_WRITE);
if (Config.LOGV)
{
Log.v(Email.LOG_TAG, "POP3: command '" + command + "'");
}
if (command != null) { if (command != null) {
writeLine(command); writeLine(command);
} }
String response = readLine(); String response = readLine();
if (Config.LOGV)
{
Log.v(Email.LOG_TAG, "POP3: response '" + command + "'");
}
if (response.length() > 1 && response.charAt(0) == '-') { if (response.length() > 1 && response.charAt(0) == '-') {
throw new MessagingException(response); throw new MessagingException(response);
} }
@ -831,6 +865,7 @@ public class Pop3Store extends Store {
mUid = uid; mUid = uid;
mFolder = folder; mFolder = folder;
mSize = -1; mSize = -1;
mFlags.add(Flag.X_NO_SEEN_INFO);
} }
public void setSize(int size) { public void setSize(int size) {
@ -846,6 +881,20 @@ public class Pop3Store extends Store {
super.setFlag(flag, set); super.setFlag(flag, set);
mFolder.setFlags(new Message[] { this }, new Flag[] { flag }, set); mFolder.setFlags(new Message[] { this }, new Flag[] { flag }, set);
} }
@Override
public void delete(String trashFolderName) throws MessagingException
{
// try
// {
// Poor POP3 users, we can't copy the message to the Trash folder, but they still want a delete
setFlag(Flag.DELETED, true);
// }
// catch (MessagingException me)
// {
// Log.w(Email.LOG_TAG, "Could not delete non-existant message", me);
// }
}
} }
class Pop3Capabilities { class Pop3Capabilities {

View File

@ -246,6 +246,10 @@ public class SmtpTransport extends Transport {
} catch (IOException ioe) { } catch (IOException ioe) {
throw new MessagingException("Unable to send message", ioe); throw new MessagingException("Unable to send message", ioe);
} }
finally
{
close();
}
} }
public void close() { public void close() {

View File

@ -1,6 +1,8 @@
package com.android.email.service; package com.android.email.service;
import com.android.email.Email;
import android.util.Log;
import com.android.email.MessagingController; import com.android.email.MessagingController;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
@ -9,8 +11,10 @@ import android.content.Intent;
public class BootReceiver extends BroadcastReceiver { public class BootReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Log.v(Email.LOG_TAG, "BootReceiver.onReceive" + intent);
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
MailService.actionReschedule(context); Email.setServicesEnabled(context);
} }
else if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(intent.getAction())) { else if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(intent.getAction())) {
MailService.actionCancel(context); MailService.actionCancel(context);

View File

@ -1,6 +1,7 @@
package com.android.email.service; package com.android.email.service;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import android.app.AlarmManager; import android.app.AlarmManager;
@ -27,6 +28,9 @@ import com.android.email.Preferences;
import com.android.email.R; import com.android.email.R;
import com.android.email.activity.Accounts; import com.android.email.activity.Accounts;
import com.android.email.activity.FolderMessageList; import com.android.email.activity.FolderMessageList;
import com.android.email.mail.Folder;
import com.android.email.mail.MessagingException;
import com.android.email.mail.Store;
/** /**
*/ */
@ -34,7 +38,7 @@ public class MailService extends Service {
private static final String ACTION_CHECK_MAIL = "com.android.email.intent.action.MAIL_SERVICE_WAKEUP"; private static final String ACTION_CHECK_MAIL = "com.android.email.intent.action.MAIL_SERVICE_WAKEUP";
private static final String ACTION_RESCHEDULE = "com.android.email.intent.action.MAIL_SERVICE_RESCHEDULE"; private static final String ACTION_RESCHEDULE = "com.android.email.intent.action.MAIL_SERVICE_RESCHEDULE";
private static final String ACTION_CANCEL = "com.android.email.intent.action.MAIL_SERVICE_CANCEL"; private static final String ACTION_CANCEL = "com.android.email.intent.action.MAIL_SERVICE_CANCEL";
private Listener mListener = new Listener(); private Listener mListener = new Listener();
private int mStartId; private int mStartId;
@ -52,17 +56,26 @@ public class MailService extends Service {
i.setAction(MailService.ACTION_CANCEL); i.setAction(MailService.ACTION_CANCEL);
context.startService(i); context.startService(i);
} }
@Override
public void onCreate() {
super.onCreate();
Log.v(Email.LOG_TAG, "***** MailService *****: onCreate");
}
@Override @Override
public void onStart(Intent intent, int startId) { public void onStart(Intent intent, int startId) {
setForeground(true); // if it gets killed once, it'll never restart
Log.v(Email.LOG_TAG, "***** MailService *****: onStart(" + intent + ", " + startId + ")");
super.onStart(intent, startId); super.onStart(intent, startId);
this.mStartId = startId; this.mStartId = startId;
MessagingController.getInstance(getApplication()).addListener(mListener); MessagingController.getInstance(getApplication()).addListener(mListener);
if (ACTION_CHECK_MAIL.equals(intent.getAction())) { if (ACTION_CHECK_MAIL.equals(intent.getAction())) {
if (Config.LOGV) { //if (Config.LOGV) {
MessagingController.getInstance(getApplication()).log("***** MailService *****: checking mail");
Log.v(Email.LOG_TAG, "***** MailService *****: checking mail"); Log.v(Email.LOG_TAG, "***** MailService *****: checking mail");
} //}
mListener.wakeLockAcquire(); mListener.wakeLockAcquire();
MessagingController.getInstance(getApplication()).checkMail(this, null, mListener); MessagingController.getInstance(getApplication()).checkMail(this, null, mListener);
} }
@ -70,6 +83,8 @@ public class MailService extends Service {
if (Config.LOGV) { if (Config.LOGV) {
Log.v(Email.LOG_TAG, "***** MailService *****: cancel"); Log.v(Email.LOG_TAG, "***** MailService *****: cancel");
} }
MessagingController.getInstance(getApplication()).log("***** MailService *****: cancel");
cancel(); cancel();
stopSelf(startId); stopSelf(startId);
} }
@ -77,6 +92,7 @@ public class MailService extends Service {
if (Config.LOGV) { if (Config.LOGV) {
Log.v(Email.LOG_TAG, "***** MailService *****: reschedule"); Log.v(Email.LOG_TAG, "***** MailService *****: reschedule");
} }
MessagingController.getInstance(getApplication()).log("***** MailService *****: reschedule");
reschedule(); reschedule();
stopSelf(startId); stopSelf(startId);
} }
@ -84,6 +100,7 @@ public class MailService extends Service {
@Override @Override
public void onDestroy() { public void onDestroy() {
Log.v(Email.LOG_TAG, "***** MailService *****: onDestroy()");
super.onDestroy(); super.onDestroy();
MessagingController.getInstance(getApplication()).removeListener(mListener); MessagingController.getInstance(getApplication()).removeListener(mListener);
} }
@ -103,22 +120,31 @@ public class MailService extends Service {
i.setClassName(getApplication().getPackageName(), "com.android.email.service.MailService"); i.setClassName(getApplication().getPackageName(), "com.android.email.service.MailService");
i.setAction(ACTION_CHECK_MAIL); i.setAction(ACTION_CHECK_MAIL);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0); PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
int shortestInterval = -1; int shortestInterval = -1;
for (Account account : Preferences.getPreferences(this).getAccounts()) { for (Account account : Preferences.getPreferences(this).getAccounts()) {
if (account.getAutomaticCheckIntervalMinutes() != -1 if (account.getAutomaticCheckIntervalMinutes() != -1
&& (account.getAutomaticCheckIntervalMinutes() < shortestInterval || shortestInterval == -1)) { && (account.getAutomaticCheckIntervalMinutes() < shortestInterval || shortestInterval == -1)) {
shortestInterval = account.getAutomaticCheckIntervalMinutes(); shortestInterval = account.getAutomaticCheckIntervalMinutes();
} }
} }
if (shortestInterval == -1) { if (shortestInterval == -1) {
Log.v(Email.LOG_TAG, "No next check scheduled for package " + getApplication().getPackageName());
alarmMgr.cancel(pi); alarmMgr.cancel(pi);
} }
else { else
alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() {
+ (shortestInterval * (60 * 1000)), pi); long delay = (shortestInterval * (60 * 1000));
long nextTime = System.currentTimeMillis() + delay;
String checkString = "Next check for package " + getApplication().getPackageName() + " scheduled for " + new Date(nextTime);
Log.v(Email.LOG_TAG, checkString);
MessagingController.getInstance(getApplication()).log(checkString);
alarmMgr.set(AlarmManager.RTC_WAKEUP, nextTime, pi);
} }
} }
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
@ -131,22 +157,24 @@ public class MailService extends Service {
// wakelock strategy is to be very conservative. If there is any reason to release, then release // wakelock strategy is to be very conservative. If there is any reason to release, then release
// don't want to take the chance of running wild // don't want to take the chance of running wild
public synchronized void wakeLockAcquire() { public synchronized void wakeLockAcquire()
if (wakeLock == null) { {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); if (wakeLock == null)
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Email"); {
wakeLock.setReferenceCounted(false); PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock.acquire(Email.WAKE_LOCK_TIMEOUT); wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Email");
wakeLock.setReferenceCounted(false);
wakeLock.acquire(Email.WAKE_LOCK_TIMEOUT);
} }
} }
public synchronized void wakeLockRelease()
public synchronized void wakeLockRelease() { {
if (wakeLock != null) { if (wakeLock != null)
{
wakeLock.release(); wakeLock.release();
wakeLock = null; wakeLock = null;
} }
} }
@Override @Override
public void checkMailStarted(Context context, Account account) { public void checkMailStarted(Context context, Account account) {
accountsWithNewMail.clear(); accountsWithNewMail.clear();
@ -154,8 +182,14 @@ public class MailService extends Service {
@Override @Override
public void checkMailFailed(Context context, Account account, String reason) { public void checkMailFailed(Context context, Account account, String reason) {
try
{
reschedule(); reschedule();
}
finally
{
wakeLockRelease(); wakeLockRelease();
}
stopSelf(mStartId); stopSelf(mStartId);
} }
@ -169,54 +203,105 @@ public class MailService extends Service {
accountsWithNewMail.put(account, numNewMessages); accountsWithNewMail.put(account, numNewMessages);
} }
} }
private void checkMailDone(Context context, Account doNotUseaccount)
{
if (accountsWithNewMail.isEmpty())
{
return;
}
StringBuffer notice = new StringBuffer();
int accountNumber = Email.FETCHING_EMAIL_NOTIFICATION_NO_ACCOUNT;
boolean vibrate = false;
String ringtone = null;
for (Account thisAccount : Preferences.getPreferences(context).getAccounts()) {
if (thisAccount.isNotifyNewMail())
{
int unreadMessageCount = 0;
try
{
unreadMessageCount = thisAccount.getUnreadMessageCount(context, getApplication());
if (unreadMessageCount > 0)
{
notice.append(getString(R.string.notification_new_one_account_fmt, unreadMessageCount,
thisAccount.getDescription()) + "\n");
if (accountNumber != Email.FETCHING_EMAIL_NOTIFICATION_MULTI_ACCOUNT_ID) // if already set to Multi, nothing to do
{
if (accountNumber == Email.FETCHING_EMAIL_NOTIFICATION_NO_ACCOUNT) // Haven't set to anything, yet, set to this account number
{
accountNumber = thisAccount.getAccountNumber();
}
else // Another account was already set, so there is more than one with new mail
{
accountNumber = Email.FETCHING_EMAIL_NOTIFICATION_MULTI_ACCOUNT_ID;
}
}
}
}
catch (MessagingException me)
{
Log.e(Email.LOG_TAG, "***** MailService *****: couldn't get unread count for account " +
thisAccount.getDescription(), me);
}
if (ringtone == null)
{
ringtone = thisAccount.getRingtone();
}
vibrate |= thisAccount.isVibrate();
}
}
if (notice.length() > 0)
{
NotificationManager notifMgr =
(NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notif = new Notification(R.drawable.stat_notify_email_generic,
getString(R.string.notification_new_title), System.currentTimeMillis());
// If only one account has mail, maybe go back to the old way of targetting the account.
Intent i = new Intent(context, Accounts.class);
PendingIntent pi = PendingIntent.getActivity(context, 0, i, 0);
notif.setLatestEventInfo(context, getString(R.string.notification_new_title),
notice, pi);
// notif.defaults = Notification.DEFAULT_LIGHTS;
notif.sound = TextUtils.isEmpty(ringtone) ? null : Uri.parse(ringtone);
if (vibrate) {
notif.defaults |= Notification.DEFAULT_VIBRATE;
}
notif.flags |= Notification.FLAG_SHOW_LIGHTS;
notif.ledARGB = Email.NOTIFICATION_LED_COLOR;
notif.ledOnMS = Email.NOTIFICATION_LED_ON_TIME;
notif.ledOffMS = Email.NOTIFICATION_LED_OFF_TIME;
notifMgr.notify(accountNumber, notif);
}
}
@Override @Override
public void checkMailFinished(Context context, Account account) { public void checkMailFinished(Context context, Account account) {
NotificationManager notifMgr = (NotificationManager)context
.getSystemService(Context.NOTIFICATION_SERVICE);
if (accountsWithNewMail.size() > 0) { Log.v(Email.LOG_TAG, "***** MailService *****: checkMailFinished");
Notification notif = new Notification(R.drawable.stat_notify_email_generic, try
getString(R.string.notification_new_title), System.currentTimeMillis()); {
boolean vibrate = false; checkMailDone(context, account);
String ringtone = null; }
if (accountsWithNewMail.size() > 1) { finally
for (Account account1 : accountsWithNewMail.keySet()) { {
if (account1.isVibrate()) vibrate = true; try
if (account1.isNotifyRingtone()) ringtone = account1.getRingtone(); {
} reschedule();
Intent i = new Intent(context, Accounts.class); }
PendingIntent pi = PendingIntent.getActivity(context, 0, i, 0); finally
notif.setLatestEventInfo(context, getString(R.string.notification_new_title), {
getString(R.string.notification_new_multi_account_fmt, wakeLockRelease();
accountsWithNewMail.size()), pi);
} else {
Account account1 = accountsWithNewMail.keySet().iterator().next();
int totalNewMails = accountsWithNewMail.get(account1);
Intent i = FolderMessageList.actionHandleAccountIntent(context, account1, Email.INBOX);
PendingIntent pi = PendingIntent.getActivity(context, 0, i, 0);
notif.setLatestEventInfo(context, getString(R.string.notification_new_title),
getString(R.string.notification_new_one_account_fmt, totalNewMails,
account1.getDescription()), pi);
vibrate = account1.isVibrate();
if (account1.isNotifyRingtone()) ringtone = account1.getRingtone();
}
notif.sound = TextUtils.isEmpty(ringtone) ? null : Uri.parse(ringtone);
if (vibrate) {
notif.defaults |= Notification.DEFAULT_VIBRATE;
}
notif.flags |= Notification.FLAG_SHOW_LIGHTS;
notif.ledARGB = Email.NOTIFICATION_LED_COLOR;
notif.ledOnMS = Email.NOTIFICATION_LED_ON_TIME;
notif.ledOffMS = Email.NOTIFICATION_LED_OFF_TIME;
notifMgr.notify(Email.NEW_EMAIL_NOTIFICATION_ID, notif);
} }
reschedule();
wakeLockRelease();
stopSelf(mStartId); stopSelf(mStartId);
}
} }
} }
} }