diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 94fa2c742..e880fb7c4 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -37,6 +37,18 @@ android:label="@string/remote_control_label" android:description="@string/remote_control_desc"/> + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 4b254f25c..fccc94841 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10,6 +10,10 @@ read Email attachments Allows this application to read your Email attachments. + read Emails + Allows this application to read your Emails. + Delete Emails + Allows this application to delete your Emails. About %s Accounts diff --git a/src/com/fsck/k9/K9.java b/src/com/fsck/k9/K9.java index 440b25e1a..dc30f1c6a 100644 --- a/src/com/fsck/k9/K9.java +++ b/src/com/fsck/k9/K9.java @@ -20,6 +20,7 @@ import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.internet.BinaryTempFileBody; +import com.fsck.k9.provider.MessageProvider; import com.fsck.k9.service.BootReceiver; import com.fsck.k9.service.MailService; @@ -252,6 +253,7 @@ public class K9 extends Application { public static final String ACTION_EMAIL_RECEIVED = "com.fsck.k9.intent.action.EMAIL_RECEIVED"; public static final String ACTION_EMAIL_DELETED = "com.fsck.k9.intent.action.EMAIL_DELETED"; + public static final String ACTION_REFRESH_OBSERVER = "com.fsck.k9.intent.action.REFRESH_OBSERVER"; public static final String EXTRA_ACCOUNT = "com.fsck.k9.intent.extra.ACCOUNT"; public static final String EXTRA_FOLDER = "com.fsck.k9.intent.extra.FOLDER"; public static final String EXTRA_SENT_DATE = "com.fsck.k9.intent.extra.SENT_DATE"; @@ -393,6 +395,8 @@ public class K9 extends Application K9.setK9Language(sprefs.getString("language", "")); K9.setK9Theme(sprefs.getInt("theme", android.R.style.Theme_Light)); MessagingController.getInstance(this).resetVisibleLimits(prefs.getAccounts()); + MessageProvider mp = new MessageProvider(); + mp.setApplication(this); /* * We have to give MimeMessage a temp directory because File.createTempFile(String, String) diff --git a/src/com/fsck/k9/activity/FolderInfoHolder.java b/src/com/fsck/k9/activity/FolderInfoHolder.java new file mode 100644 index 000000000..3907213d8 --- /dev/null +++ b/src/com/fsck/k9/activity/FolderInfoHolder.java @@ -0,0 +1,267 @@ +package com.fsck.k9.activity; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.text.Spannable; +import android.text.style.TextAppearanceSpan; +import android.util.Config; +import android.util.Log; +import android.util.TypedValue; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.AdapterView; +import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.BaseAdapter; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.ImageButton; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; +import android.text.format.DateFormat; + +import com.fsck.k9.Account; +import com.fsck.k9.AccountStats; +import com.fsck.k9.FontSizes; +import com.fsck.k9.K9; +import com.fsck.k9.Preferences; +import com.fsck.k9.R; +import com.fsck.k9.SearchSpecification; +import com.fsck.k9.activity.setup.AccountSettings; +import com.fsck.k9.activity.setup.FolderSettings; +import com.fsck.k9.activity.setup.Prefs; +import com.fsck.k9.controller.MessagingController; +import com.fsck.k9.controller.MessagingController.SORT_TYPE; +import com.fsck.k9.controller.MessagingListener; +import com.fsck.k9.helper.Utility; +import com.fsck.k9.mail.Address; +import com.fsck.k9.mail.Flag; +import com.fsck.k9.mail.Folder; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.Message.RecipientType; +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.store.LocalStore; +import com.fsck.k9.mail.store.LocalStore.LocalFolder; +import com.fsck.k9.mail.store.LocalStore.LocalMessage; + +public class FolderInfoHolder implements Comparable +{ + public String name; + public String displayName; + public long lastChecked; + public int unreadMessageCount; + public int flaggedMessageCount; + public boolean loading; + public String status; + public boolean lastCheckFailed; + public Folder folder; + public boolean pushActive; + + /** + * Outbox is handled differently from any other folder. + */ + public boolean outbox; + + @Override + public boolean equals(Object o) + { + return this.name.equals(((FolderInfoHolder)o).name); + } + + @Override + public int hashCode() + { + return name.hashCode(); + } + + public int compareTo(FolderInfoHolder o) + { + String s1 = this.name; + String s2 = o.name; + + int ret = s1.compareToIgnoreCase(s2); + if (ret != 0) + { + return ret; + } + else + { + return s1.compareTo(s2); + } + + } + + private String truncateStatus(String mess) + { + if (mess != null && mess.length() > 27) + { + mess = mess.substring(0, 27); + } + return mess; + } + + // constructor for an empty object for comparisons + public FolderInfoHolder() + { + } + + public FolderInfoHolder(Context context, Folder folder, Account account) + { + populate(context, folder, account); + } + + public FolderInfoHolder(Context context, Folder folder, Account mAccount, int unreadCount) + { + populate(context, folder, mAccount, unreadCount); + } + + public void populate(Context context, Folder folder, Account mAccount, int unreadCount) + { + + try + { + folder.open(Folder.OpenMode.READ_WRITE); + // unreadCount = folder.getUnreadMessageCount(); + } + catch (MessagingException me) + { + Log.e(K9.LOG_TAG, "Folder.getUnreadMessageCount() failed", me); + } + + this.name = folder.getName(); + + if (this.name.equalsIgnoreCase(K9.INBOX)) + { + this.displayName = context.getString(R.string.special_mailbox_name_inbox); + } + else + { + this.displayName = folder.getName(); + } + + if (this.name.equals(mAccount.getOutboxFolderName())) + { + this.displayName = String.format(context.getString(R.string.special_mailbox_name_outbox_fmt), this.name); + this.outbox = true; + } + + if (this.name.equals(mAccount.getDraftsFolderName())) + { + this.displayName = String.format(context.getString(R.string.special_mailbox_name_drafts_fmt), this.name); + } + + if (this.name.equals(mAccount.getTrashFolderName())) + { + this.displayName = String.format(context.getString(R.string.special_mailbox_name_trash_fmt), this.name); + } + + if (this.name.equals(mAccount.getSentFolderName())) + { + this.displayName = String.format(context.getString(R.string.special_mailbox_name_sent_fmt), this.name); + } + + if (this.name.equals(mAccount.getArchiveFolderName())) + { + this.displayName = String.format(context.getString(R.string.special_mailbox_name_archive_fmt), this.name); + } + + if (this.name.equals(mAccount.getSpamFolderName())) + { + this.displayName = String.format(context.getString(R.string.special_mailbox_name_spam_fmt), this.name); + } + + this.lastChecked = folder.getLastUpdate(); + + String mess = truncateStatus(folder.getStatus()); + + this.status = mess; + + this.unreadMessageCount = unreadCount; + + try + { + this.flaggedMessageCount = folder.getFlaggedMessageCount(); + } + catch (Exception e) + { + Log.e(K9.LOG_TAG, "Unable to get flaggedMessageCount", e); + } + + folder.close(); + + } + + + public void populate(Context context, Folder folder, Account account) + { + this.folder = folder; + this.name = folder.getName(); + + if (this.name.equalsIgnoreCase(K9.INBOX)) + { + this.displayName = context.getString(R.string.special_mailbox_name_inbox); + } + else + { + this.displayName = this.name; + } + + if (this.name.equals(account.getOutboxFolderName())) + { + this.displayName = String.format(context.getString(R.string.special_mailbox_name_outbox_fmt), this.name); + this.outbox = true; + } + + if (this.name.equals(account.getDraftsFolderName())) + { + this.displayName = String.format(context.getString(R.string.special_mailbox_name_drafts_fmt), this.name); + } + + if (this.name.equals(account.getTrashFolderName())) + { + this.displayName = String.format(context.getString(R.string.special_mailbox_name_trash_fmt), this.name); + } + + if (this.name.equals(account.getSentFolderName())) + { + this.displayName = String.format(context.getString(R.string.special_mailbox_name_sent_fmt), this.name); + } + + if (this.name.equals(account.getArchiveFolderName())) + { + this.displayName = String.format(context.getString(R.string.special_mailbox_name_archive_fmt), this.name); + } + + if (this.name.equals(account.getSpamFolderName())) + { + this.displayName = String.format(context.getString(R.string.special_mailbox_name_spam_fmt), this.name); + } + } +} diff --git a/src/com/fsck/k9/activity/FolderList.java b/src/com/fsck/k9/activity/FolderList.java index fb44ad6e6..ea2e447bd 100644 --- a/src/com/fsck/k9/activity/FolderList.java +++ b/src/com/fsck/k9/activity/FolderList.java @@ -20,6 +20,7 @@ import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; import com.fsck.k9.*; import com.fsck.k9.Account.FolderMode; +import com.fsck.k9.activity.FolderInfoHolder; import com.fsck.k9.activity.setup.Prefs; import com.fsck.k9.activity.setup.AccountSettings; import com.fsck.k9.activity.setup.FolderSettings; @@ -69,6 +70,7 @@ public class FolderList extends K9ListActivity private int mUnreadMessageCount; private FontSizes mFontSizes = K9.getFontSizes(); + private Context context; class FolderListHandler extends Handler { @@ -293,6 +295,8 @@ public class FolderList extends K9ListActivity mInflater = getLayoutInflater(); onNewIntent(getIntent()); + + context = this; } @Override @@ -776,16 +780,6 @@ public class FolderList extends K9ListActivity menu.setHeaderTitle(folder.displayName); } - private String truncateStatus(String mess) - { - if (mess != null && mess.length() > 27) - { - mess = mess.substring(0, 27); - } - - return mess; - } - class FolderListAdapter extends BaseAdapter { private ArrayList mFolders = new ArrayList(); @@ -932,11 +926,11 @@ public class FolderList extends K9ListActivity if (holder == null) { - holder = new FolderInfoHolder(folder, unreadMessageCount); + holder = new FolderInfoHolder(context, folder, mAccount, unreadMessageCount); } else { - holder.populate(folder, unreadMessageCount); + holder.populate(context, folder, mAccount, unreadMessageCount); } if (folder.isInTopGroup()) @@ -1039,7 +1033,7 @@ public class FolderList extends K9ListActivity FolderInfoHolder folderHolder = getFolder(folderName); if (folderHolder != null) { - folderHolder.populate(localFolder, unreadMessageCount); + folderHolder.populate(context, localFolder, mAccount, unreadMessageCount); mHandler.dataChanged(); } } @@ -1385,147 +1379,6 @@ public class FolderList extends K9ListActivity } - public class FolderInfoHolder implements Comparable - { - public String name; - - public String displayName; - - public long lastChecked; - - public int unreadMessageCount; - - public int flaggedMessageCount; - - public boolean loading; - - public String status; - - public boolean pushActive; - - public boolean lastCheckFailed; - - /** - * Outbox is handled differently from any other folder. - */ - public boolean outbox; - - - @Override - public boolean equals(Object o) - { - return this.name.equals(((FolderInfoHolder)o).name); - } - - @Override - public int hashCode() - { - return name.hashCode(); - } - - public int compareTo(FolderInfoHolder o) - { - String s1 = this.name; - String s2 = o.name; - - int ret = s1.compareToIgnoreCase(s2); - if (ret != 0) - { - return ret; - } - else - { - return s1.compareTo(s2); - } - - } - - // constructor for an empty object for comparisons - public FolderInfoHolder() - { - } - - public FolderInfoHolder(Folder folder, int unreadCount) - { - populate(folder, unreadCount); - } - public void populate(Folder folder, int unreadCount) - { - - try - { - folder.open(Folder.OpenMode.READ_WRITE); - // unreadCount = folder.getUnreadMessageCount(); - } - catch (MessagingException me) - { - Log.e(K9.LOG_TAG, "Folder.getUnreadMessageCount() failed", me); - } - - this.name = folder.getName(); - - if (this.name.equalsIgnoreCase(K9.INBOX)) - { - this.displayName = getString(R.string.special_mailbox_name_inbox); - } - else - { - this.displayName = folder.getName(); - } - - if (this.name.equals(mAccount.getOutboxFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_outbox_fmt), this.name); - this.outbox = true; - } - - if (this.name.equals(mAccount.getDraftsFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_drafts_fmt), this.name); - } - - if (this.name.equals(mAccount.getTrashFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_trash_fmt), this.name); - } - - if (this.name.equals(mAccount.getSentFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_sent_fmt), this.name); - } - - if (this.name.equals(mAccount.getArchiveFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_archive_fmt), this.name); - } - - if (this.name.equals(mAccount.getSpamFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_spam_fmt), this.name); - } - - this.lastChecked = folder.getLastUpdate(); - - String mess = truncateStatus(folder.getStatus()); - - this.status = mess; - - this.unreadMessageCount = unreadCount; - - try - { - this.flaggedMessageCount = folder.getFlaggedMessageCount(); - } - catch (Exception e) - { - Log.e(K9.LOG_TAG, "Unable to get flaggedMessageCount", e); - } - - folder.close(); - - } - } - class FolderViewHolder { public TextView folderName; diff --git a/src/com/fsck/k9/activity/MessageInfoHolder.java b/src/com/fsck/k9/activity/MessageInfoHolder.java new file mode 100644 index 000000000..8fe94873a --- /dev/null +++ b/src/com/fsck/k9/activity/MessageInfoHolder.java @@ -0,0 +1,310 @@ +package com.fsck.k9.activity; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.text.Spannable; +import android.text.style.TextAppearanceSpan; +import android.util.Config; +import android.util.Log; +import android.util.TypedValue; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.AdapterView; +import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.BaseAdapter; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.ImageButton; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; +import android.text.format.DateFormat; + +import com.fsck.k9.Account; +import com.fsck.k9.AccountStats; +import com.fsck.k9.FontSizes; +import com.fsck.k9.K9; +import com.fsck.k9.Preferences; +import com.fsck.k9.R; +import com.fsck.k9.SearchSpecification; +import com.fsck.k9.activity.setup.AccountSettings; +import com.fsck.k9.activity.setup.FolderSettings; +import com.fsck.k9.activity.setup.Prefs; +import com.fsck.k9.activity.FolderInfoHolder; +import com.fsck.k9.controller.MessagingController; +import com.fsck.k9.controller.MessagingController.SORT_TYPE; +import com.fsck.k9.controller.MessagingListener; +import com.fsck.k9.helper.Utility; +import com.fsck.k9.mail.Address; +import com.fsck.k9.mail.Flag; +import com.fsck.k9.mail.Folder; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.Message.RecipientType; +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.store.LocalStore; +import com.fsck.k9.mail.store.LocalStore.LocalFolder; +import com.fsck.k9.mail.store.LocalStore.LocalMessage; + +public class MessageInfoHolder implements Comparable +{ + public String subject; + public String date; + public String fullDate; + public Date compareDate; + public String compareSubject; + public String sender; + public String senderAddress; + public String compareCounterparty; + public String preview; + public String[] recipients; + public boolean hasAttachments; + public String uid; + public boolean read; + public boolean answered; + public boolean flagged; + public boolean downloaded; + public boolean partially_downloaded; + public Message message; + public FolderInfoHolder folder; + public boolean selected; + public String account; + public String uri; + + private SORT_TYPE sortType = SORT_TYPE.SORT_DATE; + + private boolean sortAscending = true; + private boolean sortDateAscending = false; + private MessagingController mController; + + // Empty constructor for comparison + public MessageInfoHolder() + { + this.selected = false; + } + + public MessageInfoHolder(Context context, Message m) + { + this(); + Account account = m.getFolder().getAccount(); + mController = MessagingController.getInstance(K9.app); + sortType = mController.getSortType(); + sortAscending = mController.isSortAscending(sortType); + sortDateAscending = mController.isSortAscending(SORT_TYPE.SORT_DATE); + populate(context, m, new FolderInfoHolder(context, m.getFolder(), m.getFolder().getAccount()), account); + } + + public MessageInfoHolder(Context context ,Message m, SORT_TYPE t_sort, boolean asc) + { + this(); + Account account = m.getFolder().getAccount(); + mController = MessagingController.getInstance(K9.app); + sortType = t_sort; + sortAscending = asc; + sortDateAscending = asc; + populate(context, m, new FolderInfoHolder(context, m.getFolder(), m.getFolder().getAccount()), account); + } + + public MessageInfoHolder(Context context, Message m, FolderInfoHolder folder, Account account) + { + this(); + mController = MessagingController.getInstance(K9.app); + sortType = mController.getSortType(); + sortAscending = mController.isSortAscending(sortType); + sortDateAscending = mController.isSortAscending(SORT_TYPE.SORT_DATE); + populate(context, m, folder, account); + } + + public void populate(Context context, Message m, FolderInfoHolder folder, Account account) + { + try + { + LocalMessage message = (LocalMessage) m; + Date date = message.getSentDate(); + this.compareDate = message.getSentDate(); + if (this.compareDate == null) + { + this.compareDate = message.getInternalDate(); + } + + this.folder = folder; + + if (Utility.isDateToday(date)) + { + this.date = android.text.format.DateFormat.getTimeFormat(context).format(date); + } + else + { + this.date = DateFormatter.getDateFormat(context).format(date); + } + + this.hasAttachments = message.getAttachmentCount() > 0; + + this.read = message.isSet(Flag.SEEN); + this.answered = message.isSet(Flag.ANSWERED); + this.flagged = message.isSet(Flag.FLAGGED); + this.downloaded = message.isSet(Flag.X_DOWNLOADED_FULL); + this.partially_downloaded = message.isSet(Flag.X_DOWNLOADED_PARTIAL); + + Address[] addrs = message.getFrom(); + + if (addrs.length > 0 && account.isAnIdentity(addrs[0])) + { + this.compareCounterparty = Address.toFriendly(message .getRecipients(RecipientType.TO)); + this.sender = String.format(context.getString(R.string.message_list_to_fmt), this.compareCounterparty); + } + else + { + this.sender = Address.toFriendly(addrs); + this.compareCounterparty = this.sender; + } + + if (addrs.length > 0) + { + this.senderAddress = addrs[0].getAddress(); + } + else + { + // a reasonable fallback "whomever we were corresponding with + this.senderAddress = this.compareCounterparty; + } + + this.subject = message.getSubject(); + + this.uid = message.getUid(); + this.message = m; + this.preview = message.getPreview(); + + this.fullDate = DateFormatter.getDateFormat(context).format(date)+" "+android.text.format.DateFormat.getTimeFormat(context).format(date); + this.account = account.getDescription(); + this.uri = "email://messages/"+account.getAccountNumber()+"/"+m.getFolder().getName()+"/"+m.getUid(); + + } + catch (MessagingException me) + { + if (Config.LOGV) + { + Log.v(K9.LOG_TAG, "Unable to load message info", me); + } + } + } + + @Override + public boolean equals(Object o) + { + if (o instanceof MessageInfoHolder == false) + { + return false; + } + MessageInfoHolder other = (MessageInfoHolder)o; + return message.equals(other.message); + } + + @Override + public int hashCode() + { + return uid.hashCode(); + } + + public int compareTo(MessageInfoHolder o) + { + int ascender = (sortAscending ? 1 : -1); + int comparison = 0; + + if (sortType == SORT_TYPE.SORT_SUBJECT) + { + if (compareSubject == null) + { + compareSubject = stripPrefixes(subject).toLowerCase(); + } + + if (o.compareSubject == null) + { + o.compareSubject = stripPrefixes(o.subject).toLowerCase(); + } + + comparison = this.compareSubject.compareTo(o.compareSubject); + } + else if (sortType == SORT_TYPE.SORT_SENDER) + { + comparison = this.compareCounterparty.toLowerCase().compareTo(o.compareCounterparty.toLowerCase()); + } + else if (sortType == SORT_TYPE.SORT_FLAGGED) + { + comparison = (this.flagged ? 0 : 1) - (o.flagged ? 0 : 1); + } + else if (sortType == SORT_TYPE.SORT_UNREAD) + { + comparison = (this.read ? 1 : 0) - (o.read ? 1 : 0); + } + else if (sortType == SORT_TYPE.SORT_ATTACHMENT) + { + comparison = (this.hasAttachments ? 0 : 1) - (o.hasAttachments ? 0 : 1); + } + + if (comparison != 0) + { + return comparison * ascender; + } + + int dateAscender = (sortDateAscending ? 1 : -1); + + return this.compareDate.compareTo(o.compareDate) * dateAscender; + } + + Pattern pattern = null; + String patternString = "^ *(re|aw|fw|fwd): *"; + private String stripPrefixes(String in) + { + synchronized (patternString) + { + if (pattern == null) + { + pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE); + } + } + + Matcher matcher = pattern.matcher(in); + + int lastPrefix = -1; + + while (matcher.find()) + { + lastPrefix = matcher.end(); + } + + if (lastPrefix > -1 && lastPrefix < in.length() - 1) + { + return in.substring(lastPrefix); + } + else + { + return in; + } + } +} diff --git a/src/com/fsck/k9/activity/MessageList.java b/src/com/fsck/k9/activity/MessageList.java index f02ac2995..eadbf99aa 100644 --- a/src/com/fsck/k9/activity/MessageList.java +++ b/src/com/fsck/k9/activity/MessageList.java @@ -44,6 +44,7 @@ import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; +import android.text.format.DateFormat; import com.fsck.k9.Account; import com.fsck.k9.AccountStats; @@ -68,7 +69,8 @@ import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.store.LocalStore; import com.fsck.k9.mail.store.LocalStore.LocalFolder; import com.fsck.k9.mail.store.LocalStore.LocalMessage; - +import com.fsck.k9.activity.MessageInfoHolder; +import com.fsck.k9.activity.FolderInfoHolder; /** * MessageList is the primary user interface for the program. This Activity @@ -159,6 +161,8 @@ public class MessageList private Bundle mState = null; private MessageInfoHolder mSelectedMessage = null; + private Context context = null; + class MessageListHandler { public void removeMessage(final List messages) @@ -437,6 +441,7 @@ public class MessageList mInflater = getLayoutInflater(); initializeLayout(); onNewIntent(getIntent()); + context=this; } @Override @@ -2029,7 +2034,7 @@ public class MessageList { if (updateForMe(account, folder)) { - m = new MessageInfoHolder(message); + m = new MessageInfoHolder(context, message); messagesToAdd.add(m); } else @@ -2042,7 +2047,7 @@ public class MessageList } else { - m = new MessageInfoHolder(message); + m = new MessageInfoHolder(context, message); messagesToAdd.add(m); } } @@ -2050,7 +2055,7 @@ public class MessageList } else { - m.populate(message, new FolderInfoHolder(message.getFolder(), account), account); + m.populate(context, message, new FolderInfoHolder(context, message.getFolder(), account), account); needsSort = true; } } @@ -2121,7 +2126,7 @@ public class MessageList { LocalStore localStore = account.getLocalStore(); local_folder = localStore.getFolder(folder); - return new FolderInfoHolder((Folder)local_folder, account); + return new FolderInfoHolder(context, (Folder)local_folder, account); } catch (Exception e) { @@ -2473,213 +2478,6 @@ public class MessageList } } - public class MessageInfoHolder implements Comparable - { - public String subject; - public String date; - public Date compareDate; - public String compareSubject; - public String sender; - public String senderAddress; - public String compareCounterparty; - public String preview; - public String[] recipients; - public boolean hasAttachments; - public String uid; - public boolean read; - public boolean answered; - public boolean flagged; - public boolean downloaded; - public boolean partially_downloaded; - public Message message; - public FolderInfoHolder folder; - public boolean selected; - - // Empty constructor for comparison - public MessageInfoHolder() - { - this.selected = false; - } - - public MessageInfoHolder(Message m) - { - this(); - Account account = m.getFolder().getAccount(); - populate(m, new FolderInfoHolder(m.getFolder(), m.getFolder().getAccount()), account); - } - - public MessageInfoHolder(Message m, FolderInfoHolder folder, Account account) - { - this(); - populate(m, folder, account); - } - - public void populate(Message m, FolderInfoHolder folder, Account account) - { - try - { - LocalMessage message = (LocalMessage) m; - Date date = message.getSentDate(); - this.compareDate = message.getSentDate(); - if (this.compareDate == null) - { - this.compareDate = message.getInternalDate(); - } - - this.folder = folder; - - if (Utility.isDateToday(date)) - { - this.date = getTimeFormat().format(date); - } - else - { - this.date = getDateFormat().format(date); - } - - this.hasAttachments = message.getAttachmentCount() > 0; - - this.read = message.isSet(Flag.SEEN); - this.answered = message.isSet(Flag.ANSWERED); - this.flagged = message.isSet(Flag.FLAGGED); - this.downloaded = message.isSet(Flag.X_DOWNLOADED_FULL); - this.partially_downloaded = message.isSet(Flag.X_DOWNLOADED_PARTIAL); - - Address[] addrs = message.getFrom(); - - if (addrs.length > 0 && account.isAnIdentity(addrs[0])) - { - this.compareCounterparty = Address.toFriendly(message .getRecipients(RecipientType.TO)); - this.sender = String.format(getString(R.string.message_list_to_fmt), this.compareCounterparty); - } - else - { - this.sender = Address.toFriendly(addrs); - this.compareCounterparty = this.sender; - } - - if (addrs.length > 0) - { - this.senderAddress = addrs[0].getAddress(); - } - else - { - // a reasonable fallback "whomever we were corresponding with - this.senderAddress = this.compareCounterparty; - } - - - this.subject = message.getSubject(); - - this.uid = message.getUid(); - this.message = m; - this.preview = message.getPreview(); - } - catch (MessagingException me) - { - if (Config.LOGV) - { - Log.v(K9.LOG_TAG, "Unable to load message info", me); - } - } - } - - @Override - public boolean equals(Object o) - { - if (o instanceof MessageInfoHolder == false) - { - return false; - } - MessageInfoHolder other = (MessageInfoHolder)o; - return message.equals(other.message); - } - - @Override - public int hashCode() - { - return uid.hashCode(); - } - - @Override - public int compareTo(MessageInfoHolder o) - { - int ascender = (sortAscending ? 1 : -1); - int comparison = 0; - - if (sortType == SORT_TYPE.SORT_SUBJECT) - { - if (compareSubject == null) - { - compareSubject = stripPrefixes(subject).toLowerCase(); - } - - if (o.compareSubject == null) - { - o.compareSubject = stripPrefixes(o.subject).toLowerCase(); - } - - comparison = this.compareSubject.compareTo(o.compareSubject); - } - else if (sortType == SORT_TYPE.SORT_SENDER) - { - comparison = this.compareCounterparty.toLowerCase().compareTo(o.compareCounterparty.toLowerCase()); - } - else if (sortType == SORT_TYPE.SORT_FLAGGED) - { - comparison = (this.flagged ? 0 : 1) - (o.flagged ? 0 : 1); - } - else if (sortType == SORT_TYPE.SORT_UNREAD) - { - comparison = (this.read ? 1 : 0) - (o.read ? 1 : 0); - } - else if (sortType == SORT_TYPE.SORT_ATTACHMENT) - { - comparison = (this.hasAttachments ? 0 : 1) - (o.hasAttachments ? 0 : 1); - } - - if (comparison != 0) - { - return comparison * ascender; - } - - int dateAscender = (sortDateAscending ? 1 : -1); - - return this.compareDate.compareTo(o.compareDate) * dateAscender; - } - - Pattern pattern = null; - String patternString = "^ *(re|aw|fw|fwd): *"; - private String stripPrefixes(String in) - { - synchronized (patternString) - { - if (pattern == null) - { - pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE); - } - } - - Matcher matcher = pattern.matcher(in); - - int lastPrefix = -1; - - while (matcher.find()) - { - lastPrefix = matcher.end(); - } - - if (lastPrefix > -1 && lastPrefix < in.length() - 1) - { - return in.substring(lastPrefix); - } - else - { - return in; - } - } - } - class MessageViewHolder implements OnCheckedChangeListener { @@ -2791,70 +2589,6 @@ public class MessageList public TextView main; } - public class FolderInfoHolder - { - public String name; - public String displayName; - public boolean loading; - public boolean lastCheckFailed; - public Folder folder; - - /** - * Outbox is handled differently from any other folder. - */ - public boolean outbox; - - public FolderInfoHolder(Folder folder, Account account) - { - populate(folder, account); - } - - public void populate(Folder folder, Account account) - { - this.folder = folder; - this.name = folder.getName(); - - if (this.name.equalsIgnoreCase(K9.INBOX)) - { - this.displayName = getString(R.string.special_mailbox_name_inbox); - } - else - { - this.displayName = this.name; - } - - if (this.name.equals(account.getOutboxFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_outbox_fmt), this.name); - this.outbox = true; - } - - if (this.name.equals(account.getDraftsFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_drafts_fmt), this.name); - } - - if (this.name.equals(account.getTrashFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_trash_fmt), this.name); - } - - if (this.name.equals(account.getSentFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_sent_fmt), this.name); - } - - if (this.name.equals(account.getArchiveFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_archive_fmt), this.name); - } - - if (this.name.equals(account.getSpamFolderName())) - { - this.displayName = String.format(getString(R.string.special_mailbox_name_spam_fmt), this.name); - } - } - } private boolean computeBatchDirection(boolean flagged) { diff --git a/src/com/fsck/k9/provider/MessageProvider.java b/src/com/fsck/k9/provider/MessageProvider.java new file mode 100644 index 000000000..41456e973 --- /dev/null +++ b/src/com/fsck/k9/provider/MessageProvider.java @@ -0,0 +1,390 @@ +package com.fsck.k9.provider; + +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.Date; +import java.text.SimpleDateFormat; + +import android.app.Application; +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.Context; +import android.content.res.Resources; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; +import android.util.Log; +import android.os.Bundle; +import android.util.Config; +import android.text.format.DateFormat; +import android.provider.Settings; +import android.content.Intent; + +import com.fsck.k9.Account; +import com.fsck.k9.AccountStats; +import com.fsck.k9.K9; +import com.fsck.k9.controller.MessagingController; +import com.fsck.k9.controller.MessagingListener; +import com.fsck.k9.Preferences; +import com.fsck.k9.mail.Flag; +import com.fsck.k9.mail.Folder; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.Address; +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.store.LocalStore; +import com.fsck.k9.SearchAccount; +import com.fsck.k9.Account; +import com.fsck.k9.mail.Message.RecipientType; +import com.fsck.k9.R; +import com.fsck.k9.mail.store.LocalStore.LocalMessage; +import com.fsck.k9.AccountStats; +import com.fsck.k9.helper.Utility; +import com.fsck.k9.controller.MessagingController.SORT_TYPE; +import com.fsck.k9.activity.DateFormatter; +import com.fsck.k9.activity.MessageInfoHolder; +import com.fsck.k9.controller.MessagingController.SORT_TYPE; + +public class MessageProvider extends ContentProvider +{ + + public static final String AUTHORITY = "com.fsck.k9.messageprovider"; + + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); + + private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); + + private static final int URI_INBOX_MESSAGES = 0; + private static final int URI_DELETE_MESSAGE = 1; + private static final int URI_ACCOUNTS = 2; + private static final int URI_ACCOUNT_UNREAD = 3; + + private static String mCurrentEmailAccount = ""; + private static Context context = null; + private static boolean mIsListenerRegister = false; + private static boolean inSync = false; + + private static Application mApp; + + private List glb_messages; + private static MatrixCursor mCursor; + + static + { + URI_MATCHER.addURI(AUTHORITY, "inbox_messages/", URI_INBOX_MESSAGES); + URI_MATCHER.addURI(AUTHORITY, "delete_message/", URI_DELETE_MESSAGE); + URI_MATCHER.addURI(AUTHORITY, "accounts", URI_ACCOUNTS); + URI_MATCHER.addURI(AUTHORITY, "account_unread/#", URI_ACCOUNT_UNREAD); + } + + static String[] messages_projection = new String[] + { + "id", + "date", + "sender", + "subject", + "preview", + "account", + "uri", + "delUri" + }; + MessagingListener mListener = new MessagingListener() + { + + public void messageDeleted(Account account, String folder, Message message) + { + } + + public void folderStatusChanged(Account account, String folderName, int unreadMessageCount) + { + if (inSync == false) + { + inSync = true; + glb_messages = new ArrayList(); + SearchAccount integratedInboxAccount = new SearchAccount(getContext(), true, null, null); + MessagingController msgController = MessagingController.getInstance(mApp); + msgController.searchLocalMessages(integratedInboxAccount, null, mListener); + } + } + + public void listLocalMessagesStarted(Account account, String folder) + { + } + + public void listLocalMessagesFinished(Account account, String folder) + { + } + + public void searchStats(AccountStats stats) + { + int id = -1; + Collections.sort(glb_messages); + MatrixCursor tmpCur = new MatrixCursor(messages_projection); + for (MessageInfoHolder mi : glb_messages) + { + ++id; + Message msg = mi.message; + tmpCur.addRow(new Object[] {id,mi.fullDate,mi.sender,mi.subject,mi.preview,mi.account,mi.uri,CONTENT_URI+"/delete_message/"+msg.getFolder().getAccount().getAccountNumber()+"/"+msg.getFolder().getName()+"/"+msg.getUid()}); + } + mCursor = tmpCur; + inSync=false; + notifyDatabaseModification(); + } + + public void listLocalMessagesAddMessages(Account account, String folder, List messages) + { +// We will by default sort by DATE desc + SORT_TYPE t_sort = SORT_TYPE.SORT_DATE; + + for (Message m : messages) + { + MessageInfoHolder m1 = new MessageInfoHolder(context,m,t_sort,false); + glb_messages.add(m1); + } + } + + }; + + public Cursor getAllAccounts() + { + String[] projection = new String[] { "accountNumber", "accountName" }; + + MatrixCursor ret = new MatrixCursor(projection); + + for (Account account : Preferences.getPreferences(getContext()).getAccounts()) + { + Object[] values = new Object[2]; + values[0] = account.getAccountNumber(); + values[1] = account.getDescription(); + ret.addRow(values); + } + + return ret; + } + + public Cursor getAccountUnread(int accountNumber) + { + String[] projection = new String[] { "accountName", "unread" }; + + MatrixCursor ret = new MatrixCursor(projection); + + Account myAccount; + AccountStats myAccountStats = null; + + Object[] values = new Object[2]; + + for (Account account : Preferences.getPreferences(getContext()).getAccounts()) + { + if (account.getAccountNumber()==accountNumber) + { + myAccount = account; + try + { + myAccountStats = account.getStats(getContext()); + values[0] = myAccount.getDescription(); + values[1] = myAccountStats.unreadMessageCount; + ret.addRow(values); + } + catch (MessagingException e) + { + Log.e(K9.LOG_TAG, e.getMessage()); + values[0] = "Unknown"; + values[1] = 0; + } + } + } + + return ret; + } + + public void setApplication(Application app) + { + + if (app != null) + { + mApp = app; + MessagingController msgController = MessagingController.getInstance(mApp); + if ((msgController != null) && (!mIsListenerRegister)) + { + msgController.addListener(mListener); + mIsListenerRegister = true; + } + } + + } + + @Override + public boolean onCreate() + { + context = getContext(); + + if (mApp != null) + { + MessagingController msgController = MessagingController.getInstance(mApp); + if ((msgController != null) && (!mIsListenerRegister)) + { + msgController.addListener(mListener); + mIsListenerRegister = true; + } + } + + return false; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) + { + + if (K9.DEBUG) + Log.d(K9.LOG_TAG, "delete"); + + if (mApp == null) + { + Log.d(K9.LOG_TAG, "K9 not ready"); + return 0; + } + + // Nota : can only delete a message + + List segments = null; + int accountId = -1; + String folderName = null; + String msgUid = null; + + segments = uri.getPathSegments(); + accountId = Integer.parseInt(segments.get(1)); + folderName = segments.get(2); + msgUid = segments.get(3); + + // get account + Account myAccount = null; + for (Account account : Preferences.getPreferences(getContext()).getAccounts()) + { + if (account.getAccountNumber() == accountId) + { + myAccount = account; + } + } + + // get localstore parameter + Message msg = null; + try + { + Folder lf = LocalStore.getLocalInstance(myAccount, mApp).getFolder(folderName); + int msgCount = lf.getMessageCount(); + if (K9.DEBUG) + Log.d(K9.LOG_TAG, "folder msg count = " + msgCount); + msg = lf.getMessage(msgUid); + } + catch (MessagingException e) + { + Log.e(K9.LOG_TAG, e.getMessage()); + } + + // launch command to delete the message + if ((myAccount != null) && (msg != null)) + { + MessagingController.getInstance(mApp).deleteMessages(new Message[] { msg }, mListener); + } + + return 0; + } + + @Override + public String getType(Uri uri) + { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) + { + return null; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) + { + + if (K9.DEBUG) + Log.d(K9.LOG_TAG, "query"); + + if (mApp == null) + { + Log.d(K9.LOG_TAG, "K9 not ready"); + return null; + } + + Cursor cursor; + switch (URI_MATCHER.match(uri)) + { + case URI_INBOX_MESSAGES: + + + if (mCursor == null) + { + mCursor = new MatrixCursor(messages_projection); + // new code for integrated inbox, only execute this once as it will be processed afterwards via the listener + glb_messages = new ArrayList(); + SearchAccount integratedInboxAccount = new SearchAccount(getContext(), true, null, null); + MessagingController msgController = MessagingController.getInstance(mApp); + msgController.searchLocalMessages(integratedInboxAccount, null, mListener); + } + + int id = -1; + +// Process messages + +//cursor = getAllMessages(projection, selection, selectionArgs, sortOrder); + cursor = (Cursor)mCursor; + break; + + case URI_ACCOUNTS: + cursor = getAllAccounts(); + break; + + case URI_ACCOUNT_UNREAD: + + List segments = null; + int accountId = -1; + segments = uri.getPathSegments(); + accountId = Integer.parseInt(segments.get(1)); + cursor = getAccountUnread(accountId); + break; + + default: + throw new IllegalStateException("Unrecognized URI:" + uri); + } + + return cursor; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) + { + + if (K9.DEBUG) + Log.d(K9.LOG_TAG, "update"); + +//TBD + + return 0; + } + + public static void notifyDatabaseModification() + { + + if (K9.DEBUG) + Log.d(K9.LOG_TAG, "notifyDatabaseModification -> UPDATE"); + + Intent intent = new Intent(K9.Intents.EmailReceived.ACTION_REFRESH_OBSERVER, null); + context.sendBroadcast(intent); + + context.getContentResolver().notifyChange(CONTENT_URI, null); + + } + +}