1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-10-31 23:55:08 -04:00

Provide folder threading support. When threading is on, messages in

folders are sorted first by subject, then by date.  Subject sorting is
case insensitive and disregards leading re:, fw: and fwd: prefixes.

Threading can be toggled via the T hotkey or the option menu.

The icons could use improvement.

Threading state is maintained during a run of K-9, among all
accounts.
This commit is contained in:
Daniel Applebaum 2009-02-01 00:08:14 +00:00
parent f184350aec
commit 3dfabbbd0c
6 changed files with 114 additions and 8 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -28,10 +28,10 @@
android:title="@string/account_settings_action" android:title="@string/account_settings_action"
android:icon="@android:drawable/ic_menu_preferences" android:icon="@android:drawable/ic_menu_preferences"
/> />
<!-- <item
<item android:id="@+id/search" android:id="@+id/thread"
android:title="@string/search_action" /> android:title="@string/thread_action"
<item android:id="@+id/preferences" android:icon="@drawable/ic_menu_thread"
android:title="@string/preferences_action" /> />
-->
</menu> </menu>

View File

@ -60,6 +60,8 @@
<string name="add_attachment_action">Add attachment</string> <string name="add_attachment_action">Add attachment</string>
<string name="dump_settings_action">Dump settings</string> <string name="dump_settings_action">Dump settings</string>
<string name="empty_trash_action">Empty Trash</string> <string name="empty_trash_action">Empty Trash</string>
<string name="thread_action">Thread</string>
<string name="unthread_action">Unthread</string>
<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>
@ -111,6 +113,8 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
* Keyboard shortcuts * Keyboard shortcuts
* Better IMAP support * Better IMAP support
* Saving attachments to SD * Saving attachments to SD
* Empty Trash
* Threading
* ...and more * ...and more
\nPlease note that K-9 does not support most free hotmail accounts and, like many email clients, has some quirks when talking to Microsoft Exchange. \nPlease note that K-9 does not support most free hotmail accounts and, like many email clients, has some quirks when talking to Microsoft Exchange.
\nSubmit bug reports, contribute new features and ask questions at http://code.google.com/p/k9mail \nSubmit bug reports, contribute new features and ask questions at http://code.google.com/p/k9mail
@ -371,6 +375,6 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
Message\u000AK, N - Next Message\u000AZ - Zoom Out\u000AShift-Z - Message\u000AK, N - Next Message\u000AZ - Zoom Out\u000AShift-Z -
Zoom In\u000aG - Flag</string> Zoom In\u000aG - Flag</string>
<string name="message_list_help_key">Del (or D) - Delete\u000AR - <string name="message_list_help_key">Del (or D) - Delete\u000AR -
Reply\u000AA - Reply All\u000AC - Compose\u000AF - Forward\u000aG - Flag\u000AQ Reply\u000AA - Reply All\u000AC - Compose\u000AF - Forward\u000aG - Flag\u000AT - Thread\u000AQ
- Return to Accounts</string> - Return to Accounts</string>
</resources> </resources>

View File

@ -108,6 +108,8 @@ public class MessagingController implements Runnable {
//private Set<MessagingListener> mListeners = Collections.synchronizedSet(new HashSet<MessagingListener>()); //private Set<MessagingListener> mListeners = Collections.synchronizedSet(new HashSet<MessagingListener>());
private Set<MessagingListener> mListeners = new CopyOnWriteArraySet<MessagingListener>(); private Set<MessagingListener> mListeners = new CopyOnWriteArraySet<MessagingListener>();
private boolean threading = false;
private MessagingListener checkMailListener = null; private MessagingListener checkMailListener = null;
private boolean mBusy; private boolean mBusy;
@ -2477,4 +2479,14 @@ s * critical data as fast as possible, and then we'll fill in the de
addListener(this.checkMailListener); addListener(this.checkMailListener);
} }
} }
public boolean isThreading()
{
return threading;
}
public void setThreading(boolean threading)
{
this.threading = threading;
}
} }

View File

