diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d6f8aa48a..2a951fb0e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -402,6 +402,11 @@ otherwise it would make K-9 start at the wrong time
android:readPermission="com.fsck.k9.permission.READ_MESSAGES"
android:writePermission="com.fsck.k9.permission.DELETE_MESSAGES"
/>
+
{
+
+ private static final String[] PROJECTION = {
+ MessageColumns.ID,
+ MessageColumns.UID,
+ MessageColumns.INTERNAL_DATE,
+ MessageColumns.SUBJECT,
+ MessageColumns.DATE,
+ MessageColumns.SENDER_LIST,
+ MessageColumns.TO_LIST,
+ MessageColumns.CC_LIST,
+ MessageColumns.FLAGS,
+ MessageColumns.ATTACHMENT_COUNT,
+ MessageColumns.FOLDER_ID,
+ MessageColumns.PREVIEW,
+ MessageColumns.THREAD_ROOT,
+ MessageColumns.THREAD_PARENT
+ };
+
+ private static final int ID_COLUMN = 0;
+ private static final int UID_COLUMN = 1;
+ private static final int INTERNAL_DATE_COLUMN = 2;
+ private static final int SUBJECT_COLUMN = 3;
+ private static final int DATE_COLUMN = 4;
+ private static final int SENDER_LIST_COLUMN = 5;
+ private static final int TO_LIST_COLUMN = 6;
+ private static final int CC_LIST_COLUMN = 7;
+ private static final int FLAGS_COLUMN = 8;
+ private static final int ATTACHMENT_COUNT_COLUMN = 9;
+ private static final int FOLDER_ID_COLUMN = 10;
+ private static final int PREVIEW_COLUMN = 11;
+ private static final int THREAD_ROOT_COLUMN = 12;
+ private static final int THREAD_PARENT_COLUMN = 13;
+
public static MessageListFragment newInstance(Account account, String folderName) {
MessageListFragment fragment = new MessageListFragment();
@@ -283,11 +323,6 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
}
- /**
- * Immutable empty {@link Message} array
- */
- private static final Message[] EMPTY_MESSAGE_ARRAY = new Message[0];
-
private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;
@@ -376,6 +411,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
private boolean mSenderAboveSubject = false;
private int mSelectedCount = 0;
+ private SparseBooleanArray mSelected;
private FontSizes mFontSizes = K9.getFontSizes();
@@ -391,7 +427,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
* chosen messages between user interactions (eg. Selecting a folder for
* move operation)
*/
- private List mActiveMessages;
+ private Message[] mActiveMessages;
/* package visibility for faster inner class access */
MessageHelper mMessageHelper;
@@ -412,6 +448,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
private Context mContext;
+ private final ActivityListener mListener = new MessageListActivityListener();
/**
* This class is used to run operations that modify UI elements in the UI thread.
@@ -423,20 +460,12 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
* perform the operation in the calling thread.
*/
class MessageListHandler extends Handler {
- private static final int ACTION_REMOVE_MESSAGE = 1;
- private static final int ACTION_RESET_UNREAD_COUNT = 2;
- private static final int ACTION_SORT_MESSAGES = 3;
- private static final int ACTION_FOLDER_LOADING = 4;
- private static final int ACTION_REFRESH_TITLE = 5;
- private static final int ACTION_PROGRESS = 6;
+ private static final int ACTION_SORT_MESSAGES = 1;
+ private static final int ACTION_FOLDER_LOADING = 2;
+ private static final int ACTION_REFRESH_TITLE = 3;
+ private static final int ACTION_PROGRESS = 4;
- public void removeMessage(MessageReference messageReference) {
- android.os.Message msg = android.os.Message.obtain(this, ACTION_REMOVE_MESSAGE,
- messageReference);
- sendMessage(msg);
- }
-
public void sortMessages() {
android.os.Message msg = android.os.Message.obtain(this, ACTION_SORT_MESSAGES);
sendMessage(msg);
@@ -469,43 +498,9 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
});
}
- public void changeMessageUid(final MessageReference ref, final String newUid) {
- // Instead of explicitly creating a container to be able to pass both arguments in a
- // Message we post a Runnable to the message queue.
- post(new Runnable() {
- @Override
- public void run() {
- mAdapter.changeMessageUid(ref, newUid);
- }
- });
- }
-
- public void addOrUpdateMessages(final Account account, final String folderName,
- final List providedMessages, final boolean verifyAgainstSearch) {
- // We copy the message list because it's later modified by MessagingController
- final List messages = new ArrayList(providedMessages);
-
- post(new Runnable() {
- @Override
- public void run() {
- mAdapter.addOrUpdateMessages(account, folderName, messages,
- verifyAgainstSearch);
- }
- });
- }
-
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
- case ACTION_REMOVE_MESSAGE: {
- MessageReference messageReference = (MessageReference) msg.obj;
- mAdapter.removeMessage(messageReference);
- break;
- }
- case ACTION_RESET_UNREAD_COUNT: {
- mAdapter.resetUnreadCount();
- break;
- }
case ACTION_SORT_MESSAGES: {
mAdapter.sortMessages();
break;
@@ -581,10 +576,10 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
private void setWindowProgress() {
int level = Window.PROGRESS_END;
- if (mCurrentFolder != null && mCurrentFolder.loading && mAdapter.mListener.getFolderTotal() > 0) {
- int divisor = mAdapter.mListener.getFolderTotal();
+ if (mCurrentFolder != null && mCurrentFolder.loading && mListener.getFolderTotal() > 0) {
+ int divisor = mListener.getFolderTotal();
if (divisor != 0) {
- level = (Window.PROGRESS_END / divisor) * (mAdapter.mListener.getFolderCompleted()) ;
+ level = (Window.PROGRESS_END / divisor) * (mListener.getFolderCompleted()) ;
if (level > Window.PROGRESS_END) {
level = Window.PROGRESS_END;
}
@@ -603,7 +598,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
mFragmentListener.setMessageListTitle(displayName);
- String operation = mAdapter.mListener.getOperation(activity, getTimeFormat()).trim();
+ String operation = mListener.getOperation(activity, getTimeFormat()).trim();
if (operation.length() < 1) {
mFragmentListener.setMessageListSubTitle(mAccount.getEmail());
} else {
@@ -664,13 +659,13 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
public void onItemClick(AdapterView> parent, View view, int position, long id) {
if (view == mFooterView) {
if (mCurrentFolder != null && !mRemoteSearch) {
- mController.loadMoreMessages(mAccount, mFolderName, mAdapter.mListener);
- } else if (mRemoteSearch && mAdapter.mExtraSearchResults != null && mAdapter.mExtraSearchResults.size() > 0 && mSearchAccount != null) {
+ mController.loadMoreMessages(mAccount, mFolderName, null);
+ } /*else if (mRemoteSearch && mAdapter.mExtraSearchResults != null && mAdapter.mExtraSearchResults.size() > 0 && mSearchAccount != null) {
int numResults = mAdapter.mExtraSearchResults.size();
Context appContext = getActivity().getApplicationContext();
Account account = Preferences.getPreferences(appContext).getAccount(mSearchAccount);
if (account == null) {
- mHandler.updateFooter("", false);
+ updateFooter("", false);
return;
}
int limit = account.getRemoteSearchNumResults();
@@ -680,22 +675,26 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
mAdapter.mExtraSearchResults = mAdapter.mExtraSearchResults.subList(limit, mAdapter.mExtraSearchResults.size());
} else {
mAdapter.mExtraSearchResults = null;
- mHandler.updateFooter("", false);
+ updateFooter("", false);
}
mController.loadSearchResults(account, mSearchFolder, toProcess, mAdapter.mListener);
- }
+ }*/
return;
}
- final MessageInfoHolder message = (MessageInfoHolder) parent.getItemAtPosition(position);
+ Cursor cursor = (Cursor) parent.getItemAtPosition(position);
if (mSelectedCount > 0) {
- toggleMessageSelect(message);
- } else if (message.threadCount > 1) {
- Folder folder = message.message.getFolder();
- long rootId = ((LocalMessage) message.message).getRootId();
- mFragmentListener.showThread(folder.getAccount(), folder.getName(), rootId);
+ toggleMessageSelect(position);
+// } else if (message.threadCount > 1) {
+// Folder folder = message.message.getFolder();
+// long rootId = ((LocalMessage) message.message).getRootId();
+// mFragmentListener.showThread(folder.getAccount(), folder.getName(), rootId);
} else {
- onOpenMessage(message);
+ MessageReference ref = new MessageReference();
+ ref.accountUuid = mAccount.getUuid();
+ ref.folderName = mCurrentFolder.name;
+ ref.uid = cursor.getString(UID_COLUMN);
+ onOpenMessage(ref);
}
}
@@ -749,6 +748,8 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
mMessageHelper = MessageHelper.getInstance(getActivity());
initializeMessageList();
+
+ getLoaderManager().initLoader(0, null, this);
}
private void decodeArguments() {
@@ -794,7 +795,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
mAdapter = new MessageListAdapter();
if (mFolderName != null) {
- mCurrentFolder = mAdapter.getFolder(mFolderName, mAccount);
+ mCurrentFolder = getFolder(mFolderName, mAccount);
}
// Hide "Load up to x more" footer for search views
@@ -804,10 +805,26 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
mListView.setAdapter(mAdapter);
}
+ private FolderInfoHolder getFolder(String folder, Account account) {
+ LocalFolder local_folder = null;
+ try {
+ LocalStore localStore = account.getLocalStore();
+ local_folder = localStore.getFolder(folder);
+ return new FolderInfoHolder(mContext, local_folder, account);
+ } catch (Exception e) {
+ Log.e(K9.LOG_TAG, "getFolder(" + folder + ") goes boom: ", e);
+ return null;
+ } finally {
+ if (local_folder != null) {
+ local_folder.close();
+ }
+ }
+ }
+
@Override
public void onPause() {
super.onPause();
- mController.removeListener(mAdapter.mListener);
+ mController.removeListener(mListener);
saveListState();
}
@@ -894,7 +911,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
mPullToRefreshView.setMode(PullToRefreshBase.Mode.DISABLED);
}
- mController.addListener(mAdapter.mListener);
+ mController.addListener(mListener);
//Cancel pending new mail notifications when we open an account
Account[] accountsWithNotification;
@@ -917,6 +934,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
mController.notifyAccountCancel(appContext, accountWithNotification);
}
+ /*
if (mAdapter.isEmpty()) {
if (mRemoteSearch) {
//TODO: Support flag based search
@@ -964,9 +982,10 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
.start();
}
}
+ */
if (mAccount != null && mFolderName != null && !mRemoteSearch) {
- mController.getFolderUnreadMessageCount(mAccount, mFolderName, mAdapter.mListener);
+ mController.getFolderUnreadMessageCount(mAccount, mFolderName, mListener);
}
refreshTitle();
@@ -980,12 +999,13 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
mListView.setScrollingCacheEnabled(false);
mListView.setOnItemClickListener(this);
mListView.addFooterView(getFooterView(mListView));
+ //mListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
registerForContextMenu(mListView);
}
- private void onOpenMessage(MessageInfoHolder message) {
- mFragmentListener.openMessage(message.message.makeMessageReference());
+ private void onOpenMessage(MessageReference reference) {
+ mFragmentListener.openMessage(reference);
}
public void onCompose() {
@@ -1000,20 +1020,20 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
}
}
- public void onReply(MessageInfoHolder holder) {
- mFragmentListener.onReply(holder.message);
+ public void onReply(Message message) {
+ mFragmentListener.onReply(message);
}
- public void onReplyAll(MessageInfoHolder holder) {
- mFragmentListener.onReplyAll(holder.message);
+ public void onReplyAll(Message message) {
+ mFragmentListener.onReplyAll(message);
}
- public void onForward(MessageInfoHolder holder) {
- mFragmentListener.onForward(holder.message);
+ public void onForward(Message message) {
+ mFragmentListener.onForward(message);
}
- public void onResendMessage(MessageInfoHolder holder) {
- mFragmentListener.onResendMessage(holder.message);
+ public void onResendMessage(Message message) {
+ mFragmentListener.onResendMessage(message);
}
public void changeSort(SortType sortType) {
@@ -1117,17 +1137,12 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
changeSort(sorts[curIndex]);
}
- /**
- * @param holders
- * Never {@code null}.
- */
- private void onDelete(final List holders) {
- final List messagesToRemove = new ArrayList();
- for (MessageInfoHolder holder : holders) {
- messagesToRemove.add(holder.message);
- }
- mAdapter.removeMessages(holders);
- mController.deleteMessages(messagesToRemove.toArray(EMPTY_MESSAGE_ARRAY), null);
+ private void onDelete(Message message) {
+ onDelete(new Message[] { message });
+ }
+
+ private void onDelete(Message[] messages) {
+ mController.deleteMessages(messages, null);
}
@Override
@@ -1144,22 +1159,22 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
}
final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER);
- final List holders = mActiveMessages;
+ final Message[] messages = mActiveMessages;
if (destFolderName != null) {
mActiveMessages = null; // don't need it any more
- final Account account = holders.get(0).message.getFolder().getAccount();
+ final Account account = messages[0].getFolder().getAccount();
account.setLastSelectedFolderName(destFolderName);
switch (requestCode) {
case ACTIVITY_CHOOSE_FOLDER_MOVE:
- move(holders, destFolderName);
+ move(messages, destFolderName);
break;
case ACTIVITY_CHOOSE_FOLDER_COPY:
- copy(holders, destFolderName);
+ copy(messages, destFolderName);
break;
}
}
@@ -1184,7 +1199,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
case R.id.dialog_confirm_spam: {
String title = getString(R.string.dialog_confirm_spam_title);
- int selectionSize = mActiveMessages.size();
+ int selectionSize = mActiveMessages.length;
String message = getResources().getQuantityString(
R.plurals.dialog_confirm_spam_message, selectionSize,
Integer.valueOf(selectionSize));
@@ -1242,7 +1257,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
return true;
}
case R.id.select_all: {
- setSelectionState(true);
+ selectAll();
return true;
}
}
@@ -1271,110 +1286,144 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
}
public void onSendPendingMessages() {
- mController.sendPendingMessages(mAccount, mAdapter.mListener);
+ mController.sendPendingMessages(mAccount, null);
}
@Override
public boolean onContextItemSelected(android.view.MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
- final MessageInfoHolder message = (MessageInfoHolder) mListView.getItemAtPosition(info.position);
+ int adapterPosition = listViewToAdapterPosition(info.position);
+ Message message = getMessageAtPosition(adapterPosition);
-
- final List selection = getSelectionFromMessage(message);
- switch (item.getItemId()) {
- case R.id.reply: {
- onReply(message);
- break;
- }
- case R.id.reply_all: {
- onReplyAll(message);
- break;
- }
- case R.id.forward: {
- onForward(message);
- break;
- }
- case R.id.send_again: {
- onResendMessage(message);
- mSelectedCount = 0;
- break;
- }
- case R.id.same_sender: {
- mFragmentListener.showMoreFromSameSender(message.senderAddress);
- break;
- }
- case R.id.delete: {
- onDelete(selection);
- break;
- }
- case R.id.mark_as_read: {
- setFlag(selection, Flag.SEEN, true);
- break;
- }
- case R.id.mark_as_unread: {
- setFlag(selection, Flag.SEEN, false);
- break;
- }
- case R.id.flag: {
- setFlag(selection, Flag.FLAGGED, true);
- break;
- }
- case R.id.unflag: {
- setFlag(selection, Flag.FLAGGED, false);
- break;
- }
-
- // only if the account supports this
- case R.id.archive: {
- onArchive(selection);
- break;
- }
- case R.id.spam: {
- onSpam(selection);
- break;
- }
- case R.id.move: {
- onMove(selection);
- break;
- }
- case R.id.copy: {
- onCopy(selection);
- break;
+ switch (item.getItemId()) {
+ case R.id.reply: {
+ onReply(message);
+ break;
+ }
+ case R.id.reply_all: {
+ onReplyAll(message);
+ break;
+ }
+ case R.id.forward: {
+ onForward(message);
+ break;
+ }
+ case R.id.send_again: {
+ onResendMessage(message);
+ mSelectedCount = 0;
+ break;
+ }
+ case R.id.same_sender: {
+ Cursor cursor = (Cursor) mAdapter.getItem(adapterPosition);
+ String senderAddress = getSenderAddressFromCursor(cursor);
+ if (senderAddress != null) {
+ mFragmentListener.showMoreFromSameSender(senderAddress);
}
+ break;
+ }
+ case R.id.delete: {
+ onDelete(message);
+ break;
+ }
+ case R.id.mark_as_read: {
+ setFlag(message, Flag.SEEN, true);
+ break;
+ }
+ case R.id.mark_as_unread: {
+ setFlag(message, Flag.SEEN, false);
+ break;
+ }
+ case R.id.flag: {
+ setFlag(message, Flag.FLAGGED, true);
+ break;
+ }
+ case R.id.unflag: {
+ setFlag(message, Flag.FLAGGED, false);
+ break;
}
- return true;
+ // only if the account supports this
+ case R.id.archive: {
+ onArchive(message);
+ break;
+ }
+ case R.id.spam: {
+ onSpam(message);
+ break;
+ }
+ case R.id.move: {
+ onMove(message);
+ break;
+ }
+ case R.id.copy: {
+ onCopy(message);
+ break;
+ }
}
+ return true;
+ }
+
+
+ private String getSenderAddressFromCursor(Cursor cursor) {
+ String fromList = cursor.getString(SENDER_LIST_COLUMN);
+ Address[] fromAddrs = Address.unpack(fromList);
+ return (fromAddrs.length > 0) ? fromAddrs[0].getAddress() : null;
+ }
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
- MessageInfoHolder message = (MessageInfoHolder) mListView.getItemAtPosition(info.position);
+ Cursor cursor = (Cursor) mListView.getItemAtPosition(info.position);
- if (message == null) {
+ if (cursor == null) {
return;
}
getActivity().getMenuInflater().inflate(R.menu.message_list_item_context, menu);
- menu.setHeaderTitle(message.message.getSubject());
+ //TODO: get account from cursor
+ Account account = mAccount;
- if (message.read) {
+ String subject = cursor.getString(SUBJECT_COLUMN);
+ String flagList = cursor.getString(FLAGS_COLUMN);
+ String[] flags = flagList.split(",");
+ boolean read = false;
+ boolean flagged = false;
+ for (int i = 0, len = flags.length; i < len; i++) {
+ try {
+ switch (Flag.valueOf(flags[i])) {
+ case SEEN: {
+ read = true;
+ break;
+ }
+ case FLAGGED: {
+ flagged = true;
+ break;
+ }
+ default: {
+ // We don't care about the other flags
+ }
+ }
+ } catch (Exception e) { /* ignore */ }
+ }
+
+ menu.setHeaderTitle(subject);
+
+ if (read) {
menu.findItem(R.id.mark_as_read).setVisible(false);
} else {
menu.findItem(R.id.mark_as_unread).setVisible(false);
}
- if (message.flagged) {
+ if (flagged) {
menu.findItem(R.id.flag).setVisible(false);
} else {
menu.findItem(R.id.unflag).setVisible(false);
}
- Account account = message.message.getFolder().getAccount();
if (!mController.isCopyCapable(account)) {
menu.findItem(R.id.copy).setVisible(false);
}
@@ -1413,209 +1462,155 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
private void handleSwipe(final MotionEvent downMotion, final boolean selected) {
int[] listPosition = new int[2];
mListView.getLocationOnScreen(listPosition);
- int position = mListView.pointToPosition((int) downMotion.getRawX() - listPosition[0], (int) downMotion.getRawY() - listPosition[1]);
- if (position != AdapterView.INVALID_POSITION) {
- final MessageInfoHolder message = (MessageInfoHolder) mListView.getItemAtPosition(position);
- toggleMessageSelect(message);
+
+ int listX = (int) downMotion.getRawX() - listPosition[0];
+ int listY = (int) downMotion.getRawY() - listPosition[1];
+
+ int listViewPosition = mListView.pointToPosition(listX, listY);
+
+ toggleMessageSelect(listViewPosition);
+ }
+
+ private int listViewToAdapterPosition(int position) {
+ if (position > 0 && position <= mAdapter.getCount()) {
+ return position - 1;
+ }
+
+ return AdapterView.INVALID_POSITION;
+ }
+
+ class MessageListActivityListener extends ActivityListener {
+ @Override
+ public void remoteSearchFailed(Account acct, String folder, final String err) {
+ //TODO: Better error handling
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(getActivity(), err, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+
+ @Override
+ public void remoteSearchStarted(Account acct, String folder) {
+ mHandler.progress(true);
+ mHandler.updateFooter(mContext.getString(R.string.remote_search_sending_query), true);
+ }
+
+
+ @Override
+ public void remoteSearchFinished(Account acct, String folder, int numResults, List extraResults) {
+ mHandler.progress(false);
+ if (extraResults != null && extraResults.size() > 0) {
+ mHandler.updateFooter(String.format(mContext.getString(R.string.load_more_messages_fmt), acct.getRemoteSearchNumResults()), false);
+ } else {
+ mHandler.updateFooter("", false);
+ }
+ mFragmentListener.setMessageListProgress(Window.PROGRESS_END);
+
+ }
+
+ @Override
+ public void remoteSearchServerQueryComplete(Account account, String folderName, int numResults) {
+ mHandler.progress(true);
+ if (account != null && account.getRemoteSearchNumResults() != 0 && numResults > account.getRemoteSearchNumResults()) {
+ mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading_limited, account.getRemoteSearchNumResults(), numResults), true);
+ } else {
+ mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading, numResults), true);
+ }
+ mFragmentListener.setMessageListProgress(Window.PROGRESS_START);
+ }
+
+ @Override
+ public void informUserOfStatus() {
+ mHandler.refreshTitle();
+ }
+
+ @Override
+ public void synchronizeMailboxStarted(Account account, String folder) {
+ if (updateForMe(account, folder)) {
+ mHandler.progress(true);
+ mHandler.folderLoading(folder, true);
+ }
+ super.synchronizeMailboxStarted(account, folder);
+ }
+
+ @Override
+ public void synchronizeMailboxFinished(Account account, String folder,
+ int totalMessagesInMailbox, int numNewMessages) {
+
+ if (updateForMe(account, folder)) {
+ mHandler.progress(false);
+ mHandler.folderLoading(folder, false);
+ mHandler.sortMessages();
+ }
+ super.synchronizeMailboxFinished(account, folder, totalMessagesInMailbox, numNewMessages);
+ }
+
+ @Override
+ public void synchronizeMailboxFailed(Account account, String folder, String message) {
+
+ if (updateForMe(account, folder)) {
+ mHandler.progress(false);
+ mHandler.folderLoading(folder, false);
+ mHandler.sortMessages();
+ }
+ super.synchronizeMailboxFailed(account, folder, message);
+ }
+
+ @Override
+ public void listLocalMessagesStarted(Account account, String folder) {
+ if ((mQueryString != null && folder == null) || (account != null && account.equals(mAccount))) {
+ mHandler.progress(true);
+ if (folder != null) {
+ mHandler.folderLoading(folder, true);
+ }
+ }
+ }
+
+ @Override
+ public void listLocalMessagesFailed(Account account, String folder, String message) {
+ if ((mQueryString != null && folder == null) || (account != null && account.equals(mAccount))) {
+ mHandler.sortMessages();
+ mHandler.progress(false);
+ if (folder != null) {
+ mHandler.folderLoading(folder, false);
+ }
+ }
+ }
+
+ @Override
+ public void listLocalMessagesFinished(Account account, String folder) {
+ if ((mQueryString != null && folder == null) || (account != null && account.equals(mAccount))) {
+ mHandler.sortMessages();
+ mHandler.progress(false);
+ if (folder != null) {
+ mHandler.folderLoading(folder, false);
+ }
+ }
+ }
+
+ @Override
+ public void searchStats(AccountStats stats) {
+ mUnreadMessageCount = stats.unreadMessageCount;
+ super.searchStats(stats);
+ }
+
+ @Override
+ public void folderStatusChanged(Account account, String folder, int unreadMessageCount) {
+ if (updateForMe(account, folder)) {
+ mUnreadMessageCount = unreadMessageCount;
+ }
+ super.folderStatusChanged(account, folder, unreadMessageCount);
+ }
+
+ private boolean updateForMe(Account account, String folder) {
+ return ((account.equals(mAccount) && folder.equals(mFolderName)));
}
}
- class MessageListAdapter extends BaseAdapter {
- private final List mMessages =
- Collections.synchronizedList(new ArrayList());
- public List mExtraSearchResults;
-
- private final ActivityListener mListener = new ActivityListener() {
-
- @Override
- public void remoteSearchAddMessage(Account account, String folderName, Message message, final int numDone, final int numTotal) {
-
- if (numTotal > 0 && numDone < numTotal) {
- mFragmentListener.setMessageListProgress(Window.PROGRESS_END / numTotal * numDone);
- } else {
- mFragmentListener.setMessageListProgress(Window.PROGRESS_END);
- }
-
- mHandler.addOrUpdateMessages(account, folderName, Collections.singletonList(message), false);
- }
-
- @Override
- public void remoteSearchFailed(Account acct, String folder, final String err) {
- //TODO: Better error handling
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(getActivity(), err, Toast.LENGTH_LONG).show();
- }
- });
- }
-
- @Override
- public void remoteSearchStarted(Account acct, String folder) {
- mHandler.progress(true);
- mHandler.updateFooter(mContext.getString(R.string.remote_search_sending_query), true);
- }
-
-
- @Override
- public void remoteSearchFinished(Account acct, String folder, int numResults, List extraResults) {
- mHandler.progress(false);
- if (extraResults != null && extraResults.size() > 0) {
- mExtraSearchResults = extraResults;
- mHandler.updateFooter(String.format(mContext.getString(R.string.load_more_messages_fmt), acct.getRemoteSearchNumResults()), false);
- } else {
- mHandler.updateFooter("", false);
- }
- mFragmentListener.setMessageListProgress(Window.PROGRESS_END);
-
- }
-
- @Override
- public void remoteSearchServerQueryComplete(Account account, String folderName, int numResults) {
- mHandler.progress(true);
- if (account != null && account.getRemoteSearchNumResults() != 0 && numResults > account.getRemoteSearchNumResults()) {
- mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading_limited, account.getRemoteSearchNumResults(), numResults), true);
- } else {
- mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading, numResults), true);
- }
- mFragmentListener.setMessageListProgress(Window.PROGRESS_START);
- }
-
- @Override
- public void informUserOfStatus() {
- mHandler.refreshTitle();
- }
-
- @Override
- public void synchronizeMailboxStarted(Account account, String folder) {
- if (updateForMe(account, folder)) {
- mHandler.progress(true);
- mHandler.folderLoading(folder, true);
- }
- super.synchronizeMailboxStarted(account, folder);
- }
-
- @Override
- public void synchronizeMailboxFinished(Account account, String folder,
- int totalMessagesInMailbox, int numNewMessages) {
-
- if (updateForMe(account, folder)) {
- mHandler.progress(false);
- mHandler.folderLoading(folder, false);
- mHandler.sortMessages();
- }
- super.synchronizeMailboxFinished(account, folder, totalMessagesInMailbox, numNewMessages);
- }
-
- @Override
- public void synchronizeMailboxFailed(Account account, String folder, String message) {
-
- if (updateForMe(account, folder)) {
- mHandler.progress(false);
- mHandler.folderLoading(folder, false);
- mHandler.sortMessages();
- }
- super.synchronizeMailboxFailed(account, folder, message);
- }
-
- @Override
- public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder, Message message) {
- mHandler.addOrUpdateMessages(account, folder, Collections.singletonList(message), true);
- }
-
- @Override
- public void synchronizeMailboxRemovedMessage(Account account, String folder, Message message) {
- mHandler.removeMessage(message.makeMessageReference());
- }
-
- @Override
- public void listLocalMessagesStarted(Account account, String folder) {
- if ((mQueryString != null && folder == null) || (account != null && account.equals(mAccount))) {
- mHandler.progress(true);
- if (folder != null) {
- mHandler.folderLoading(folder, true);
- }
- }
- }
-
- @Override
- public void listLocalMessagesFailed(Account account, String folder, String message) {
- if ((mQueryString != null && folder == null) || (account != null && account.equals(mAccount))) {
- mHandler.sortMessages();
- mHandler.progress(false);
- if (folder != null) {
- mHandler.folderLoading(folder, false);
- }
- }
- }
-
- @Override
- public void listLocalMessagesFinished(Account account, String folder) {
- if ((mQueryString != null && folder == null) || (account != null && account.equals(mAccount))) {
- mHandler.sortMessages();
- mHandler.progress(false);
- if (folder != null) {
- mHandler.folderLoading(folder, false);
- }
- }
- }
-
- @Override
- public void listLocalMessagesRemoveMessage(Account account, String folder, Message message) {
- mHandler.removeMessage(message.makeMessageReference());
- }
-
- @Override
- public void listLocalMessagesAddMessages(Account account, String folder, List messages) {
- mHandler.addOrUpdateMessages(account, folder, messages, false);
- }
-
- @Override
- public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {
- mHandler.addOrUpdateMessages(account, folder, Collections.singletonList(message), false);
- }
-
- @Override
- public void searchStats(AccountStats stats) {
- mUnreadMessageCount = stats.unreadMessageCount;
- super.searchStats(stats);
- }
-
- @Override
- public void folderStatusChanged(Account account, String folder, int unreadMessageCount) {
- if (updateForMe(account, folder)) {
- mUnreadMessageCount = unreadMessageCount;
- }
- super.folderStatusChanged(account, folder, unreadMessageCount);
- }
-
- @Override
- public void messageUidChanged(Account account, String folder, String oldUid, String newUid) {
- MessageReference ref = new MessageReference();
- ref.accountUuid = account.getUuid();
- ref.folderName = folder;
- ref.uid = oldUid;
-
- mHandler.changeMessageUid(ref, newUid);
- }
- };
-
- private boolean updateForMe(Account account, String folder) {
- if ((account.equals(mAccount) && mFolderName != null && folder.equals(mFolderName))) {
- return true;
- } else {
- return false;
- }
- }
-
- public List getMessages() {
- return mMessages;
- }
-
- public void restoreMessages(List messages) {
- mMessages.addAll(messages);
- }
+ class MessageListAdapter extends CursorAdapter {
private Drawable mAttachmentIcon;
private Drawable mForwardedIcon;
@@ -1623,576 +1618,33 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
private Drawable mForwardedAnsweredIcon;
MessageListAdapter() {
+ super(getActivity(), null, 0);
mAttachmentIcon = getResources().getDrawable(R.drawable.ic_email_attachment_small);
mAnsweredIcon = getResources().getDrawable(R.drawable.ic_email_answered_small);
mForwardedIcon = getResources().getDrawable(R.drawable.ic_email_forwarded_small);
mForwardedAnsweredIcon = getResources().getDrawable(R.drawable.ic_email_forwarded_answered_small);
}
- public void markAllMessagesAsDirty() {
- for (MessageInfoHolder holder : mMessages) {
- holder.dirty = true;
- }
- }
-
- public void pruneDirtyMessages() {
- List messagesToRemove = new ArrayList();
-
- for (MessageInfoHolder holder : mMessages) {
- if (holder.dirty) {
- messagesToRemove.add(holder);
- }
- }
- removeMessages(messagesToRemove);
- }
-
- public void removeMessage(MessageReference messageReference) {
- MessageInfoHolder holder = getMessage(messageReference);
- if (holder == null) {
- Log.w(K9.LOG_TAG, "Got callback to remove non-existent message with UID " +
- messageReference.uid);
- } else {
- removeMessages(Collections.singletonList(holder));
- }
- }
-
- public void removeMessages(final List messages) {
- if (messages.isEmpty()) {
- return;
- }
-
- for (MessageInfoHolder message : messages) {
- if (message != null && (mFolderName == null || (
- message.folder != null &&
- message.folder.name.equals(mFolderName)))) {
- if (message.selected && mSelectedCount > 0) {
- mSelectedCount--;
- }
- mMessages.remove(message);
- }
- }
- resetUnreadCount();
-
- notifyDataSetChanged();
- computeSelectAllVisibility();
- }
-
/**
* Set the selection state for all messages at once.
* @param selected Selection state to set.
*/
public void setSelectionForAllMesages(final boolean selected) {
- for (MessageInfoHolder message : mMessages) {
- message.selected = selected;
- }
+ //TODO: implement
- notifyDataSetChanged();
- }
-
- public void addMessages(final List messages) {
- if (messages.isEmpty()) {
- return;
- }
-
- final boolean wasEmpty = mMessages.isEmpty();
-
- for (final MessageInfoHolder message : messages) {
- if (mFolderName == null || (message.folder != null && message.folder.name.equals(mFolderName))) {
- int index = Collections.binarySearch(mMessages, message, getComparator());
-
- if (index < 0) {
- index = (index * -1) - 1;
- }
-
- mMessages.add(index, message);
- }
- }
-
- if (wasEmpty) {
- mListView.setSelection(0);
- }
- resetUnreadCount();
-
- notifyDataSetChanged();
- computeSelectAllVisibility();
- }
-
- public void changeMessageUid(MessageReference ref, String newUid) {
- MessageInfoHolder holder = getMessage(ref);
- if (holder != null) {
- holder.uid = newUid;
- holder.message.setUid(newUid);
- }
- }
-
- public void resetUnreadCount() {
- if (mQueryString != null) {
- int unreadCount = 0;
-
- for (MessageInfoHolder holder : mMessages) {
- unreadCount += holder.read ? 0 : 1;
- }
-
- mUnreadMessageCount = unreadCount;
- refreshTitle();
- }
+ //notifyDataSetChanged();
}
public void sortMessages() {
- final Comparator chainComparator = getComparator();
+ //TODO: implement
- Collections.sort(mMessages, chainComparator);
-
- notifyDataSetChanged();
+ //notifyDataSetChanged();
}
- public void addOrUpdateMessages(final Account account, final String folderName,
- final List messages, final boolean verifyAgainstSearch) {
-
- boolean needsSort = false;
- final List messagesToAdd = new ArrayList();
- List messagesToRemove = new ArrayList();
- List messagesToSearch = new ArrayList();
-
- // cache field into local variable for faster access for JVM without JIT
- final MessageHelper messageHelper = mMessageHelper;
-
- for (Message message : messages) {
- MessageInfoHolder m = getMessage(message);
- if (message.isSet(Flag.DELETED)) {
- if (m != null) {
- messagesToRemove.add(m);
- }
- } else {
- final Folder messageFolder = message.getFolder();
- final Account messageAccount = messageFolder.getAccount();
- if (m == null) {
- if (updateForMe(account, folderName)) {
- m = new MessageInfoHolder();
- FolderInfoHolder folderInfoHolder = new FolderInfoHolder(
- mContext, messageFolder, messageAccount);
- messageHelper.populate(m, message, folderInfoHolder, messageAccount);
-
- if (verifyAgainstSearch) {
- LocalMessage localMessage = (LocalMessage) message;
-
- if (mThreadId != -1) {
- if (localMessage.getRootId() == mThreadId ||
- localMessage.getId() == mThreadId) {
- messagesToAdd.add(m);
- }
- } else if (mThreadViewEnabled) {
- long threadId = localMessage.getRootId();
- if (threadId == -1) {
- threadId = localMessage.getId();
- }
-
- MessageInfoHolder threadPlaceHolder = getThread(threadId);
- if (threadPlaceHolder == null) {
- messagesToAdd.add(m);
- } else if (m.compareDate.after(threadPlaceHolder.compareDate)) {
- messagesToRemove.add(threadPlaceHolder);
- messagesToAdd.add(m);
- } else {
- threadPlaceHolder.threadCount = m.threadCount;
- }
- } else {
- messagesToAdd.add(m);
- }
- } else {
- messagesToAdd.add(m);
- }
- } else {
- if (mQueryString != null) {
- if (verifyAgainstSearch) {
- messagesToSearch.add(message);
- } else {
- m = new MessageInfoHolder();
- FolderInfoHolder folderInfoHolder = new FolderInfoHolder(
- mContext, messageFolder, messageAccount);
- messageHelper.populate(m, message, folderInfoHolder,
- messageAccount);
- messagesToAdd.add(m);
- }
- }
- }
- } else {
- m.dirty = false; // as we reload the message, unset its dirty flag
- FolderInfoHolder folderInfoHolder = new FolderInfoHolder(mContext,
- messageFolder, account);
- messageHelper.populate(m, message, folderInfoHolder, account);
- needsSort = true;
- }
- }
- }
-
- if (!messagesToSearch.isEmpty()) {
- mController.searchLocalMessages(mAccountUuids, mFolderNames,
- messagesToSearch.toArray(EMPTY_MESSAGE_ARRAY), mQueryString, mIntegrate,
- mQueryFlags, mForbiddenFlags,
- new MessagingListener() {
- @Override
- public void listLocalMessagesAddMessages(Account account, String folder,
- List messages) {
- mHandler.addOrUpdateMessages(account, folder, messages, false);
- }
- });
- }
-
- if (!messagesToRemove.isEmpty()) {
- removeMessages(messagesToRemove);
- }
-
- if (!messagesToAdd.isEmpty()) {
- addMessages(messagesToAdd);
- }
-
- if (needsSort) {
- sortMessages();
- resetUnreadCount();
- }
- }
-
- /**
- * Find a specific message in the message list.
- *
- * Note:
- * This method was optimized because it is called a lot. Don't change it unless you know
- * what you are doing.
- *
- * @param message
- * A {@link Message} instance describing the message to look for.
- *
- * @return The corresponding {@link MessageInfoHolder} instance if the message was found in
- * the message list. {@code null} otherwise.
- */
- private MessageInfoHolder getMessage(Message message) {
- String uid;
- Folder folder;
- for (MessageInfoHolder holder : mMessages) {
- uid = message.getUid();
- if (uid != null && (holder.uid == uid || uid.equals(holder.uid))) {
- folder = message.getFolder();
- if (holder.folder.name.equals(folder.getName()) &&
- holder.account.equals(folder.getAccount().getUuid())) {
- return holder;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Find a specific message in the message list.
- *
- * Note:
- * This method was optimized because it is called a lot. Don't change it unless you know
- * what you are doing.
- *
- * @param messageReference
- * A {@link MessageReference} instance describing the message to look for.
- *
- * @return The corresponding {@link MessageInfoHolder} instance if the message was found in
- * the message list. {@code null} otherwise.
- */
- private MessageInfoHolder getMessage(MessageReference messageReference) {
- String uid;
- for (MessageInfoHolder holder : mMessages) {
- uid = messageReference.uid;
- if ((holder.uid == uid || uid.equals(holder.uid)) &&
- holder.folder.name.equals(messageReference.folderName) &&
- holder.account.equals(messageReference.accountUuid)) {
- return holder;
- }
- }
-
- return null;
- }
-
- private MessageInfoHolder getThread(long threadId) {
- for (MessageInfoHolder holder : mMessages) {
- LocalMessage localMessage = (LocalMessage) holder.message;
- if (localMessage.getId() == threadId || localMessage.getRootId() == threadId) {
- return holder;
- }
- }
-
- return null;
- }
-
- public FolderInfoHolder getFolder(String folder, Account account) {
- LocalFolder local_folder = null;
- try {
- LocalStore localStore = account.getLocalStore();
- local_folder = localStore.getFolder(folder);
- return new FolderInfoHolder(mContext, local_folder, account);
- } catch (Exception e) {
- Log.e(K9.LOG_TAG, "getFolder(" + folder + ") goes boom: ", e);
- return null;
- } finally {
- if (local_folder != null) {
- local_folder.close();
- }
- }
- }
-
-
- @Override
- public int getCount() {
- return mMessages.size();
- }
-
- @Override
- public long getItemId(int position) {
- try {
- MessageInfoHolder messageHolder = (MessageInfoHolder) getItem(position);
- if (messageHolder != null) {
- return messageHolder.message.getId();
- }
- } catch (Exception e) {
- Log.i(K9.LOG_TAG, "getItemId(" + position + ") ", e);
- }
- return -1;
- }
-
- @Override
- public Object getItem(int position) {
- try {
- if (position < mMessages.size()) {
- return mMessages.get(position);
- }
- } catch (Exception e) {
- Log.e(K9.LOG_TAG, "getItem(" + position + "), but folder.messages.size() = " + mMessages.size(), e);
- }
- return null;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- MessageInfoHolder message = (MessageInfoHolder) getItem(position);
- View view;
-
- if ((convertView != null) && (convertView.getId() == R.layout.message_list_item)) {
- view = convertView;
- } else {
- view = mInflater.inflate(R.layout.message_list_item, parent, false);
- view.setId(R.layout.message_list_item);
- }
-
- MessageViewHolder holder = (MessageViewHolder) view.getTag();
-
- if (holder == null) {
- holder = new MessageViewHolder();
- holder.date = (TextView) view.findViewById(R.id.date);
- holder.chip = view.findViewById(R.id.chip);
- holder.preview = (TextView) view.findViewById(R.id.preview);
-
- if (mSenderAboveSubject) {
- holder.from = (TextView) view.findViewById(R.id.subject);
- holder.from.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListSender());
- } else {
- holder.subject = (TextView) view.findViewById(R.id.subject);
- holder.subject.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListSubject());
- }
-
- holder.date.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListDate());
-
- holder.preview.setLines(mPreviewLines);
- holder.preview.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListPreview());
- holder.threadCount = (TextView) view.findViewById(R.id.thread_count);
-
- view.setTag(holder);
- }
-
- if (message != null) {
- bindView(position, view, holder, message);
- } else {
- // This branch code is triggered when the local store
- // hands us an invalid message
-
- holder.chip.getBackground().setAlpha(0);
- if (holder.subject != null) {
- holder.subject.setText(getString(R.string.general_no_subject));
- holder.subject.setTypeface(null, Typeface.NORMAL);
- }
-
- String noSender = getString(R.string.general_no_sender);
-
- if (holder.preview != null) {
- holder.preview.setText(noSender, TextView.BufferType.SPANNABLE);
- Spannable str = (Spannable) holder.preview.getText();
-
- str.setSpan(new StyleSpan(Typeface.NORMAL),
- 0,
- noSender.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- str.setSpan(new AbsoluteSizeSpan(mFontSizes.getMessageListSender(), true),
- 0,
- noSender.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else {
- holder.from.setText(noSender);
- holder.from.setTypeface(null, Typeface.NORMAL);
- holder.from.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
- }
-
- holder.date.setText(getString(R.string.general_no_date));
-
- //WARNING: Order of the next 2 lines matter
- holder.position = -1;
- }
-
-
- return view;
- }
-
- /**
- * Associate model data to view object.
- *
- * @param position
- * The position of the item within the adapter's data set of
- * the item whose view we want.
- * @param view
- * Main view component to alter. Never null
.
- * @param holder
- * Convenience view holder - eases access to view
- * child views. Never null
.
- * @param message
- * Never null
.
- */
-
-
-
- private void bindView(final int position, final View view, final MessageViewHolder holder,
- final MessageInfoHolder message) {
-
- int maybeBoldTypeface = message.read ? Typeface.NORMAL : Typeface.BOLD;
-
- // So that the mSelectedCount is only incremented/decremented
- // when a user checks the checkbox (vs code)
- holder.position = -1;
-
- if (message.selected) {
-
- holder.chip.setBackgroundDrawable(message.message.getFolder().getAccount().getCheckmarkChip().drawable());
- }
-
- else {
- holder.chip.setBackgroundDrawable(message.message.getFolder().getAccount().generateColorChip(message.read,message.message.toMe(), message.message.ccMe(), message.message.fromMe(), message.flagged).drawable());
-
- }
-
- if (K9.useBackgroundAsUnreadIndicator()) {
- int res = (message.read) ? R.attr.messageListReadItemBackgroundColor :
- R.attr.messageListUnreadItemBackgroundColor;
-
- TypedValue outValue = new TypedValue();
- getActivity().getTheme().resolveAttribute(res, outValue, true);
- view.setBackgroundColor(outValue.data);
- }
-
- String subject = null;
-
- if ((message.message.getSubject() == null) || message.message.getSubject().equals("")) {
- subject = (String) getText(R.string.general_no_subject);
-
- } else {
- subject = message.message.getSubject();
- }
-
- int threadCount = message.threadCount;
- if (mThreadId == -1 && threadCount > 1) {
- holder.threadCount.setText(Integer.toString(threadCount));
- holder.threadCount.setVisibility(View.VISIBLE);
- } else {
- holder.threadCount.setVisibility(View.GONE);
- }
-
-
- // We'll get badge support soon --jrv
-// if (holder.badge != null) {
-// String email = message.counterpartyAddress;
-// holder.badge.assignContactFromEmail(email, true);
-// if (email != null) {
-// mContactsPictureLoader.loadContactPicture(email, holder.badge);
-// }
-// }
-
- if (holder.preview != null) {
- /*
- * In the touchable UI, we have previews. Otherwise, we
- * have just a "from" line.
- * Because text views can't wrap around each other(?) we
- * compose a custom view containing the preview and the
- * from.
- */
-
- CharSequence beforePreviewText = null;
- if (mSenderAboveSubject) {
- beforePreviewText = subject;
- } else {
- beforePreviewText = message.sender;
- }
-
- holder.preview.setText(new SpannableStringBuilder(recipientSigil(message))
- .append(beforePreviewText).append(" ").append(message.message.getPreview()),
- TextView.BufferType.SPANNABLE);
- Spannable str = (Spannable)holder.preview.getText();
-
- // Create a span section for the sender, and assign the correct font size and weight.
- str.setSpan(new AbsoluteSizeSpan((mSenderAboveSubject ? mFontSizes.getMessageListSubject(): mFontSizes.getMessageListSender()), true),
- 0, beforePreviewText.length() + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-
- int color = (K9.getK9Theme() == K9.THEME_LIGHT) ?
- Color.rgb(105, 105, 105) :
- Color.rgb(160, 160, 160);
-
- // set span for preview message.
- str.setSpan(new ForegroundColorSpan(color), // How do I can specify the android.R.attr.textColorTertiary
- beforePreviewText.length() + 1,
- str.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
-
-
- if (holder.from != null ) {
- holder.from.setTypeface(null, maybeBoldTypeface);
- if (mSenderAboveSubject) {
- holder.from.setCompoundDrawablesWithIntrinsicBounds(
- message.answered ? mAnsweredIcon : null, // left
- null, // top
- message.message.hasAttachments() ? mAttachmentIcon : null, // right
- null); // bottom
-
- holder.from.setText(message.sender);
- } else {
- holder.from.setText(new SpannableStringBuilder(recipientSigil(message)).append(message.sender));
- }
- }
-
- if (holder.subject != null ) {
- if (!mSenderAboveSubject) {
- holder.subject.setCompoundDrawablesWithIntrinsicBounds(
- message.answered ? mAnsweredIcon : null, // left
- null, // top
- message.message.hasAttachments() ? mAttachmentIcon : null, // right
- null); // bottom
- }
-
- holder.subject.setTypeface(null, maybeBoldTypeface);
- holder.subject.setText(subject);
- }
-
- holder.date.setText(message.getDate(mMessageHelper));
- holder.position = position;
- }
-
-
- private String recipientSigil(MessageInfoHolder message) {
- if (message.message.toMe()) {
+ private String recipientSigil(boolean toMe, boolean ccMe) {
+ if (toMe) {
return getString(R.string.messagelist_sent_to_me_sigil);
- } else if (message.message.ccMe()) {
+ } else if (ccMe) {
return getString(R.string.messagelist_sent_cc_me_sigil);
} else {
return "";
@@ -2200,31 +1652,212 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
}
@Override
- public boolean hasStableIds() {
- return true;
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ View view = mInflater.inflate(R.layout.message_list_item, parent, false);
+ view.setId(R.layout.message_list_item);
+
+ MessageViewHolder holder = new MessageViewHolder();
+ holder.date = (TextView) view.findViewById(R.id.date);
+ holder.chip = view.findViewById(R.id.chip);
+ holder.preview = (TextView) view.findViewById(R.id.preview);
+
+ if (mSenderAboveSubject) {
+ holder.from = (TextView) view.findViewById(R.id.subject);
+ holder.from.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListSender());
+ } else {
+ holder.subject = (TextView) view.findViewById(R.id.subject);
+ holder.subject.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListSubject());
+ }
+
+ holder.date.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListDate());
+
+ holder.preview.setLines(mPreviewLines);
+ holder.preview.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListPreview());
+ holder.threadCount = (TextView) view.findViewById(R.id.thread_count);
+
+ view.setTag(holder);
+
+ return view;
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ //TODO: make this work for search results
+ Account account = mAccount;
+
+ String fromList = cursor.getString(SENDER_LIST_COLUMN);
+ String toList = cursor.getString(TO_LIST_COLUMN);
+ String ccList = cursor.getString(CC_LIST_COLUMN);
+ Address[] fromAddrs = Address.unpack(fromList);
+ Address[] toAddrs = Address.unpack(toList);
+ Address[] ccAddrs = Address.unpack(ccList);
+
+ boolean fromMe = mMessageHelper.toMe(account, fromAddrs);
+ boolean toMe = mMessageHelper.toMe(account, toAddrs);
+ boolean ccMe = mMessageHelper.toMe(account, ccAddrs);
+
+ CharSequence displayName = mMessageHelper.getDisplayName(account, fromAddrs, toAddrs);
+
+ Date sentDate = new Date(cursor.getLong(DATE_COLUMN));
+ String displayDate = mMessageHelper.formatDate(sentDate);
+
+ String preview = cursor.getString(PREVIEW_COLUMN);
+ if (preview == null) {
+ preview = "";
+ }
+
+ String subject = cursor.getString(SUBJECT_COLUMN);
+ if (StringUtils.isNullOrEmpty(subject)) {
+ subject = getString(R.string.general_no_subject);
+ }
+
+ int threadCount = 0; //TODO: get thread count from cursor
+
+ String flagList = cursor.getString(FLAGS_COLUMN);
+ String[] flags = flagList.split(",");
+ boolean read = false;
+ boolean flagged = false;
+ boolean answered = false;
+ boolean forwarded = false;
+ for (int i = 0, len = flags.length; i < len; i++) {
+ try {
+ switch (Flag.valueOf(flags[i])) {
+ case SEEN: {
+ read = true;
+ break;
+ }
+ case FLAGGED: {
+ flagged = true;
+ break;
+ }
+ case ANSWERED: {
+ answered = true;
+ break;
+ }
+ case FORWARDED: {
+ forwarded = true;
+ break;
+ }
+ default: {
+ // We don't care about the other flags
+ }
+ }
+ } catch (Exception e) { /* ignore */ }
+ }
+
+ boolean hasAttachments = (cursor.getInt(ATTACHMENT_COUNT_COLUMN) > 0);
+
+ MessageViewHolder holder = (MessageViewHolder) view.getTag();
+
+ int maybeBoldTypeface = (read) ? Typeface.NORMAL : Typeface.BOLD;
+
+ int adapterPosition = cursor.getPosition();
+ boolean selected = mSelected.get(adapterPosition, false);
+
+ if (selected) {
+ holder.chip.setBackgroundDrawable(account.getCheckmarkChip().drawable());
+ } else {
+ holder.chip.setBackgroundDrawable(account.generateColorChip(read, toMe, ccMe,
+ fromMe, flagged).drawable());
+ }
+
+ // Background indicator
+ if (K9.useBackgroundAsUnreadIndicator()) {
+ int res = (read) ? R.attr.messageListReadItemBackgroundColor :
+ R.attr.messageListUnreadItemBackgroundColor;
+
+ TypedValue outValue = new TypedValue();
+ getActivity().getTheme().resolveAttribute(res, outValue, true);
+ view.setBackgroundColor(outValue.data);
+ }
+
+ // Thread count
+ if (mThreadId == -1 && threadCount > 1) {
+ holder.threadCount.setText(Integer.toString(threadCount));
+ holder.threadCount.setVisibility(View.VISIBLE);
+ } else {
+ holder.threadCount.setVisibility(View.GONE);
+ }
+
+ CharSequence beforePreviewText = (mSenderAboveSubject) ? subject : displayName;
+
+ String sigil = recipientSigil(toMe, ccMe);
+
+ holder.preview.setText(
+ new SpannableStringBuilder(sigil)
+ .append(beforePreviewText)
+ .append(" ")
+ .append(preview), TextView.BufferType.SPANNABLE);
+
+ Spannable str = (Spannable)holder.preview.getText();
+
+ // Create a span section for the sender, and assign the correct font size and weight
+ int fontSize = (mSenderAboveSubject) ?
+ mFontSizes.getMessageListSubject():
+ mFontSizes.getMessageListSender();
+
+ AbsoluteSizeSpan span = new AbsoluteSizeSpan(fontSize, true);
+ str.setSpan(span, 0, beforePreviewText.length() + 1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ //TODO: make this part of the theme
+ int color = (K9.getK9Theme() == K9.THEME_LIGHT) ?
+ Color.rgb(105, 105, 105) :
+ Color.rgb(160, 160, 160);
+
+ // Set span (color) for preview message
+ str.setSpan(new ForegroundColorSpan(color), beforePreviewText.length() + 1,
+ str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ Drawable statusHolder = null;
+ if (forwarded && answered) {
+ statusHolder = mForwardedAnsweredIcon;
+ } else if (answered) {
+ statusHolder = mAnsweredIcon;
+ } else if (forwarded) {
+ statusHolder = mForwardedIcon;
+ }
+
+ if (holder.from != null ) {
+ holder.from.setTypeface(null, maybeBoldTypeface);
+ if (mSenderAboveSubject) {
+ holder.from.setCompoundDrawablesWithIntrinsicBounds(
+ statusHolder, // left
+ null, // top
+ hasAttachments ? mAttachmentIcon : null, // right
+ null); // bottom
+
+ holder.from.setText(displayName);
+ } else {
+ holder.from.setText(new SpannableStringBuilder(sigil).append(displayName));
+ }
+ }
+
+ if (holder.subject != null ) {
+ if (!mSenderAboveSubject) {
+ holder.subject.setCompoundDrawablesWithIntrinsicBounds(
+ statusHolder, // left
+ null, // top
+ hasAttachments ? mAttachmentIcon : null, // right
+ null); // bottom
+ }
+
+ holder.subject.setTypeface(null, maybeBoldTypeface);
+ holder.subject.setText(subject);
+ }
+
+ holder.date.setText(displayDate);
}
}
- class MessageViewHolder
- implements OnCheckedChangeListener {
+ class MessageViewHolder {
public TextView subject;
public TextView preview;
public TextView from;
public TextView time;
public TextView date;
public View chip;
- public int position = -1;
public TextView threadCount;
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (position != -1) {
- MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(position);
- toggleMessageSelect(message);
-
-
- }
- }
}
@@ -2286,23 +1919,6 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
public TextView main;
}
- private void setAllSelected(boolean isSelected) {
- mSelectedCount = 0;
-
- for (MessageInfoHolder holder : mAdapter.getMessages()) {
- holder.selected = isSelected;
- mSelectedCount += (isSelected ? 1 : 0);
- }
-
- computeBatchDirection();
- mAdapter.notifyDataSetChanged();
-
- if (isSelected) {
- updateActionModeTitle();
- computeSelectAllVisibility();
- }
- }
-
/**
* Set selection state for all messages.
*
@@ -2311,40 +1927,54 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
* action mode is finished.
*/
private void setSelectionState(boolean selected) {
- mAdapter.setSelectionForAllMesages(selected);
-
if (selected) {
mSelectedCount = mAdapter.getCount();
- mActionMode = getSherlockActivity().startActionMode(mActionModeCallback);
+ for (int i = 0, end = mSelectedCount; i < end; i++) {
+ mSelected.put(i, true);
+ }
+
+ if (mActionMode == null) {
+ mActionMode = getSherlockActivity().startActionMode(mActionModeCallback);
+ }
+ computeBatchDirection();
updateActionModeTitle();
computeSelectAllVisibility();
- computeBatchDirection();
} else {
+ mSelected.clear();
mSelectedCount = 0;
if (mActionMode != null) {
mActionMode.finish();
+ mActionMode = null;
}
}
+
+ mAdapter.notifyDataSetChanged();
}
- private void toggleMessageSelect(final MessageInfoHolder holder){
+ private void toggleMessageSelect(int listViewPosition) {
+ int adapterPosition = listViewToAdapterPosition(listViewPosition);
+ if (adapterPosition == AdapterView.INVALID_POSITION) {
+ return;
+ }
+
+ boolean selected = mSelected.get(adapterPosition, false);
+ mSelected.put(adapterPosition, !selected);
+
if (mActionMode != null) {
- if (mSelectedCount == 1 && holder.selected) {
+ if (mSelectedCount == 1 && selected) {
mActionMode.finish();
+ mActionMode = null;
return;
}
} else {
mActionMode = getSherlockActivity().startActionMode(mActionModeCallback);
}
- if (holder.selected) {
- holder.selected = false;
+ if (selected) {
mSelectedCount -= 1;
} else {
- holder.selected = true;
mSelectedCount += 1;
}
- mAdapter.notifyDataSetChanged();
computeBatchDirection();
updateActionModeTitle();
@@ -2353,6 +1983,8 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
mActionMode.invalidate();
computeSelectAllVisibility();
+
+ mAdapter.notifyDataSetChanged();
}
private void updateActionModeTitle() {
@@ -2367,6 +1999,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
boolean isBatchFlag = false;
boolean isBatchRead = false;
+ /*
for (MessageInfoHolder holder : mAdapter.getMessages()) {
if (holder.selected) {
if (!holder.flagged) {
@@ -2381,54 +2014,48 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
}
}
}
+ */
+ //TODO: implement
mActionModeCallback.showMarkAsRead(isBatchRead);
mActionModeCallback.showFlag(isBatchFlag);
}
- /**
- * @param holders
- * Messages to update. Never {@code null}.
- * @param flag
- * Flag to be updated on the specified messages. Never
- * {@code null}.
- * @param newState
- * State to set for the given flag.
- */
- private void setFlag(final List holders, final Flag flag, final boolean newState) {
- if (holders.isEmpty()) {
+ private void setFlag(Message message, final Flag flag, final boolean newState) {
+ setFlag(new Message[] { message }, flag, newState);
+ }
+
+ private void setFlag(Message[] messages, final Flag flag, final boolean newState) {
+ if (messages.length == 0) {
return;
}
- final Message[] messageList = new Message[holders.size()];
- int i = 0;
- for (final Iterator iterator = holders.iterator(); iterator.hasNext(); i++) {
- final MessageInfoHolder holder = iterator.next();
- messageList[i] = holder.message;
- if (flag == Flag.SEEN) {
- holder.read = newState;
- } else if (flag == Flag.FLAGGED) {
- holder.flagged = newState;
- }
- }
- mController.setFlag(messageList, flag, newState);
- mAdapter.sortMessages();
+
+ mController.setFlag(messages, flag, newState);
computeBatchDirection();
}
+ private void onMove(Message message) {
+ onMove(new Message[] { message });
+ }
+
/**
* Display the message move activity.
*
* @param holders
* Never {@code null}.
*/
- private void onMove(final List holders) {
- if (!checkCopyOrMovePossible(holders, FolderOperation.MOVE)) {
+ private void onMove(Message[] messages) {
+ if (!checkCopyOrMovePossible(messages, FolderOperation.MOVE)) {
return;
}
- final Folder folder = holders.size() == 1 ? holders.get(0).message.getFolder() : mCurrentFolder.folder;
- displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_MOVE, folder, holders);
+ final Folder folder = messages.length == 1 ? messages[0].getFolder() : mCurrentFolder.folder;
+ displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_MOVE, folder, messages);
+ }
+
+ private void onCopy(Message message) {
+ onCopy(new Message[] { message });
}
/**
@@ -2437,13 +2064,13 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
* @param holders
* Never {@code null}.
*/
- private void onCopy(final List holders) {
- if (!checkCopyOrMovePossible(holders, FolderOperation.COPY)) {
+ private void onCopy(Message[] messages) {
+ if (!checkCopyOrMovePossible(messages, FolderOperation.COPY)) {
return;
}
- final Folder folder = holders.size() == 1 ? holders.get(0).message.getFolder() : mCurrentFolder.folder;
- displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_COPY, folder, holders);
+ final Folder folder = messages.length == 1 ? messages[0].getFolder() : mCurrentFolder.folder;
+ displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_COPY, folder, messages);
}
/**
@@ -2462,41 +2089,45 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
* {@code null}.
* @see #startActivityForResult(Intent, int)
*/
- private void displayFolderChoice(final int requestCode, final Folder folder, final List holders) {
+ private void displayFolderChoice(final int requestCode, final Folder folder, final Message[] messages) {
final Intent intent = new Intent(getActivity(), ChooseFolder.class);
intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, folder.getAccount().getUuid());
intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, folder.getName());
intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, folder.getAccount().getLastSelectedFolderName());
// remember the selected messages for #onActivityResult
- mActiveMessages = holders;
+ mActiveMessages = messages;
startActivityForResult(intent, requestCode);
}
- /**
- * @param holders
- * Never {@code null}.
- */
- private void onArchive(final List holders) {
- final String folderName = holders.get(0).message.getFolder().getAccount().getArchiveFolderName();
+ private void onArchive(final Message message) {
+ onArchive(new Message[] { message });
+ }
+
+ private void onArchive(final Message[] messages) {
+ final String folderName = messages[0].getFolder().getAccount().getArchiveFolderName();
if (K9.FOLDER_NONE.equalsIgnoreCase(folderName)) {
return;
}
// TODO one should separate messages by account and call move afterwards
// (because each account might have a specific Archive folder name)
- move(holders, folderName);
+ move(messages, folderName);
+ }
+
+ private void onSpam(Message message) {
+ onSpam(new Message[] { message });
}
/**
* @param holders
* Never {@code null}.
*/
- private void onSpam(final List holders) {
+ private void onSpam(Message[] messages) {
if (K9.confirmSpam()) {
// remember the message selection for #onCreateDialog(int)
- mActiveMessages = holders;
+ mActiveMessages = messages;
showDialog(R.id.dialog_confirm_spam);
} else {
- onSpamConfirmed(holders);
+ onSpamConfirmed(messages);
}
}
@@ -2504,14 +2135,14 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
* @param holders
* Never {@code null}.
*/
- private void onSpamConfirmed(final List holders) {
- final String folderName = holders.get(0).message.getFolder().getAccount().getSpamFolderName();
+ private void onSpamConfirmed(Message[] messages) {
+ final String folderName = messages[0].getFolder().getAccount().getSpamFolderName();
if (K9.FOLDER_NONE.equalsIgnoreCase(folderName)) {
return;
}
// TODO one should separate messages by account and call move afterwards
// (because each account might have a specific Spam folder name)
- move(holders, folderName);
+ move(messages, folderName);
}
private static enum FolderOperation {
@@ -2528,23 +2159,25 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
*
* @return true
if operation is possible
*/
- private boolean checkCopyOrMovePossible(final List holders, final FolderOperation operation) {
- if (holders.isEmpty()) {
+ private boolean checkCopyOrMovePossible(final Message[] messages, final FolderOperation operation) {
+ if (messages.length == 0) {
return false;
}
+
boolean first = true;
- for (final MessageInfoHolder holder : holders) {
- final Message message = holder.message;
+ for (final Message message : messages) {
if (first) {
first = false;
// account check
final Account account = message.getFolder().getAccount();
- if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(account)) || (operation == FolderOperation.COPY && !mController.isCopyCapable(account))) {
+ if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(account)) ||
+ (operation == FolderOperation.COPY && !mController.isCopyCapable(account))) {
return false;
}
}
// message check
- if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(message)) || (operation == FolderOperation.COPY && !mController.isCopyCapable(message))) {
+ if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(message)) ||
+ (operation == FolderOperation.COPY && !mController.isCopyCapable(message))) {
final Toast toast = Toast.makeText(getActivity(), R.string.move_copy_cannot_copy_unsynced_message,
Toast.LENGTH_LONG);
toast.show();
@@ -2554,42 +2187,14 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
return true;
}
- /**
- * Helper method to get a List of message ready to be processed. This implementation will return a list containing the sole argument.
- *
- * @param holder Never {@code null}.
- * @return Never {@code null}.
- */
- private List getSelectionFromMessage(final MessageInfoHolder holder) {
- final List selection = Collections.singletonList(holder);
- return selection;
- }
-
- /**
- * Helper method to get a List of message ready to be processed. This implementation will iterate over messages and choose the checked ones.
- *
- * @return Never {@code null}.
- */
- private List getSelectionFromCheckboxes() {
- final List selection = new ArrayList();
-
- for (final MessageInfoHolder holder : mAdapter.getMessages()) {
- if (holder.selected) {
- selection.add(holder);
- }
- }
-
- return selection;
- }
-
/**
* Copy the specified messages to the specified folder.
*
* @param holders Never {@code null}.
* @param destination Never {@code null}.
*/
- private void copy(final List holders, final String destination) {
- copyOrMove(holders, destination, FolderOperation.COPY);
+ private void copy(Message[] messages, final String destination) {
+ copyOrMove(messages, destination, FolderOperation.COPY);
}
/**
@@ -2598,8 +2203,8 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
* @param holders Never {@code null}.
* @param destination Never {@code null}.
*/
- private void move(final List holders, final String destination) {
- copyOrMove(holders, destination, FolderOperation.MOVE);
+ private void move(Message[] messages, final String destination) {
+ copyOrMove(messages, destination, FolderOperation.MOVE);
}
/**
@@ -2616,7 +2221,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
* @param operation
* Never {@code null}.
*/
- private void copyOrMove(final List holders, final String destination, final FolderOperation operation) {
+ private void copyOrMove(Message[] messages, final String destination, final FolderOperation operation) {
if (K9.FOLDER_NONE.equalsIgnoreCase(destination)) {
return;
}
@@ -2625,10 +2230,9 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
Account account = null;
String folderName = null;
- final List messages = new ArrayList(holders.size());
+ List outMessages = new ArrayList();
- for (final MessageInfoHolder holder : holders) {
- final Message message = holder.message;
+ for (Message message : messages) {
if (first) {
first = false;
folderName = message.getFolder().getName();
@@ -2652,15 +2256,14 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
// message isn't synchronized
return;
}
- messages.add(message);
+ outMessages.add(message);
}
if (operation == FolderOperation.MOVE) {
- mController.moveMessages(account, folderName, messages.toArray(new Message[messages.size()]), destination,
+ mController.moveMessages(account, folderName, outMessages.toArray(new Message[outMessages.size()]), destination,
null);
- mAdapter.removeMessages(holders);
} else {
- mController.copyMessages(account, folderName, messages.toArray(new Message[messages.size()]), destination,
+ mController.copyMessages(account, folderName, outMessages.toArray(new Message[outMessages.size()]), destination,
null);
}
}
@@ -2715,11 +2318,11 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
* TODO think of a better way then looping over all
* messages.
*/
- final List selection = getSelectionFromCheckboxes();
+ Message[] messages = getCheckedMessages();
Account account;
- for (MessageInfoHolder holder : selection) {
- account = holder.message.getFolder().getAccount();
+ for (Message message : messages) {
+ account = message.getFolder().getAccount();
setContextCapabilities(account, menu);
}
@@ -2735,7 +2338,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
mMarkAsUnread = null;
mFlag = null;
mUnflag = null;
- setAllSelected(false);
+ setSelectionState(false);
}
@Override
@@ -2816,7 +2419,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- final List selection = getSelectionFromCheckboxes();
+ Message[] messages = getCheckedMessages();
/*
* In the following we assume that we can't move or copy
@@ -2827,49 +2430,51 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
*/
switch (item.getItemId()) {
case R.id.delete: {
- onDelete(selection);
+ onDelete(messages);
+
+ //FIXME
mSelectedCount = 0;
break;
}
case R.id.mark_as_read: {
- setFlag(selection, Flag.SEEN, true);
+ setFlag(messages, Flag.SEEN, true);
break;
}
case R.id.mark_as_unread: {
- setFlag(selection, Flag.SEEN, false);
+ setFlag(messages, Flag.SEEN, false);
break;
}
case R.id.flag: {
- setFlag(selection, Flag.FLAGGED, true);
+ setFlag(messages, Flag.FLAGGED, true);
break;
}
case R.id.unflag: {
- setFlag(selection, Flag.FLAGGED, false);
+ setFlag(messages, Flag.FLAGGED, false);
break;
}
case R.id.select_all: {
- setAllSelected(true);
+ selectAll();
break;
}
// only if the account supports this
case R.id.archive: {
- onArchive(selection);
+ onArchive(messages);
mSelectedCount = 0;
break;
}
case R.id.spam: {
- onSpam(selection);
+ onSpam(messages);
mSelectedCount = 0;
break;
}
case R.id.move: {
- onMove(selection);
+ onMove(messages);
mSelectedCount = 0;
break;
}
case R.id.copy: {
- onCopy(selection);
+ onCopy(messages);
mSelectedCount = 0;
break;
}
@@ -2911,8 +2516,8 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
}
public void checkMail() {
- mController.synchronizeMailbox(mAccount, mFolderName, mAdapter.mListener, null);
- mController.sendPendingMessages(mAccount, mAdapter.mListener);
+ mController.synchronizeMailbox(mAccount, mFolderName, mListener, null);
+ mController.sendPendingMessages(mAccount, mListener);
}
/**
@@ -2937,7 +2542,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
final Folder remoteFolder = remoteStore.getFolder(mSearchFolder);
remoteFolder.close();
// Send a remoteSearchFinished() message for good measure.
- mAdapter.mListener.remoteSearchFinished(searchAccount, mSearchFolder, 0, null);
+ //mAdapter.mListener.remoteSearchFinished(searchAccount, mSearchFolder, 0, null);
} catch (Exception e) {
// Since the user is going back, log and squash any exceptions.
Log.e(K9.LOG_TAG, "Could not abort remote search before going back", e);
@@ -2949,10 +2554,13 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
public ArrayList getMessageReferences() {
ArrayList messageRefs = new ArrayList();
+ /*
for (MessageInfoHolder holder : mAdapter.getMessages()) {
MessageReference ref = holder.message.makeMessageReference();
messageRefs.add(ref);
}
+ */
+ //TODO: implement
return messageRefs;
}
@@ -3003,56 +2611,89 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
changeSort(mSortType);
}
- private MessageInfoHolder getSelection() {
- return (MessageInfoHolder) mListView.getSelectedItem();
+ private Message getSelectedMessage() {
+ int listViewPosition = mListView.getSelectedItemPosition();
+ int adapterPosition = listViewToAdapterPosition(listViewPosition);
+
+ return getMessageAtPosition(adapterPosition);
+ }
+
+ private Message getMessageAtPosition(int adapterPosition) {
+ if (adapterPosition == AdapterView.INVALID_POSITION) {
+ return null;
+ }
+
+ Cursor cursor = (Cursor) mAdapter.getItem(adapterPosition);
+ String uid = cursor.getString(UID_COLUMN);
+
+ //TODO: get account and folder from cursor
+ Folder folder = mCurrentFolder.folder;
+
+ try {
+ return folder.getMessage(uid);
+ } catch (MessagingException e) {
+ Log.e(K9.LOG_TAG, "Something went wrong while fetching a message", e);
+ }
+
+ return null;
+ }
+
+ private Message[] getCheckedMessages() {
+ Message[] messages = new Message[mSelectedCount];
+ int out = 0;
+ for (int position = 0, end = mAdapter.getCount(); position < end; position++) {
+ if (mSelected.get(position, false)) {
+ messages[out++] = getMessageAtPosition(position);
+ }
+ }
+
+ return messages;
}
public void onDelete() {
- MessageInfoHolder message = getSelection();
+ Message message = getSelectedMessage();
if (message != null) {
- onDelete(Collections.singletonList(message));
+ onDelete(new Message[] { message });
}
}
public void toggleMessageSelect() {
- MessageInfoHolder message = getSelection();
- if (message != null) {
- toggleMessageSelect(message);
- }
+ toggleMessageSelect(mListView.getSelectedItemPosition());
}
public void onToggleFlag() {
- MessageInfoHolder message = getSelection();
+ Message message = getSelectedMessage();
if (message != null) {
- setFlag(Collections.singletonList(message), Flag.FLAGGED, !message.flagged);
+ System.out.println("FLAGGED: " + message.isSet(Flag.FLAGGED));
+ setFlag(message, Flag.FLAGGED, !message.isSet(Flag.FLAGGED));
}
}
public void onMove() {
- MessageInfoHolder message = getSelection();
+ Message message = getSelectedMessage();
if (message != null) {
- onMove(Collections.singletonList(message));
+ onMove(message);
}
}
public void onArchive() {
- MessageInfoHolder message = getSelection();
+ Message message = getSelectedMessage();
if (message != null) {
- onArchive(Collections.singletonList(message));
+ onArchive(message);
}
}
public void onCopy() {
- MessageInfoHolder message = getSelection();
+ Message message = getSelectedMessage();
if (message != null) {
- onCopy(Collections.singletonList(message));
+ onCopy(message);
}
}
public void onToggleRead() {
- MessageInfoHolder message = getSelection();
+ Message message = getSelectedMessage();
if (message != null) {
- setFlag(Collections.singletonList(message), Flag.SEEN, !message.read);
+ setFlag(message, Flag.SEEN, !message.isSet(Flag.SEEN));
}
}
@@ -3124,4 +2765,39 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
String folderName = (mCurrentFolder != null) ? mCurrentFolder.name : null;
return mFragmentListener.startSearch(mAccount, folderName);
}
+
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ String accountUuid = mAccount.getUuid();
+
+ Uri uri = Uri.withAppendedPath(EmailProvider.CONTENT_URI, "account/" + accountUuid + "/messages");
+
+ long folderId = 0;
+ try {
+ LocalFolder folder = (LocalFolder) mCurrentFolder.folder;
+ folder.open(OpenMode.READ_ONLY);
+ folderId = folder.getId();
+ } catch (MessagingException e) {
+ //FIXME
+ e.printStackTrace();
+ }
+
+ String selection = MessageColumns.FOLDER_ID + "=?";
+ String[] selectionArgs = { Long.toString(folderId) };
+
+ return new CursorLoader(getActivity(), uri, PROJECTION, selection, selectionArgs,
+ MessageColumns.DATE + " DESC");
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, Cursor data) {
+ mSelected = new SparseBooleanArray(data.getCount());
+ mAdapter.swapCursor(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {
+ mSelected = null;
+ mAdapter.swapCursor(null);
+ }
}
diff --git a/src/com/fsck/k9/helper/MessageHelper.java b/src/com/fsck/k9/helper/MessageHelper.java
index ae708f224..ce2e9d2ed 100644
--- a/src/com/fsck/k9/helper/MessageHelper.java
+++ b/src/com/fsck/k9/helper/MessageHelper.java
@@ -108,4 +108,28 @@ public class MessageHelper {
mDateFormat = DateFormatter.getDateFormat(mContext);
mTodayDateFormat = android.text.format.DateFormat.getTimeFormat(mContext);
}
+
+ public CharSequence getDisplayName(Account account, Address[] fromAddrs, Address[] toAddrs) {
+ final Contacts contactHelper = K9.showContactName() ? Contacts.getInstance(mContext) : null;
+
+ CharSequence displayName;
+ if (fromAddrs.length > 0 && account.isAnIdentity(fromAddrs[0])) {
+ CharSequence to = Address.toFriendly(toAddrs, contactHelper);
+ displayName = new SpannableStringBuilder(
+ mContext.getString(R.string.message_to_label)).append(to);
+ } else {
+ displayName = Address.toFriendly(fromAddrs, contactHelper);
+ }
+
+ return displayName;
+ }
+
+ public boolean toMe(Account account, Address[] toAddrs) {
+ for (Address address : toAddrs) {
+ if (account.isAnIdentity(address)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java
index 8c42b6f48..641466b3c 100644
--- a/src/com/fsck/k9/mail/store/LocalStore.java
+++ b/src/com/fsck/k9/mail/store/LocalStore.java
@@ -3976,4 +3976,7 @@ public class LocalStore extends Store implements Serializable {
}
}
+ public LockableDatabase getDatabase() {
+ return database;
+ }
}
diff --git a/src/com/fsck/k9/mail/store/LockableDatabase.java b/src/com/fsck/k9/mail/store/LockableDatabase.java
index b303c2aef..67090aace 100644
--- a/src/com/fsck/k9/mail/store/LockableDatabase.java
+++ b/src/com/fsck/k9/mail/store/LockableDatabase.java
@@ -49,7 +49,7 @@ public class LockableDatabase {
* Workaround exception wrapper used to keep the inner exception generated
* in a {@link DbCallback}.
*/
- protected static class WrappedException extends RuntimeException {
+ public static class WrappedException extends RuntimeException {
/**
*
*/
diff --git a/src/com/fsck/k9/provider/EmailProvider.java b/src/com/fsck/k9/provider/EmailProvider.java
new file mode 100644
index 000000000..1cf799d75
--- /dev/null
+++ b/src/com/fsck/k9/provider/EmailProvider.java
@@ -0,0 +1,246 @@
+package com.fsck.k9.provider;
+
+import java.util.List;
+
+import com.fsck.k9.Account;
+import com.fsck.k9.Preferences;
+import com.fsck.k9.helper.StringUtils;
+import com.fsck.k9.mail.MessagingException;
+import com.fsck.k9.mail.store.LocalStore;
+import com.fsck.k9.mail.store.LockableDatabase;
+import com.fsck.k9.mail.store.LockableDatabase.DbCallback;
+import com.fsck.k9.mail.store.LockableDatabase.WrappedException;
+import com.fsck.k9.mail.store.UnavailableStorageException;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+
+/**
+ * Content Provider used to display the message list etc.
+ *
+ *
+ * For now this content provider is for internal use only. In the future we may allow third-party
+ * apps to access K-9 Mail content using this content provider.
+ *
+ */
+/*
+ * TODO:
+ * - modify MessagingController (or LocalStore?) to call ContentResolver.notifyChange() to trigger
+ * notifications when the underlying data changes.
+ * - add support for message threading
+ * - add support for search views
+ * - add support for querying multiple accounts (e.g. "Unified Inbox")
+ * - add support for account list and folder list
+ */
+public class EmailProvider extends ContentProvider {
+ private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+ public static final String AUTHORITY = "org.k9mail.provider.email";
+
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
+
+
+ /*
+ * Constants that are used for the URI matching.
+ */
+ private static final int MESSAGE_BASE = 0;
+ private static final int MESSAGES = MESSAGE_BASE;
+ //private static final int MESSAGES_THREADED = MESSAGE_BASE + 1;
+ //private static final int MESSAGES_THREAD = MESSAGE_BASE + 2;
+
+
+ private static final String MESSAGES_TABLE = "messages";
+
+
+ static {
+ UriMatcher matcher = sUriMatcher;
+
+ matcher.addURI(AUTHORITY, "account/*/messages", MESSAGES);
+ //matcher.addURI(AUTHORITY, "account/*/messages/threaded", MESSAGES_THREADED);
+ //matcher.addURI(AUTHORITY, "account/*/thread/#", MESSAGES_THREAD);
+ }
+
+
+ public interface MessageColumns {
+ public static final String ID = "id";
+ public static final String UID = "uid";
+ public static final String INTERNAL_DATE = "internal_date";
+ public static final String SUBJECT = "subject";
+ public static final String DATE = "date";
+ public static final String MESSAGE_ID = "message_id";
+ public static final String SENDER_LIST = "sender_list";
+ public static final String TO_LIST = "to_list";
+ public static final String CC_LIST = "cc_list";
+ public static final String BCC_LIST = "bcc_list";
+ public static final String REPLY_TO_LIST = "reply_to_list";
+ public static final String FLAGS = "flags";
+ public static final String ATTACHMENT_COUNT = "attachment_count";
+ public static final String FOLDER_ID = "folder_id";
+ public static final String PREVIEW = "preview";
+ public static final String THREAD_ROOT = "thread_root";
+ public static final String THREAD_PARENT = "thread_parent";
+ }
+
+ private interface InternalMessageColumns extends MessageColumns {
+ public static final String DELETED = "deleted";
+ public static final String EMPTY = "empty";
+ public static final String TEXT_CONTENT = "text_content";
+ public static final String HTML_CONTENT = "html_content";
+ public static final String MIME_TYPE = "mime_type";
+ }
+
+
+ private Preferences mPreferences;
+
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new RuntimeException("not implemented yet");
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+
+ int match = sUriMatcher.match(uri);
+ if (match < 0) {
+ throw new IllegalArgumentException("Unknown URI: " + uri);
+ }
+
+ ContentResolver contentResolver = getContext().getContentResolver();
+ Cursor cursor = null;
+ switch (match) {
+ case MESSAGES: {
+ List segments = uri.getPathSegments();
+ String accountUuid = segments.get(1);
+
+ cursor = getMessages(accountUuid, projection, selection, selectionArgs, sortOrder);
+
+ cursor.setNotificationUri(contentResolver, uri);
+ break;
+ }
+ }
+
+ return new IdTrickeryCursor(cursor);
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new RuntimeException("not implemented yet");
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new RuntimeException("not implemented yet");
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new RuntimeException("not implemented yet");
+ }
+
+ protected Cursor getMessages(String accountUuid, final String[] projection,
+ final String selection, final String[] selectionArgs, final String sortOrder) {
+
+ Account account = getAccount(accountUuid);
+ LockableDatabase database = getDatabase(account);
+
+ try {
+ return database.execute(false, new DbCallback() {
+ @Override
+ public Cursor doDbWork(SQLiteDatabase db) throws WrappedException,
+ UnavailableStorageException {
+
+ String where;
+ if (StringUtils.isNullOrEmpty(selection)) {
+ where = InternalMessageColumns.DELETED + "=0 AND " +
+ InternalMessageColumns.EMPTY + "!=1";
+ } else {
+ where = "(" + selection + ") AND " +
+ InternalMessageColumns.DELETED + "=0 AND " +
+ InternalMessageColumns.EMPTY + "!=1";
+ }
+
+ return db.query(MESSAGES_TABLE, projection, where, selectionArgs, null, null,
+ sortOrder);
+ }
+ });
+ } catch (UnavailableStorageException e) {
+ throw new RuntimeException("Storage not available", e);
+ }
+ }
+
+ private Account getAccount(String accountUuid) {
+ if (mPreferences == null) {
+ Context appContext = getContext().getApplicationContext();
+ mPreferences = Preferences.getPreferences(appContext);
+ }
+
+ Account account = mPreferences.getAccount(accountUuid);
+
+ if (account == null) {
+ throw new IllegalArgumentException("Unknown account: " + accountUuid);
+ }
+
+ return account;
+ }
+
+ private LockableDatabase getDatabase(Account account) {
+ LocalStore localStore;
+ try {
+ localStore = account.getLocalStore();
+ } catch (MessagingException e) {
+ throw new RuntimeException("Couldn't get LocalStore", e);
+ }
+
+ return localStore.getDatabase();
+ }
+
+ /**
+ * This class is needed to make {@link CursorAdapter} work with our database schema.
+ *
+ *
+ * {@code CursorAdapter} requires a column named {@code "_id"} containing a stable id. We use
+ * the column name {@code "id"} as primary key in all our tables. So this {@link CursorWrapper}
+ * maps all queries for {@code "_id"} to {@code "id"}.
+ *
+ * Please note that this only works for the returned {@code Cursor}. When querying the content
+ * provider you still need to use {@link MessageColumns#ID}.
+ *
+ */
+ static class IdTrickeryCursor extends CursorWrapper {
+ public IdTrickeryCursor(Cursor cursor) {
+ super(cursor);
+ }
+
+ @Override
+ public int getColumnIndex(String columnName) {
+ if ("_id".equals(columnName)) {
+ return super.getColumnIndex("id");
+ }
+
+ return super.getColumnIndex(columnName);
+ }
+
+ @Override
+ public int getColumnIndexOrThrow(String columnName) {
+ if ("_id".equals(columnName)) {
+ return super.getColumnIndexOrThrow("id");
+ }
+
+ return super.getColumnIndexOrThrow(columnName);
+ }
+ }
+}