mirror of
https://github.com/moparisthebest/k-9
synced 2025-02-07 02:30:10 -05:00
Refactored message sorting code by extracting the sort code from the sorted object (this was a bad design, worsened by the MessageProvider patch). Ideally, new Comparator classes should get promoted to top-level classes and not be enclosed in MessageList.
Subject stripper backported from issue258 branch (Utility.java) The result is a cleaner MessageInfoHolder class.
This commit is contained in:
parent
39fd77591c
commit
4cb2d52c9c
@ -1,7 +1,5 @@
|
|||||||
package com.fsck.k9.activity;
|
package com.fsck.k9.activity;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
@ -11,19 +9,16 @@ import android.util.Log;
|
|||||||
import com.fsck.k9.Account;
|
import com.fsck.k9.Account;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.R;
|
import com.fsck.k9.R;
|
||||||
import com.fsck.k9.activity.FolderInfoHolder;
|
|
||||||
import com.fsck.k9.controller.MessagingController;
|
|
||||||
import com.fsck.k9.controller.MessagingController.SORT_TYPE;
|
|
||||||
import com.fsck.k9.helper.Contacts;
|
import com.fsck.k9.helper.Contacts;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.Message.RecipientType;
|
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
import com.fsck.k9.mail.Message.RecipientType;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
||||||
|
|
||||||
public class MessageInfoHolder implements Comparable<MessageInfoHolder>
|
public class MessageInfoHolder
|
||||||
{
|
{
|
||||||
public String subject;
|
public String subject;
|
||||||
public String date;
|
public String date;
|
||||||
@ -50,12 +45,6 @@ public class MessageInfoHolder implements Comparable<MessageInfoHolder>
|
|||||||
|
|
||||||
private Contacts mContacts;
|
private Contacts mContacts;
|
||||||
|
|
||||||
private SORT_TYPE sortType = SORT_TYPE.SORT_DATE;
|
|
||||||
|
|
||||||
private boolean sortAscending = true;
|
|
||||||
private boolean sortDateAscending = false;
|
|
||||||
private MessagingController mController;
|
|
||||||
|
|
||||||
// Empty constructor for comparison
|
// Empty constructor for comparison
|
||||||
public MessageInfoHolder()
|
public MessageInfoHolder()
|
||||||
{
|
{
|
||||||
@ -69,24 +58,6 @@ public class MessageInfoHolder implements Comparable<MessageInfoHolder>
|
|||||||
mContacts = Contacts.getInstance(context);
|
mContacts = Contacts.getInstance(context);
|
||||||
|
|
||||||
Account account = m.getFolder().getAccount();
|
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();
|
|
||||||
|
|
||||||
mContacts = Contacts.getInstance(context);
|
|
||||||
|
|
||||||
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);
|
populate(context, m, new FolderInfoHolder(context, m.getFolder(), m.getFolder().getAccount()), account);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,12 +67,7 @@ public class MessageInfoHolder implements Comparable<MessageInfoHolder>
|
|||||||
|
|
||||||
mContacts = Contacts.getInstance(context);
|
mContacts = Contacts.getInstance(context);
|
||||||
|
|
||||||
mController = MessagingController.getInstance(K9.app);
|
|
||||||
sortType = mController.getSortType();
|
|
||||||
sortAscending = mController.isSortAscending(sortType);
|
|
||||||
sortDateAscending = mController.isSortAscending(SORT_TYPE.SORT_DATE);
|
|
||||||
populate(context, m, folder, account);
|
populate(context, m, folder, account);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void populate(Context context, Message m, FolderInfoHolder folder, Account account)
|
public void populate(Context context, Message m, FolderInfoHolder folder, Account account)
|
||||||
@ -196,80 +162,4 @@ public class MessageInfoHolder implements Comparable<MessageInfoHolder>
|
|||||||
return uid.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@ package com.fsck.k9.activity;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Comparator;
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.Map;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
@ -20,34 +20,32 @@ import android.text.Spannable;
|
|||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.text.style.StyleSpan;
|
import android.text.style.StyleSpan;
|
||||||
import android.util.Config;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import android.view.ContextMenu.ContextMenuInfo;
|
|
||||||
import android.view.GestureDetector;
|
import android.view.GestureDetector;
|
||||||
import android.view.GestureDetector.SimpleOnGestureListener;
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
|
import android.view.ContextMenu.ContextMenuInfo;
|
||||||
|
import android.view.GestureDetector.SimpleOnGestureListener;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
|
||||||
import android.widget.BaseAdapter;
|
import android.widget.BaseAdapter;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import android.text.format.DateFormat;
|
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||||
|
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||||
|
|
||||||
import com.fsck.k9.Account;
|
import com.fsck.k9.Account;
|
||||||
import com.fsck.k9.AccountStats;
|
import com.fsck.k9.AccountStats;
|
||||||
@ -60,21 +58,14 @@ import com.fsck.k9.activity.setup.AccountSettings;
|
|||||||
import com.fsck.k9.activity.setup.FolderSettings;
|
import com.fsck.k9.activity.setup.FolderSettings;
|
||||||
import com.fsck.k9.activity.setup.Prefs;
|
import com.fsck.k9.activity.setup.Prefs;
|
||||||
import com.fsck.k9.controller.MessagingController;
|
import com.fsck.k9.controller.MessagingController;
|
||||||
import com.fsck.k9.controller.MessagingController.SORT_TYPE;
|
|
||||||
import com.fsck.k9.controller.MessagingListener;
|
import com.fsck.k9.controller.MessagingListener;
|
||||||
import com.fsck.k9.helper.Contacts;
|
import com.fsck.k9.controller.MessagingController.SORT_TYPE;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
import com.fsck.k9.mail.Address;
|
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.Message;
|
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;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
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
|
* MessageList is the primary user interface for the program. This Activity
|
||||||
@ -86,6 +77,144 @@ public class MessageList
|
|||||||
implements OnClickListener, AdapterView.OnItemClickListener
|
implements OnClickListener, AdapterView.OnItemClickListener
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverses the result of a {@link Comparator}.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
public static class ReverseComparator<T> implements Comparator<T>
|
||||||
|
{
|
||||||
|
private Comparator<T> mDelegate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param delegate
|
||||||
|
* Never <code>null</code>.
|
||||||
|
*/
|
||||||
|
public ReverseComparator(final Comparator<T> delegate)
|
||||||
|
{
|
||||||
|
mDelegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(final T object1, final T object2)
|
||||||
|
{
|
||||||
|
// arg1 & 2 are mixed up, this is done on purpose
|
||||||
|
return mDelegate.compare(object2, object1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chains comparator to find a non-0 result.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
public static class ComparatorChain<T> implements Comparator<T>
|
||||||
|
{
|
||||||
|
|
||||||
|
private List<Comparator<T>> mChain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param chain
|
||||||
|
* Comparator chain. Never <code>null</code>.
|
||||||
|
*/
|
||||||
|
public ComparatorChain(final List<Comparator<T>> chain)
|
||||||
|
{
|
||||||
|
mChain = chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(T object1, T object2)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
for (final Comparator<T> comparator : mChain)
|
||||||
|
{
|
||||||
|
result = comparator.compare(object1, object2);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AttachmentComparator implements Comparator<MessageInfoHolder>
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(MessageInfoHolder object1, MessageInfoHolder object2)
|
||||||
|
{
|
||||||
|
return (object1.hasAttachments ? 0 : 1) - (object2.hasAttachments ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FlaggedComparator implements Comparator<MessageInfoHolder>
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(MessageInfoHolder object1, MessageInfoHolder object2)
|
||||||
|
{
|
||||||
|
return (object1.flagged ? 0 : 1) - (object2.flagged ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UnreadComparator implements Comparator<MessageInfoHolder>
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(MessageInfoHolder object1, MessageInfoHolder object2)
|
||||||
|
{
|
||||||
|
return (object1.read ? 1 : 0) - (object2.read ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SenderComparator implements Comparator<MessageInfoHolder>
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(MessageInfoHolder object1, MessageInfoHolder object2)
|
||||||
|
{
|
||||||
|
return object1.compareCounterparty.toLowerCase().compareTo(object2.compareCounterparty.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DateComparator implements Comparator<MessageInfoHolder>
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(MessageInfoHolder object1, MessageInfoHolder object2)
|
||||||
|
{
|
||||||
|
return object1.compareDate.compareTo(object2.compareDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SubjectComparator implements Comparator<MessageInfoHolder>
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(MessageInfoHolder arg0, MessageInfoHolder arg1)
|
||||||
|
{
|
||||||
|
// XXX doesn't respect the Comparator contract since it alters the compared object
|
||||||
|
if (arg0.compareSubject == null)
|
||||||
|
{
|
||||||
|
arg0.compareSubject = Utility.stripSubject(arg0.subject);
|
||||||
|
}
|
||||||
|
if (arg1.compareSubject == null)
|
||||||
|
{
|
||||||
|
arg1.compareSubject = Utility.stripSubject(arg1.subject);
|
||||||
|
}
|
||||||
|
return arg0.compareSubject.compareToIgnoreCase(arg1.compareSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutable empty {@link Message} array
|
* Immutable empty {@link Message} array
|
||||||
*/
|
*/
|
||||||
@ -109,6 +238,27 @@ public class MessageList
|
|||||||
private static final String EXTRA_TITLE = "title";
|
private static final String EXTRA_TITLE = "title";
|
||||||
private static final String EXTRA_LIST_POSITION = "listPosition";
|
private static final String EXTRA_LIST_POSITION = "listPosition";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a {@link SORT_TYPE} to a {@link Comparator} implementation.
|
||||||
|
*/
|
||||||
|
private static final Map<SORT_TYPE, Comparator<MessageInfoHolder>> SORT_COMPARATORS;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
// fill the mapping at class time loading
|
||||||
|
|
||||||
|
final Map<SORT_TYPE, Comparator<MessageInfoHolder>> map = new EnumMap<SORT_TYPE, Comparator<MessageInfoHolder>>(SORT_TYPE.class);
|
||||||
|
map.put(SORT_TYPE.SORT_ATTACHMENT, new AttachmentComparator());
|
||||||
|
map.put(SORT_TYPE.SORT_DATE, new DateComparator());
|
||||||
|
map.put(SORT_TYPE.SORT_FLAGGED, new FlaggedComparator());
|
||||||
|
map.put(SORT_TYPE.SORT_SENDER, new SenderComparator());
|
||||||
|
map.put(SORT_TYPE.SORT_SUBJECT, new SubjectComparator());
|
||||||
|
map.put(SORT_TYPE.SORT_UNREAD, new UnreadComparator());
|
||||||
|
|
||||||
|
// make it immutable to prevent accidental alteration (content is immutable already)
|
||||||
|
SORT_COMPARATORS = Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
private ListView mListView;
|
private ListView mListView;
|
||||||
|
|
||||||
private boolean mTouchView = true;
|
private boolean mTouchView = true;
|
||||||
@ -211,7 +361,7 @@ public class MessageList
|
|||||||
int index;
|
int index;
|
||||||
synchronized (mAdapter.messages)
|
synchronized (mAdapter.messages)
|
||||||
{
|
{
|
||||||
index = Collections.binarySearch(mAdapter.messages, message);
|
index = Collections.binarySearch(mAdapter.messages, message, getComparator());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
@ -264,19 +414,64 @@ public class MessageList
|
|||||||
|
|
||||||
private void sortMessages()
|
private void sortMessages()
|
||||||
{
|
{
|
||||||
|
final Comparator<MessageInfoHolder> chainComparator = getComparator();
|
||||||
|
|
||||||
runOnUiThread(new Runnable()
|
runOnUiThread(new Runnable()
|
||||||
{
|
{
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
synchronized (mAdapter.messages)
|
synchronized (mAdapter.messages)
|
||||||
{
|
{
|
||||||
Collections.sort(mAdapter.messages);
|
Collections.sort(mAdapter.messages, chainComparator);
|
||||||
}
|
}
|
||||||
mAdapter.notifyDataSetChanged();
|
mAdapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The comparator to use to display messages in an ordered
|
||||||
|
* fashion. Never <code>null</code>.
|
||||||
|
*/
|
||||||
|
protected Comparator<MessageInfoHolder> getComparator()
|
||||||
|
{
|
||||||
|
final List<Comparator<MessageInfoHolder>> chain = new ArrayList<Comparator<MessageInfoHolder>>(2 /* we add 2 comparators at most */ );
|
||||||
|
|
||||||
|
{
|
||||||
|
// add the specified comparator
|
||||||
|
final Comparator<MessageInfoHolder> comparator = SORT_COMPARATORS.get(sortType);
|
||||||
|
if (sortAscending)
|
||||||
|
{
|
||||||
|
chain.add(comparator);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chain.add(new ReverseComparator<MessageInfoHolder>(comparator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// add the date comparator if not already specified
|
||||||
|
if (sortType != SORT_TYPE.SORT_DATE)
|
||||||
|
{
|
||||||
|
final Comparator<MessageInfoHolder> comparator = SORT_COMPARATORS.get(SORT_TYPE.SORT_DATE);
|
||||||
|
if (sortDateAscending)
|
||||||
|
{
|
||||||
|
chain.add(comparator);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chain.add(new ReverseComparator<MessageInfoHolder>(comparator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the comparator chain
|
||||||
|
final Comparator<MessageInfoHolder> chainComparator = new ComparatorChain<MessageInfoHolder>(chain);
|
||||||
|
|
||||||
|
return chainComparator;
|
||||||
|
}
|
||||||
|
|
||||||
public void folderLoading(String folder, boolean loading)
|
public void folderLoading(String folder, boolean loading)
|
||||||
{
|
{
|
||||||
if (mCurrentFolder != null && mCurrentFolder.name.equals(folder))
|
if (mCurrentFolder != null && mCurrentFolder.name.equals(folder))
|
||||||
|
@ -11,9 +11,26 @@ import java.io.InputStream;
|
|||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class Utility
|
public class Utility
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// \u00A0 (non-breaking space) happens to be used by French MUA
|
||||||
|
|
||||||
|
// Note: no longer using the ^ beginning character combined with (...)+
|
||||||
|
// repetition matching as we might want to strip ML tags. Ex:
|
||||||
|
// Re: [foo] Re: RE : [foo] blah blah blah
|
||||||
|
private static final Pattern RESPONSE_PATTERN = Pattern.compile(
|
||||||
|
"((Re|Fw|Fwd|Aw|R\\u00E9f\\.)(\\[\\d+\\])?[\\u00A0 ]?: *)+", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mailing-list tag pattern to match strings like "[foobar] "
|
||||||
|
*/
|
||||||
|
private static final Pattern TAG_PATTERN = Pattern.compile("\\[[-_a-z0-9]+\\] ",
|
||||||
|
Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
public final static String readInputStream(InputStream in, String encoding) throws IOException
|
public final static String readInputStream(InputStream in, String encoding) throws IOException
|
||||||
{
|
{
|
||||||
InputStreamReader reader = new InputStreamReader(in, encoding);
|
InputStreamReader reader = new InputStreamReader(in, encoding);
|
||||||
@ -384,4 +401,99 @@ public class Utility
|
|||||||
|
|
||||||
return wrappedLine.toString();
|
return wrappedLine.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the 'original' subject value, by ignoring leading
|
||||||
|
* response/forward marker and '[XX]' formatted tags (as many mailing-list
|
||||||
|
* softwares do).
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Result is also trimmed.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param subject
|
||||||
|
* Never <code>null</code>.
|
||||||
|
* @return Never <code>null</code>.
|
||||||
|
*/
|
||||||
|
public static String stripSubject(final String subject)
|
||||||
|
{
|
||||||
|
int lastPrefix = 0;
|
||||||
|
|
||||||
|
final Matcher tagMatcher = TAG_PATTERN.matcher(subject);
|
||||||
|
String tag = null;
|
||||||
|
// whether tag stripping logic should be active
|
||||||
|
boolean tagPresent = false;
|
||||||
|
// whether the last action stripped a tag
|
||||||
|
boolean tagStripped = false;
|
||||||
|
if (tagMatcher.find(0))
|
||||||
|
{
|
||||||
|
tagPresent = true;
|
||||||
|
if (tagMatcher.start() == 0)
|
||||||
|
{
|
||||||
|
// found at beginning of subject, considering it an actual tag
|
||||||
|
tag = tagMatcher.group();
|
||||||
|
|
||||||
|
// now need to find response marker after that tag
|
||||||
|
lastPrefix = tagMatcher.end();
|
||||||
|
tagStripped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Matcher matcher = RESPONSE_PATTERN.matcher(subject);
|
||||||
|
|
||||||
|
// while:
|
||||||
|
// - lastPrefix is within the bounds
|
||||||
|
// - response marker found at lastPrefix position
|
||||||
|
// (to make sure we don't catch response markers that are part of
|
||||||
|
// the actual subject)
|
||||||
|
|
||||||
|
while (lastPrefix < subject.length() - 1
|
||||||
|
&& matcher.find(lastPrefix)
|
||||||
|
&& matcher.start() == lastPrefix
|
||||||
|
&& (!tagPresent || tag == null || subject.regionMatches(matcher.end(), tag, 0,
|
||||||
|
tag.length())))
|
||||||
|
{
|
||||||
|
lastPrefix = matcher.end();
|
||||||
|
|
||||||
|
if (tagPresent)
|
||||||
|
{
|
||||||
|
tagStripped = false;
|
||||||
|
if (tag == null)
|
||||||
|
{
|
||||||
|
// attempt to find tag
|
||||||
|
if (tagMatcher.start() == lastPrefix)
|
||||||
|
{
|
||||||
|
tag = tagMatcher.group();
|
||||||
|
lastPrefix += tag.length();
|
||||||
|
tagStripped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (lastPrefix < subject.length() - 1 && subject.startsWith(tag, lastPrefix))
|
||||||
|
{
|
||||||
|
// Re: [foo] Re: [foo] blah blah blah
|
||||||
|
// ^ ^
|
||||||
|
// ^ ^
|
||||||
|
// ^ new position
|
||||||
|
// ^
|
||||||
|
// initial position
|
||||||
|
lastPrefix += tag.length();
|
||||||
|
tagStripped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tagStripped)
|
||||||
|
{
|
||||||
|
// restore the last tag
|
||||||
|
lastPrefix -= tag.length();
|
||||||
|
}
|
||||||
|
if (lastPrefix > -1 && lastPrefix < subject.length() - 1)
|
||||||
|
{
|
||||||
|
return subject.substring(lastPrefix).trim();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return subject.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@ import com.fsck.k9.K9;
|
|||||||
import com.fsck.k9.Preferences;
|
import com.fsck.k9.Preferences;
|
||||||
import com.fsck.k9.SearchAccount;
|
import com.fsck.k9.SearchAccount;
|
||||||
import com.fsck.k9.activity.MessageInfoHolder;
|
import com.fsck.k9.activity.MessageInfoHolder;
|
||||||
|
import com.fsck.k9.activity.MessageList;
|
||||||
import com.fsck.k9.controller.MessagingController;
|
import com.fsck.k9.controller.MessagingController;
|
||||||
import com.fsck.k9.controller.MessagingController.SORT_TYPE;
|
|
||||||
import com.fsck.k9.controller.MessagingListener;
|
import com.fsck.k9.controller.MessagingListener;
|
||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
@ -107,7 +107,8 @@ public class MessageProvider extends ContentProvider
|
|||||||
int id = -1;
|
int id = -1;
|
||||||
synchronized (glb_messages)
|
synchronized (glb_messages)
|
||||||
{
|
{
|
||||||
Collections.sort(glb_messages);
|
// TODO dynamically retrieve sort order instead of hardcoding
|
||||||
|
Collections.sort(glb_messages, new MessageList.ReverseComparator<MessageInfoHolder>(new MessageList.DateComparator()));
|
||||||
}
|
}
|
||||||
MatrixCursor tmpCur = new MatrixCursor(messages_projection);
|
MatrixCursor tmpCur = new MatrixCursor(messages_projection);
|
||||||
synchronized (glb_messages)
|
synchronized (glb_messages)
|
||||||
@ -137,12 +138,9 @@ public class MessageProvider extends ContentProvider
|
|||||||
|
|
||||||
public void listLocalMessagesAddMessages(Account account, String folder, List<Message> messages)
|
public void listLocalMessagesAddMessages(Account account, String folder, List<Message> messages)
|
||||||
{
|
{
|
||||||
// We will by default sort by DATE desc
|
|
||||||
SORT_TYPE t_sort = SORT_TYPE.SORT_DATE;
|
|
||||||
|
|
||||||
for (Message m : messages)
|
for (Message m : messages)
|
||||||
{
|
{
|
||||||
MessageInfoHolder m1 = new MessageInfoHolder(context,m,t_sort,false);
|
MessageInfoHolder m1 = new MessageInfoHolder(context, m);
|
||||||
glb_messages.add(m1);
|
glb_messages.add(m1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user