@ -7,6 +7,8 @@ import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.app.ExpandableListActivity; import android.app.ExpandableListActivity;
import android.app.NotificationManager; import android.app.NotificationManager;
@ -126,6 +128,9 @@ public class FolderMessageList extends ExpandableListActivity
private Account mAccount; private Account mAccount;
private Menu optionsMenu = null;
/** /**
* Stores the name of the folder that we want to open as soon as possible * Stores the name of the folder that we want to open as soon as possible
* after load. It is set to null once the folder has been opened once. * after load. It is set to null once the folder has been opened once.
@ -144,6 +149,8 @@ public class FolderMessageList extends ExpandableListActivity
private DateFormat timeFormat = null; private DateFormat timeFormat = null;
private boolean thread = false;
private DateFormat getDateFormat() private DateFormat getDateFormat()
{ {
if (dateFormat == null) if (dateFormat == null)
@ -527,6 +534,7 @@ public class FolderMessageList extends ExpandableListActivity
{ {
final int expandedGroup = savedInstanceState.getInt( final int expandedGroup = savedInstanceState.getInt(
STATE_KEY_EXPANDED_GROUP, -1); STATE_KEY_EXPANDED_GROUP, -1);
if (expandedGroup >= 0 && mAdapter.getGroupCount() > expandedGroup) if (expandedGroup >= 0 && mAdapter.getGroupCount() > expandedGroup)
{ {
mListView.expandGroup(expandedGroup); mListView.expandGroup(expandedGroup);
@ -573,6 +581,8 @@ public class FolderMessageList extends ExpandableListActivity
NotificationManager notifMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager notifMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notifMgr.cancel(mAccount.getAccountNumber()); notifMgr.cancel(mAccount.getAccountNumber());
thread = MessagingController.getInstance(getApplication()).isThreading();
} }
@ -630,6 +640,7 @@ public class FolderMessageList extends ExpandableListActivity
case KeyEvent.KEYCODE_C: { onCompose(); return true;} case KeyEvent.KEYCODE_C: { onCompose(); return true;}
case KeyEvent.KEYCODE_Q: { onAccounts(); return true; } case KeyEvent.KEYCODE_Q: { onAccounts(); return true; }
case KeyEvent.KEYCODE_S: { onEditAccount(); return true; } case KeyEvent.KEYCODE_S: { onEditAccount(); return true; }
case KeyEvent.KEYCODE_T: { onToggleThread(); return true; }
case KeyEvent.KEYCODE_H: { case KeyEvent.KEYCODE_H: {
Toast toast = Toast.makeText(this, R.string.message_list_help_key, Toast.LENGTH_LONG); Toast toast = Toast.makeText(this, R.string.message_list_help_key, Toast.LENGTH_LONG);
toast.show(); toast.show();
@ -770,6 +781,20 @@ public class FolderMessageList extends ExpandableListActivity
MessageCompose.actionCompose(this, mAccount); MessageCompose.actionCompose(this, mAccount);
} }
private void onToggleThread()
{
thread = !thread;
for (FolderInfoHolder folder : mAdapter.mFolders)
{
Collections.sort(folder.messages);
}
mAdapter.notifyDataSetChanged();
setMenuThread();
MessagingController.getInstance(getApplication()).setThreading(thread);
}
private void onDelete(MessageInfoHolder holder) private void onDelete(MessageInfoHolder holder)
{ {
if (holder.read == false && holder.folder.unreadMessageCount > 0) if (holder.read == false && holder.folder.unreadMessageCount > 0)
@ -879,6 +904,9 @@ public class FolderMessageList extends ExpandableListActivity
case R.id.account_settings: case R.id.account_settings:
onEditAccount(); onEditAccount();
return true; return true;
case R.id.thread:
onToggleThread();
return true;
case R.id.empty_trash: case R.id.empty_trash:
onEmptyTrash(mAccount); onEmptyTrash(mAccount);
return true; return true;
@ -893,9 +921,25 @@ public class FolderMessageList extends ExpandableListActivity
{ {
super.onCreateOptionsMenu(menu); super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.folder_message_list_option, menu); getMenuInflater().inflate(R.menu.folder_message_list_option, menu);
optionsMenu = menu;
setMenuThread();
return true; return true;
} }
private void setMenuThread()
{
Menu menu = optionsMenu;
if (menu != null)
{
MenuItem threadItem = menu.findItem(R.id.thread);
if (threadItem != null)
{
threadItem.setTitle(thread ? R.string.unthread_action : R.string.thread_action);
threadItem.setIcon(thread ? R.drawable.ic_menu_unthread : R.drawable.ic_menu_thread);
}
}
}
@Override @Override
public boolean onContextItemSelected(MenuItem item) public boolean onContextItemSelected(MenuItem item)
{ {
@ -1865,6 +1909,8 @@ public class FolderMessageList extends ExpandableListActivity
public Date compareDate; public Date compareDate;
public String compareSubject;
public String sender; public String sender;
public String[] recipients; public String[] recipients;
@ -1929,8 +1975,52 @@ public class FolderMessageList extends ExpandableListActivity
public int compareTo(MessageInfoHolder o) public int compareTo(MessageInfoHolder o)
{ {
return this.compareDate.compareTo(o.compareDate) * -1; if (thread)
{
if (compareSubject == null)
{
compareSubject = stripPrefixes(subject).toLowerCase();
}
if (o.compareSubject == null)
{
o.compareSubject = stripPrefixes(o.subject).toLowerCase();
}
int subjCompare = this.compareSubject.compareTo(o.compareSubject);
if (subjCompare != 0)
{
return subjCompare;
}
}
return this.compareDate.compareTo(o.compareDate) * -1;
} }
Pattern pattern = null;
String patternString = "^ *(re|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 FolderViewHolder class FolderViewHolder