From 3dfabbbd0c4d0a090d82961f0c98ab86928acf82 Mon Sep 17 00:00:00 2001 From: Daniel Applebaum Date: Sun, 1 Feb 2009 00:08:14 +0000 Subject: [PATCH] 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. --- res/drawable/ic_menu_thread.png | Bin 0 -> 1626 bytes res/drawable/ic_menu_unthread.png | Bin 0 -> 1626 bytes res/menu/folder_message_list_option.xml | 12 +-- res/values/strings.xml | 6 +- .../android/email/MessagingController.java | 12 +++ .../email/activity/FolderMessageList.java | 92 +++++++++++++++++- 6 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 res/drawable/ic_menu_thread.png create mode 100644 res/drawable/ic_menu_unthread.png diff --git a/res/drawable/ic_menu_thread.png b/res/drawable/ic_menu_thread.png new file mode 100644 index 0000000000000000000000000000000000000000..196778f51fb8f5c92ad8de526a5bbe757baa3ae2 GIT binary patch literal 1626 zcmV-g2BrClP)1_^5ZvL0tN`4NA0O9mdT6|RkgM))Sdi1E_-p!jgdHC?5^E;QzaqHGCOw)Ak#p7`vJa_=W zjT<*8m&**C97yd|2%jY|H#f(fJ9j7)3csshQ|?$gKi>)g9{k7zW?{QNvejvOHv z45BCs%gf6wEG%&K>Qy93LQxb3P7XMSOioRTiN8)DgrKdhjlsb|jvqh1KR%GjWB^Dc z5?el#OeU#RD%`(+AAmq0Ks+8N7K;&yL`Wu+^!E1B*Vo6uzySTd{SNpIb3>dsaf0sd zZsPGc%gf8$y?d9*$;rKIAfL};7zUl4om487EuYhM9m}##W@cu1^5h9;&Yans2FAw5h(@F6y6*O3%W|2K z*Xu>sbreM*o6RDGKnUSHBg-?}bar;Kv9ZDJ+qd_o zfpWP_Fc?IVB&yY_bKiEx&5~SOTVr~9nx38>WLb8|dORLJE`D@@_aE{L%d+tKd}x}6 zEX!#W90iF30%zc-=D-6B8^fEFeh|UauF^ zH0kQ_!i9{kqqfrux1c5++XV0GPUIXpz z?Yw{g9w7vZqVV|fV^=6uRh3XEgs$s&JRUqA52sI`2H@hwi*~Gcf&cL@KZ>!jG5mf% zhGCG;=ecs_inIUv^=oElXPv#3m6hESICt(GqoboJib7Xc7v*x&*WCe%jmHdG+cQu~=+x1pIzKi9~{>r6t zP*s&usYG92-?rdu1ArZQN~KcWdygJH%FN6R<#L%&D71ZDvy+DA<;$1k^La|85`{v+ zi7t{P5eNiO6oqIs%K7u>8-m~M+DUn_qhjvDg$txoDP&njmSsnZk|ZI7pu4-<0pIMk z<67wK*|WTR_s;PH%d#jG3K)jLsZ*!6G@Y*{`CV0`xm*qapU;PBnyjv_x(c@aGM%s1 z57^~|?S$28m142j5d1Iy`9-uGfoiqtfVZzorP5IH&6;+6kqH@w;SjI^exLfj4(XeJ zzY#6x#I{uJBF(Za5^ae(@IU|SXVH>TD4WelNWwC!ws5E@Xn1KMaU zeOeNm^Fr;!qS;QYe6A9#B{jb|FEmZ7OKgprlBWB>R?@c#&HiszXqrYi943)SaQN_H zB9RD+qPQj&&5lCSPU`UVP+FL#iP!5T7!1d=Y0hg+$i7;x77D6CtGrfb zYI5qgeiVUW7_PSTK*Q)(W|SGpSl=bTvwnK>lju0sA<{!>VQXt~anUI;4%7#1K$}Tq zMlx1Mn--=M3N4kz~>@RC=?=Qy=dH(!4Dyjv)O|1_d zI&@%8+;FT?3*094;>8PoefO(-vfLgP<#L&DILxI>mjJkY`7%HJ`G=;BRkocAHS&qi z9lD>5{hkTb0!k01?TBU7Tn#~wPmH@>EJ+fhqod6;p)Zx{Za>g9jX*1}Z(r)a5!mDU YKi?4REIw;X0{{R307*qoM6N<$f_$tBOaK4? literal 0 HcmV?d00001 diff --git a/res/drawable/ic_menu_unthread.png b/res/drawable/ic_menu_unthread.png new file mode 100644 index 0000000000000000000000000000000000000000..196778f51fb8f5c92ad8de526a5bbe757baa3ae2 GIT binary patch literal 1626 zcmV-g2BrClP)1_^5ZvL0tN`4NA0O9mdT6|RkgM))Sdi1E_-p!jgdHC?5^E;QzaqHGCOw)Ak#p7`vJa_=W zjT<*8m&**C97yd|2%jY|H#f(fJ9j7)3csshQ|?$gKi>)g9{k7zW?{QNvejvOHv z45BCs%gf6wEG%&K>Qy93LQxb3P7XMSOioRTiN8)DgrKdhjlsb|jvqh1KR%GjWB^Dc z5?el#OeU#RD%`(+AAmq0Ks+8N7K;&yL`Wu+^!E1B*Vo6uzySTd{SNpIb3>dsaf0sd zZsPGc%gf8$y?d9*$;rKIAfL};7zUl4om487EuYhM9m}##W@cu1^5h9;&Yans2FAw5h(@F6y6*O3%W|2K z*Xu>sbreM*o6RDGKnUSHBg-?}bar;Kv9ZDJ+qd_o zfpWP_Fc?IVB&yY_bKiEx&5~SOTVr~9nx38>WLb8|dORLJE`D@@_aE{L%d+tKd}x}6 zEX!#W90iF30%zc-=D-6B8^fEFeh|UauF^ zH0kQ_!i9{kqqfrux1c5++XV0GPUIXpz z?Yw{g9w7vZqVV|fV^=6uRh3XEgs$s&JRUqA52sI`2H@hwi*~Gcf&cL@KZ>!jG5mf% zhGCG;=ecs_inIUv^=oElXPv#3m6hESICt(GqoboJib7Xc7v*x&*WCe%jmHdG+cQu~=+x1pIzKi9~{>r6t zP*s&usYG92-?rdu1ArZQN~KcWdygJH%FN6R<#L%&D71ZDvy+DA<;$1k^La|85`{v+ zi7t{P5eNiO6oqIs%K7u>8-m~M+DUn_qhjvDg$txoDP&njmSsnZk|ZI7pu4-<0pIMk z<67wK*|WTR_s;PH%d#jG3K)jLsZ*!6G@Y*{`CV0`xm*qapU;PBnyjv_x(c@aGM%s1 z57^~|?S$28m142j5d1Iy`9-uGfoiqtfVZzorP5IH&6;+6kqH@w;SjI^exLfj4(XeJ zzY#6x#I{uJBF(Za5^ae(@IU|SXVH>TD4WelNWwC!ws5E@Xn1KMaU zeOeNm^Fr;!qS;QYe6A9#B{jb|FEmZ7OKgprlBWB>R?@c#&HiszXqrYi943)SaQN_H zB9RD+qPQj&&5lCSPU`UVP+FL#iP!5T7!1d=Y0hg+$i7;x77D6CtGrfb zYI5qgeiVUW7_PSTK*Q)(W|SGpSl=bTvwnK>lju0sA<{!>VQXt~anUI;4%7#1K$}Tq zMlx1Mn--=M3N4kz~>@RC=?=Qy=dH(!4Dyjv)O|1_d zI&@%8+;FT?3*094;>8PoefO(-vfLgP<#L&DILxI>mjJkY`7%HJ`G=;BRkocAHS&qi z9lD>5{hkTb0!k01?TBU7Tn#~wPmH@>EJ+fhqod6;p)Zx{Za>g9jX*1}Z(r)a5!mDU YKi?4REIw;X0{{R307*qoM6N<$f_$tBOaK4? literal 0 HcmV?d00001 diff --git a/res/menu/folder_message_list_option.xml b/res/menu/folder_message_list_option.xml index 683fe8e74..f163c1c7e 100644 --- a/res/menu/folder_message_list_option.xml +++ b/res/menu/folder_message_list_option.xml @@ -28,10 +28,10 @@ android:title="@string/account_settings_action" android:icon="@android:drawable/ic_menu_preferences" /> - + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 70418d204..f9672fa07 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -60,6 +60,8 @@ Add attachment Dump settings Empty Trash + Thread + Unthread About Account options @@ -111,6 +113,8 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based * Keyboard shortcuts * Better IMAP support * Saving attachments to SD + * Empty Trash + * Threading * ...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. \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 - Zoom In\u000aG - Flag 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 diff --git a/src/com/android/email/MessagingController.java b/src/com/android/email/MessagingController.java index 87522d201..3cfbd0640 100644 --- a/src/com/android/email/MessagingController.java +++ b/src/com/android/email/MessagingController.java @@ -108,6 +108,8 @@ public class MessagingController implements Runnable { //private Set mListeners = Collections.synchronizedSet(new HashSet()); private Set mListeners = new CopyOnWriteArraySet(); + private boolean threading = false; + private MessagingListener checkMailListener = null; 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); } } + + public boolean isThreading() + { + return threading; + } + + public void setThreading(boolean threading) + { + this.threading = threading; + } } diff --git a/src/com/android/email/activity/FolderMessageList.java b/src/com/android/email/activity/FolderMessageList.java index 1c0e2f3d2..1b4ef2653 100644 --- a/src/com/android/email/activity/FolderMessageList.java +++ b/src/com/android/email/activity/FolderMessageList.java @@ -7,6 +7,8 @@ import java.util.Collections; import java.util.Date; import java.util.List; import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import android.app.ExpandableListActivity; import android.app.NotificationManager; @@ -125,6 +127,9 @@ public class FolderMessageList extends ExpandableListActivity private LayoutInflater mInflater; private Account mAccount; + + private Menu optionsMenu = null; + /** * Stores the name of the folder that we want to open as soon as possible @@ -143,6 +148,8 @@ public class FolderMessageList extends ExpandableListActivity private DateFormat dateFormat = null; private DateFormat timeFormat = null; + + private boolean thread = false; private DateFormat getDateFormat() { @@ -527,6 +534,7 @@ public class FolderMessageList extends ExpandableListActivity { final int expandedGroup = savedInstanceState.getInt( STATE_KEY_EXPANDED_GROUP, -1); + if (expandedGroup >= 0 && mAdapter.getGroupCount() > expandedGroup) { mListView.expandGroup(expandedGroup); @@ -573,6 +581,8 @@ public class FolderMessageList extends ExpandableListActivity NotificationManager notifMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 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_Q: { onAccounts(); return true; } case KeyEvent.KEYCODE_S: { onEditAccount(); return true; } + case KeyEvent.KEYCODE_T: { onToggleThread(); return true; } case KeyEvent.KEYCODE_H: { Toast toast = Toast.makeText(this, R.string.message_list_help_key, Toast.LENGTH_LONG); toast.show(); @@ -769,6 +780,20 @@ public class FolderMessageList extends ExpandableListActivity { 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) { @@ -879,6 +904,9 @@ public class FolderMessageList extends ExpandableListActivity case R.id.account_settings: onEditAccount(); return true; + case R.id.thread: + onToggleThread(); + return true; case R.id.empty_trash: onEmptyTrash(mAccount); return true; @@ -893,8 +921,24 @@ public class FolderMessageList extends ExpandableListActivity { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.folder_message_list_option, menu); + optionsMenu = menu; + setMenuThread(); 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 public boolean onContextItemSelected(MenuItem item) @@ -1864,6 +1908,8 @@ public class FolderMessageList extends ExpandableListActivity public String date; public Date compareDate; + + public String compareSubject; public String sender; @@ -1929,8 +1975,52 @@ public class FolderMessageList extends ExpandableListActivity 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